Sum of the intensity values of all pixels in image segment - python

According to the formula that is shown below, I need to calculate an average threshold value by dividing the sum of intensity values in segment on the number of pixels in segment.
where Xi' is a binary mask (structure_mask), |Xi'| is a number of ones (xi_modulus).
I(x,y) is a pixel intensity.
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
...
...
structure_mask = np.logical_and(magnitude_mask, intensity_mask).astype(np.uint8)
xi_modulus = np.count_nonzero(structure_mask.all(axis=2))
intensity_sum # = ??
How to calculate the sum of intensities with numpy?
EDITED: Based on the #HansHirse's answer I've tried to do the following:
thresh_val = np.mean(img_gray[structure_mask])
And I've got IndexError: too many indices for array
Where structure_mask was of shape (1066, 1600,1) and img_gray -
(1066,1600)
UPDATED: Just a dummy mistake. Shape mismatch was fixed by proper indexing
structure_mask = np.logical_and(magnitude_mask, intensity_mask)[:, :, 0]

Using NumPy's boolean array indexing, you can easily access the desired values. You just need to pay attention, that your mask (or segment) is of NumPy's bool_ type.
Let's see this short code snippet, where I compare the mean obtained from np.mean with the one explicitly calculated by the given formula:
import cv2
import numpy as np
# Some artificial image
img = np.uint8(255 * np.tile(np.linspace(1, 0, 400), (400, 1)))
cv2.imshow('img', img)
# Some mask (or segment)
mask = np.zeros((400, 400), np.uint8)
mask[10:390, 10:30] = 255
cv2.imshow('mask', mask)
# Convert mask to bool_ type
mask = np.bool_(mask)
# Calculate mean by NumPy's mean
mean = np.mean(img[mask])
print('mean by np.mean:\n', mean)
# Calculate mean explicitly by given formula
mean = np.sum(img[mask]) / np.count_nonzero(mask)
print('mean by formula:\n', mean)
cv2.waitKey(0)
cv2.destroyAllWindows()
Outputs (images omitted here):
mean by np.mean:
242.05
mean by formula:
242.05
Hope that helps!

numpy supports logical indexes so
magnitude_mask[intensity_mask].mean()
will give you what you want.
if you insist on having the sum use
magnitude_mask[intensity_mask].sum()

Related

contrast normalization of image python

i want to ask how to get the image result (Icon) with python code as indicated in
where ishade is a preprocessed image and std(Ishade) is the standard deviation of this image
result = ndimage.median_filter(blur, size=68)
std=cv2.meanStdDev(result)
I tried to follow the article in the reference you posted and the reference in that post to the original. But I do not get exactly what they do. Nevertheless, here is my interpretation (apart from the initial CLAHE). You can adjust the mean and median filter sizes as desired.
Input:
import cv2
import numpy as np
import skimage.exposure
# load image
img = cv2.imread("lena.jpg")
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Gaussian blurred gray image
mean = cv2.GaussianBlur(gray, (0,0), sigmaX=5, sigmaY=5)
# apply median filter to mean image
median = cv2.medianBlur(mean, 25)
# divide mean by median
division = cv2.divide(mean.astype(np.float64)/255, median.astype(np.float64)/255)
# get global standard deviation of division
std = np.std(division)
print(std)
# divide the division by the std and normalize to range 0 to 255 as unint8
result = np.divide(division, std)
result = skimage.exposure.rescale_intensity(result, in_range='image', out_range=(0,255)).astype(np.uint8)
# write result to disk
cv2.imwrite("lena_std_division2.jpg", result)
# display it
cv2.imshow("mean", mean)
cv2.imshow("median", median)
cv2.imshow("division", division)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
I am not sure I understand what you want. There are different types of normalization formulae.
The most common would be to subtract the mean from the image and then divide by the standard deviation. (I-mean(I))/std(I)
But if you want to do your formulae, I/std(I), then it can be done as follows:
Input:
import cv2
import numpy as np
import skimage.exposure
# load image
img = cv2.imread("lena.jpg")
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype(np.float64)/255
# get local mean from blurred gray image and square it
sigma=15
mean = cv2.GaussianBlur(gray, (0,0), sigmaX=sigma, sigmaY=sigma)
mean_sq = cv2.multiply(mean,mean)
# get mean of gray image squared
gray2 = cv2.multiply(gray,gray)
mean2 = cv2.GaussianBlur(gray2, (0,0), sigmaX=sigma, sigmaY=sigma)
# get variance image from the two means
var = cv2.subtract(mean2, mean_sq)
# get the standard deviation image from the variance image
std = np.sqrt(var)
print(std.dtype, np.amax(std), np.amin(std))
# divide image by std and scale using skimage
divide = (255*cv2.divide(gray, std, scale=1)).clip(0,255).astype(np.uint8)
divide = skimage.exposure.rescale_intensity(divide, in_range='image', out_range=(0,255)).astype(np.uint8)
print(divide.dtype, np.amax(divide), np.amin(divide))
# write result to disk
cv2.imwrite("lena_std_division.jpg", divide)
# display it
cv2.imshow("std", std)
cv2.imshow("divide", divide)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result (depending upon the sigma value):
An alternate formula for which I have posted a number of examples (called division normalization), would be to divide the image by its local mean image. I/mean(I))

Computing a contrast map

I am trying to compute the contrast around each pixel in an NxN window and saving the results in a new image where each pixel in the new image is the contrast of the area around it in the old image. From another post I got this:
1) Convert the image to say LAB and get the L channel
2) Compute the max for an NxN neighborhood around each pixel
3) Compute the min for an NxN neighborhood around each pixel
4) Compute the contrast from the equation above at each pixel.
5) Insert the contrast as a pixel value in new image.
Currently I have the following:
def cmap(roi):
max = roi.reshape((roi.shape[0] * roi.shape[1], 3)).max(axis=0)
min = roi.reshape((roi.shape[0] * roi.shape[1], 3)).min(axis=0)
contrast = (max - min) / (max + min)
return contrast
def cm(img):
# convert to LAB color space
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# separate channels
L, A, B = cv2.split(lab)
img_shape = L.shape
size = 5
shape = (L.shape[0] - size + 1, L.shape[1] - size + 1, size, size)
strides = 2 * L.strides
patches = np.lib.stride_tricks.as_strided(L, shape=shape, strides=strides)
patches = patches.reshape(-1, size, size)
output_img = np.array([cmap(roi) for roi in patches])
cv2.imwrite("labtest.png", output_img)
The code complains about the size of roi. Is there a better (pythonic) way of doing what I want?
You may use Dilation and Erosion morphological operations for finding the max and min for NxN neighborhood.
Dilation of NxN is equivalent to maximum of NxN neighborhood.
Erosion of NxN is equivalent to minimum of NxN neighborhood.
Using morphological operations makes the solution much simpler than "manually" dividing the image into small blocks.
You may use the following stages:
Convert to LAB color space and get L channel.
Use "dilate" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood).
Use "erode" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood).
Convert images to type float (required before using division operation).
Compute contrast map (range of contrast map is [0, 1]).
Convert contrast map to type uint8 with rounding - the conversion loosed accuracy, so I can't recommend it (but I assume you need the conversion for getting the output as an image).
Here is a complete code sample:
import numpy as np
import cv2
size_n = 5 # NxN neighborhood around each pixel
# Read input image
img = cv2.imread('chelsea.png')
# Convert to LAB color space
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# Get the L channel
L = lab[:, :, 0]
# Use "dilate" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood)
img_max = cv2.morphologyEx(L, cv2.MORPH_DILATE, np.ones((size_n, size_n)))
# Use "erode" morphological operation (dilate is equivalent to finding maximum pixel in NxN neighborhood)
img_min = cv2.morphologyEx(L, cv2.MORPH_ERODE, np.ones((size_n, size_n)))
# Convert to type float (required before using division operation)
img_max = img_max.astype(float)
img_min = img_min.astype(float)
# Compute contrast map (range of img_contrast is [0, 1])
img_contrast = (img_max - img_min) / (img_max + img_min)
# Convert contrast map to type uint8 with rounding - the conversion loosed accuracy, so I can't recommend it.
# Note: img_contrast_uint8 is scaled by 255 (scaled by 255 relative to the original formula).
img_contrast_uint8 = np.round(img_contrast*255).astype(np.uint8)
# Show img_contrast as output
cv2.imshow('img_contrast', img_contrast_uint8)
cv2.waitKey()
cv2.destroyAllWindows()
Input image:
L image:
img_max:
img_min:
Contrast map img_contrast_uint8:

How to calculate the 10th and 90th percentile for a mask over a SimpleITK image

I want to calculate the 10th and 90th percentile of the pixels within a mask drawn over a simpleITK image. I only can get the mean.
I use the following code:
img = sitk.ReadImage(image_path)
label = sitk.ReadImage(label_path)
labelstatsFilter = sitk.LabelIntensityStatisticsImageFilter()
labelstatsFilter.Execute(label, img)
mean = labelstatsFilter.GetMean(1)
I couldn't find a method to calculate the percentiles except if I can convert the mask into a numpy array. I couldn't find a way to use the GetArrayfromImage method to work for the mask as it works for the main image.
Solution:
Use np.extract to extract the mask values for an array then use np.percentile
Example:
img = sitk.ReadImage(image_path)
label = sitk.ReadImage(label_path)
img = sitk.GetArrayFromImage(img)
label = sitk.GetArrayFromImage(label)
mask = np.extract(label,img)
10xpercentile= np.percentile(mask,10)

Incorrect number of indices while increasing brightness of a color image

Code works fine while reading a gray scale image to perform brightness. But same code doesn't work with color image.
How to perform brightness operation from a color image?
While reading the image using cv2.imread at the argument 0 works fine but I tried with 1 it doesn't work as 0 is for gray scale image and 1 for color image.
import numpy as np
import cv2
img = cv2.imread('image1.jpg',1)
height = img.shape[0]
width = img.shape[1]
brightness = 100
for i in np.arange(height):
for j in np.arange(width):
a = img.item(i,j)
b = a + brightness
if b > 255:
b = 255
img.itemset((i,j), b)
cv2.imwrite('brightness.jpg', img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
I expect a color image to be read and perform brightness operation but it is showing ValueError: incorrect number of indices for array
The problem is a colored image has multiple channels for the different color (example, RGB or RGBA) so when you do img.item(i,j) you are missing the third dimension (the three color channels). You can add another for loop that loops over each of the color channels but you can also just use numpy's minimum function to make it more efficient (i.e. add 255 to the values of your image and if it's greater than 255 it will use 255).
import numpy as np
import cv2
img = cv2.imread('image1.jpg',1)
brightness = 100
np.minimum(img + brightness, 255)
cv2.imwrite('brightness.jpg', img)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
One solution is to use OpenCV normalize to stretch the image to full dynamic range (0 to 255).
Here are two output results depending upon the min and max stretch limits. Note that normalize works on float data and the min and max values are nominally stretched to full range of 0 to 1. But we ned to clip the results to this range and then scale them to the range 0 to 255 before saving as uint8 for output, if the min and max values are outside the range of 0 to 1.
The first result is stretched to min and max of 0 to 1.
The second result is stretched to min and max of 0 to 1.2 in order to make it even brighter.
Image:
#!/bin/python3.7
import cv2
import numpy as np
# read image
img = cv2.imread("zelda1_bm20_cm20.jpg", cv2.IMREAD_COLOR)
# normalize float versions
norm_img1 = cv2.normalize(img, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F)
norm_img2 = cv2.normalize(img, None, alpha=0, beta=1.2, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F)
# scale to uint8
norm_img1 = (255*norm_img1).astype(np.uint8)
norm_img2 = np.clip(norm_img2, 0, 1)
norm_img2 = (255*norm_img2).astype(np.uint8)
# write normalized output images
cv2.imwrite("zelda1_bm20_cm20_normalize1.jpg",norm_img1)
cv2.imwrite("zelda1_bm20_cm20_normalize2.jpg",norm_img2)
# display input and both output images
cv2.imshow('original',img)
cv2.imshow('normalized1',norm_img1)
cv2.imshow('normalized2',norm_img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Min and Max (0 to 1):
Min and Max (0 to 1.2):

How can I replace a value from one array with a value in the same index of another array?

I have two 3D numpy arrays which represent two images. The shape of each array is (1080, 1920, 3). The number 3 represents the RGB value of each pixel in the image.
My goal is to replace every non-black pixel in the first array to the value of the "parallel" pixel (in the same index) from the other array.
How can I do this using only numpy methods?
Use a mask with True/False values
# All pixels should be normalized 0..1 or 0..254
first_img = np.random.rand(1920,1080,3)
second_img = np.random.rand(1920,1080,3)
eps = 0.01 # Black pixel threshold
mask = first_img.sum(axis=2) > eps
for i in range(first_img.shape[2]):
first_img[:,:,i] = (first_img[:, :, i] * mask) + ((1 - mask) * second_img[:, :, i])

Categories