Why PIL.ImageChops.difference and np.array absolute difference have different results? Pillow document says ImageChops.difference acts just like absolute difference(https://pillow.readthedocs.io/en/3.1.x/reference/ImageChops.html).
tamp_image = Image.open(tamp_file_path).convert("RGB")
orig_image = Image.open(orig_file_path).convert("RGB")
diff = ImageChops.difference(orig_image, tamp_image)
diff.show() #1
Image.fromarray(abs(np.array(tamp_image)-np.array(orig_image))).show() #2
results(top:#1, bottom:#2):
Interestingly, if I convert diff to np.array and then Image object again, it shows like #1.
I had a similar problem. The solution is quite simple, the converted numpy arrays have the datatype uint8. By subtracting large values, the bits will flip entirely, and you get some weird looking result.
So the solution is to convert the images to a datatype with an appropriate range, like int8.
img1 = np.array(tamp_image, dtype='int8')
img2 = np.array(orig_image, dtype='int8')
diff = np.abs(img1 - img2)
Image.fromarray(np.array(diff, dtype='uint8')).show()
Related
When I was searching internet for an algorithm to correct luminance I came across this article about prospective correction and retrospective correction. I'm mostly interested in the prospective correction. Basically we take pictures of the scene with image in it(original one), and two other ,one bright and one dark, pictures where we only see the background of the original picture.
My problem is that I couldn't find any adaptation of these formulas in openCV or code example. I tried to use the formulas as they were in my code but this time I had a problem with data types. This happened when I tried to find C constant by applying operations on images.
This is how I implemented the formula in my code:
def calculate_C(im, im_b):
fx_mean = cv.mean(im)
fx_over_bx = np.divide(im,im_b)
mean_fx_bx = cv.mean(fx_over_bx)
c = np.divide(fx_mean, mean_fx_bx)
return c
#Basic image reading and resizing
# Original image
img = cv.imread(image_path)
img = cv.resize(img, (1000,750))
# Bright image
b_img = cv.imread(bright_image_path)
b_img = cv.resize(b_img, (1000,750))
# Calculating C constant from the formula
c_constant = calculate_C(img, b_img)
# Because I have only the bright image I am using second formula from the article
img = np.multiply(np.divide(img,b_img), c_constant)
When I try to run this code I get the error:
img = np.multiply(np.divide(img,b_img), c_constant)
ValueError: operands could not be broadcast together with shapes (750,1000,3) (4,)
So, is there anything I can do to fix my code? or is there any hints that you can share with me to handle luminance correction with this method or better methods?
You are using cv2.mean function which returns array with shape (4,) - mean value for each channel. You may need to ignore last channel and correctly broadcast it to numpy.
Or you could use numpy for calculations instead of opencv.
I just take example images from provided article.
grain.png:
grain_background.png:
Complete example:
import cv2
import numpy as np
from numpy.ma import divide, mean
f = cv2.imread("grain.png")
b = cv2.imread("grain_background.png")
f = f.astype(np.float32)
b = b.astype(np.float32)
C = mean(f) / divide(f, b).mean()
g = divide(f, b) * C
g = g.astype(np.uint8)
cv2.imwrite("grain_out.png", g)
Your need to use masked divide operation because ordinary operation could lead to division by zero => nan values.
Resulting image (output.png):
In my ex-post I optimize the python iteration loop into numpy way.
Then I face next problem to convert it to binary image like this
def convertRed(rawimg):
blue = rawimg[:,:,0]
green = rawimg[:,:,1]
red = rawimg[:,:,2]
exg = 1.5*red-green-blue
processedimg = np.where(exg > 50, exg, 2)
ret2,th2 = cv2.threshold(processedimg,0,255,cv2.THRESH_OTSU) //error line
return processedimg
The error is here
error: (-215) src.type() == CV_8UC1 in function cv::threshold
How to solve this problem?
The cv2.threshold function only accepts uint8 values, this means that you can only apply Otsu's algorithm if the pixel values in your image are between 0 and 255.
As you can see, when you multiply your values by 1.5 your image starts to present floating point values, making your image not suited for cv2.threshold, hence your error message src.type() == CV_8UC1.
You can modify the following parts of your code:
processedimg = np.where(exg > 50, exg, 2)
processedimg = cv2.convertScaleAbs(processedimg)
ret2,th2 = cv2.threshold(processedimg,0,255,cv2.THRESH_OTSU) //error line
What we are doing here is using the OpenCV function cv2.convertScaleAbs, you can see in the OpenCV Documentation:
cv2. convertScaleAbs
Scales, calculates absolute values, and converts the result to 8-bit.
Python: cv2.convertScaleAbs(src[, dst[,
alpha[, beta]]]) → dst
it is an error of "Data Type" ,
as Eliezer said,
when you multiply by 1.5 , the exg matrix convert to float64, which didn't work for cv2.threshold which require uint8 data type ,
so , one of the solutions could be adding:
def convertRed(rawimg):
b = rawimg[:,:,0]
g = rawimg[:,:,1]
r = rawimg[:,:,2]
exg = 1.5*r-g-b;
processedimg = np.where(exg > 50, exg, 2)
processedimg = np.uint8(np.abs(processedimg));#abs to fix negative values,
ret2,th2 = cv2.threshold(processedimg,0,255,cv2.THRESH_OTSU) #error line
return processedimg
I used np.uint8() after np.abs() to avoid wrong result,(nigative to white ) in the converstion to uint8 data type.
Although , your very array processedimg is positive, because of the np.where statement applied before , but this practice is usually safer.
why it converts to float64? because in python , when multiply any integer value with "float comma" it get converted to float ,
Like :
type(1.5*int(7))==float # true
another point is the usage of numpy functions instead of Opencv's, which is usually faster .
I've loaded a CT scan in SimpleITK. I'd like to do a few things that are pretty simple in NumPy, but haven't figured out how to do them in SimpleITK. I'd like to do them in SimpleITK for speed.
# NumPy: Changes all values of 100 to now become 500
nparr = nparr[nparr == 100] = 500
# SimpleITK:
???
SimpleITK image==100 will produce a binary image of the same dimension, where all intensities==100 are 1/True. This is desired. But I don't believe SimpleITK supports boolean indexing unfortunately. What's the most efficient way to accomplish this?
I've come up with this funky looking thing; but I was hoping to find the intended method / best practice means for doing this:
# Cast because data type returned is uint8 otherwise
difference = 500 - 100
offset = SimpleITK.Cast( image == 100), sitk.sitkInt32 ) * difference
image += offset
You can use the BinaryTheshold filter.
result = sitk.BinaryThreshold( image, 100, 101, 500, 0 )
That should only select pixels with intensity 100.
You are working using the SimpleITK image object to use it in a numpy style you need to use the methods GetArrayFromImage and GetImageFromArray to then get pixel access by converting the imagedata into a numpy array.
import SimpleITK as sitk
difference = 500 - 100
img_arr = sitk.GetArrayFromImage(image)
offset = img_arr[img_arr == 100] * difference
output = sitk.GetImageFromArray(image += offset)
I wrote a multilanguage 3-D image denoising ImageJ plugin that does some operations on an image and returns the denoised image as a 1-D array. The 1-D array contains NaN values (around the edges). The 1-D array is converted back into an image stack and displayed. It is simply black. I saved the image stack and opened it in ImageJ again. I moved my cursor over the image and saw the values change as they should. In some places (where I presume the neurone is) the pixels values were in the range of 1000-4000 . Yet, the whole image was simply pitch black. Here is a snippet of the code that converts the array into an image-stack at the end:
# Image-denoising routines written in C (this is where the nan values are introduced)
fimg = JNApackage.NativeCodeJNA.NativeCall(InputImgArray, medfiltArray, int(searchradius), int(patchradius), beta , int(x), int(y), int(z))
# Optimal Inverse Anscombe Transform (Some more operations in Jython)
fimg = InverseAnscombe.InvAnscombe(fimg)
InputImg.flush()
outputstack = ImageStack(x, y, z )
for i in xrange(0, z):
# Get the slice at index i and assign array elements corresponding to it.
outputstack.setPixels(fimg[int(i*x*y):int((i+1)*x*y)], i+1)
print 'Preparing denoised image for display '
outputImp = ImagePlus("Output Image", outputstack)
#print "OutputImage Stats:"
Stats = StackStatistics(outputImp)
print "mean:", Stats.mean, "minimum:", Stats.min, "maximum:", Stats.max
outputImp.show()
Any help as to what is going on?
The display range of your image might not be set correctly.
Try
outputImp.resetDisplayRange()
or
outputImp.setDisplayRange(Stats.min, Stats.max)
See the ImagePlus javadoc for more info.
I'm trying to add two images together using NumPy and PIL. The way I would do this in MATLAB would be something like:
>> M1 = imread('_1.jpg');
>> M2 = imread('_2.jpg');
>> resM = M1 + M2;
>> imwrite(resM, 'res.jpg');
I get something like this:
alt text http://www.deadlink.cc/matlab.jpg
Using a compositing program and adding the images the MATLAB result seems to be right.
In Python I'm trying to do the same thing like this:
from PIL import Image
from numpy import *
im1 = Image.open('/Users/rem7/Desktop/_1.jpg')
im2 = Image.open('/Users/rem7/Desktop/_2.jpg')
im1arr = asarray(im1)
im2arr = asarray(im2)
addition = im1arr + im2arr
resultImage = Image.fromarray(addition)
resultImage.save('/Users/rem7/Desktop/a.jpg')
and I get something like this:
alt text http://www.deadlink.cc/python.jpg
Why am I getting all those funky colors? I also tried using ImageMath.eval("a+b", a=im1, b=im2), but I get an error about RGB unsupported.
I also saw that there is an Image.blend() but that requires an alpha.
What's the best way to achieve what I'm looking for?
Source Images (images have been removed):
alt text http://www.deadlink.cc/_1.jpg
alt text http://www.deadlink.cc/_2.jpg
Humm, OK, well I added the source images using the add image icon and they show up when I'm editing the post, but for some reason the images don't show up in the post.
(images have been removed) 2013 05 09
As everyone suggested already, the weird colors you're observing are overflow. And as you point out in the comment of schnaader's answer you still get overflow if you add your images like this:
addition=(im1arr+im2arr)/2
The reason for this overflow is that your NumPy arrays (im1arr im2arr) are of the uint8 type (i.e. 8-bit). This means each element of the array can only hold values up to 255, so when your sum exceeds 255, it loops back around 0:
>>>array([255,10,100],dtype='uint8') + array([1,10,160],dtype='uint8')
array([ 0, 20, 4], dtype=uint8)
To avoid overflow, your arrays should be able to contain values beyond 255. You need to convert them to floats for instance, perform the blending operation and convert the result back to uint8:
im1arrF = im1arr.astype('float')
im2arrF = im2arr.astype('float')
additionF = (im1arrF+im2arrF)/2
addition = additionF.astype('uint8')
You should not do this:
addition = im1arr/2 + im2arr/2
as you lose information, by squashing the dynamic of the image (you effectively make the images 7-bit) before you perform the blending information.
MATLAB note: the reason you don't see this problem in MATLAB, is probably because MATLAB takes care of the overflow implicitly in one of its functions.
Using PIL's blend() with an alpha value of 0.5 would be equivalent to (im1arr + im2arr)/2. Blend does not require that the images have alpha layers.
Try this:
from PIL import Image
im1 = Image.open('/Users/rem7/Desktop/_1.jpg')
im2 = Image.open('/Users/rem7/Desktop/_2.jpg')
Image.blend(im1,im2,0.5).save('/Users/rem7/Desktop/a.jpg')
To clamp numpy array values:
>>> c = a + b
>>> c[c > 256] = 256
It seems the code you posted just sums up the values and values bigger than 256 are overflowing. You want something like "(a + b) / 2" or "min(a + b, 256)". The latter seems to be the way that your Matlab example does it.
Your sample images are not showing up form me so I am going to do a bit of guessing.
I can't remember exactly how the numpy to pil conversion works but there are two likely cases. I am 95% sure it is 1 but am giving 2 just in case I am wrong.
1) 1 im1Arr is a MxN array of integers (ARGB) and when you add im1arr and im2arr together you are overflowing from one channel into the next if the components b1+b2>255. I am guessing matlab represents their images as MxNx3 arrays so each color channel is separate. You can solve this by splitting the PIL image channels and then making numpy arrays
2) 1 im1Arr is a MxNx3 array of bytes and when you add im1arr and im2arr together you are wrapping the component around.
You are also going to have to rescale the range back to between 0-255 before displaying. Your choices are divide by 2, scale by 255/array.max() or do a clip. I don't know what matlab does