Converting to 8-bit image causes white spots where black was. Why is this? - image

Img is a dtype=float64 numpy data type. When I run this code:
Img2 = np.array(Img, np.uint8)
the background of my images turns white. How can I avoid this and still get an 8-bit image?
Edit:
Sure, I can give more info. The single image is compiled from a stack of 400 images. They are each coming from an .avi video file, and each image is converted into a NumPy array like this:
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
A more complicated operation is performed on this whole stack, but does not involve creating new images. It's simply performing calculations on each 1D array to yield a single pixel.
The interpolation is most likely linear (the default in plotting images with matplotlib. The images were saved as .PNGs.

You probably see overflow. If you cast 257 to np.uint8, you will get 1. According to a google search, avi files contain images with a color depth of 15 - 24 bit. When you cast this depth to np.uint8, you will see white regions getting darkened and (if a normalization takes place somewhere) also dark regions getting white (-5 -> 251). For the regions that become bright, you could check whether you have negative pixel values in the original image Img.
The Docs say that sometimes you have to do some scaling to get a proper cast, and to rather use higher depth whenever possible to avoid artefacts.
The solution seems to be either working at higher depth, i.e. casting to np.uint16 or np.uint32, or to scale the pixel values before reducing the depth, i.e. with Img2 already being a numpy matrix
# make sure that values are between 0 and 255, i.e. within 8bit range
Img2 *= 255/Img2.max()
# cast to 8bit
Img2 = np.array(Img, np.uint8)

Related

A proper way to convert 2D Array into RGB or GrayScale image for precision difference

I have a 2D CNN model where I perform a classification task. My images are all coming from a sensor data after conversion.
So, normally, my way is to convert them into images using the following approach
newsize = (9, 1000)
pic = acc_normalized[0]
img = Image.fromarray(np.uint8(pic*255), 'L')
img = img.resize(newsize)
image_path = "Images_Accel"
image_name = "D1." + str(2)
img.save(f"{image_path}/{image_name}.jpeg")
This is what I obtain:
However, their precision is sort of important. For instance, some of the numerical values are like:
117.79348187327987 or 117.76568758022673.
As you see in the above line, their difference is the digits, when I use uint8, it only takes 117 to when converting it into image pixels and it looks the same, right? But, I'd like to make them different. In some cases, the difference is even at the 8th or 10th digit.
So, when I try to use mode F and save them .jpeg in Image.fromarray line it gives me error and says that PIL cannot write mode F to jpeg.
Then, I tried to first convert them RGB like following;
img = Image.fromarray(pic, 'RGB')
I am not including np.float32 just before pic or not multiplying it by 255 as it is. Then, I convert this image to grayscale. This is what I got for RGB image;
After converting RGB into grayscale:
As you see, it seems that there is a critical different between the first pic and the last pic. So, what should be the proper way to use them in 2D CNN classification? or, should I convert them into RGB and choose grayscale in CNN implementation and a channel of 1? My image dimensions 1000x9. I can even change this dimension like 250x36 or 100x90. It doesn't matter too much. By the way, in the CNN network, I am able to get more than 90% test accuracy when I use the first-type of image.
The main problem here is using which image conversion method I'll be able to take into account those precision differences across the pixels. Would you give me some idea?
---- EDIT -----
Using .tiff format I made some quick comparisons.
First of all, my data looks like the following;
So, if I convert this first reading into an image using the following code where I use np.float64 and L gives me a grayscale image;
newsize = (9, 1000)
pic = acc_normalized[0]
img = Image.fromarray(np.float64(pic), 'L')
img = img.resize(newsize)
image_path = "Images_Accel"
image_name = "D1." + str(2)
img.save(f"{image_path}/{image_name}.tiff")
It gives me this image;
Then, the first 15x9 matrix seems like following image; The contradiction is that if you take a closer look at the numerical array, for instance (1,4) member, it's a complete black where the numerical array is equal to 0.4326132099074307. For grayscale images, black means that it's close to 0 cause it makes white if it's close to 1. However, if it's making a row operation, there is another value closer to 0 and I was expecting to see it black at (1,5) location. If it does a column operation, there is again something wrong. As I said, this data has been already normalized and varies within 0 and 1. So, what's the logic that it converts the array into an image? What kind of operation it does?
Secondly, if I first get an RGB image of the data and then convert it into a grayscale image, why I am not having exactly the same image as what I obtained first? Should the image coming from direct grayscale conversion (L method, np.float64) and the one coming from RGB-based (first I get RGB then convert it to grayscale) be the same? There is a difference in black-white pixels in those images. I do not know why we have it.
---- EDIT 2 ----
.tiff image with F mode and np.float32 gives the following;
I don't really understand your question, but you seem to want to store image differences that are less than 1, i.e. less than the resolution of integer values.
To do so, you need to use an image format that can store floats. JPEG, PNG, GIF, TGA and BMP cannot store floats. Instead, use TIFF, EXR or PFM formats which can handle floats.
Alternatively, you can create 16-bit PNG images wherein each pixel can store values in range 0..65535. So, say the maximum difference you wanted to store was 60 you could calculate the difference and multiply it by 1000 and round it to make an integer in range 0..60000 and store as 16-bit PNG.
You could record the scale factor as a comment within the image if it is variable.

Applying an image as a mask in matlab

I am a new user on image processing via Matlab. My first aim is applying the article and comparing my results and authors' results.
The article can be found here: http://arxiv.org/ftp/arxiv/papers/1306/1306.0139.pdf
First problem, Image Quality: In Figure 7, masks are defined but I couldn't reach the mask data set, and I use the screenshot so image quality is low. In my view, it can effect the results. Is there any suggestions?
Second problem, Merging images: I want to apply mask 1 on the Lena. But I don't want to use paint =) On the other hand, is it possible merging the images and keeping the lena?
You need to create the mask array. The first step is probably to turn your captured image from Figure 7 into a black and white image:
Mask = im2bw(Figure7, 0.5);
Now the background (white) is all 1 and the black line (or text) is 0.
Let's make sure your image of Lena that you got from imread is actually grayscale:
LenaGray = rgb2gray(Lena);
Finally, apply your mask on Lena:
LenaAndMask = LenaGray.*Mask;
Of course, this last line won't work if Lena and Figure7 don't have the same size, but this should be an easy fix.
First of all, You have to know that this paper is published in archive. when papers published in archive it is always a good idea to know more about the author and/or the university that published the paper.
TRUST me on that: you do not need to waste your time on this paper.
I understand your demand: but it is not a good idea to do get the mask by doing print screen. The pixel values that can be achieved by using print screen may not be the same as the original values. The zoom may change the size. so you need to be sure that the sizes are the same.
you can do print screen. past the image.
crop the mask.
convert rgb to gray scale.
threshold the gray scale to get the binary.
if you saved the image as jpeg. distortions because of high frequency edges will change edge shape.

Lung segmentation in Matlab

Trying to segment out the lung region, I am having a lot of trouble. Incoming image is like this: (This is essentially a jpg conversion, and each pixel is 8 bits.)
I = dicomread('000019.dcm');
I8 = uint8(I / 256);
B = im2bw(I8, 0.007);
segmented = imclearborder(B);
Above script generates:
Q-1
I am interested in entire inner black part with white matter as well. I have started matlab couple of days ago, so not quite getting how can I do it. If it is not clear to you what kind of output I want, let me know-I will upload an image. But I think there is no need.
Q-2
in B = im2bw(I8, 0.007); why I need to give a threshold so low? with higher thresholds everything is white or black. I have read the documentation and as I understand it, the pixels with value less than 0.007 are marked black and everything above is white. Is it because of my 16-to-8 bit conversion?
An other automatic solution that I did quickly using ImageJ (there are the same algorithms in MatLab):
Automatic thresholding using Huang or Li in the color space of your choice (all of them work)
Opening with a structuring element of type disk (delete the small components)
Connected components labeling.
Delete the components that touches the border of the images.
Fill holes.
And you have a clean result.
Here's a working solution in python using OpenCV:
import cv2 #openCV
import numpy as np
filename = 'zFrkx.jpg' #name of file in quotations here... assumes file is in same dir as .py file
img_gray = cv2.imread(filename, 0) #converts jpg image to grayscale representation
min_val = 100 #try shifting these around to expand or collapse area of interest
max_val = 150
ret, lung_mask = cv2.threshold(img_gray, min_val, max_val, cv2.THRESH_BINARY_INV) #fixed threshold uses values you'll def above
lung_layer = cv2.bitwise_and(img_gray, img_gray, mask = lung_mask)
cv2.imwrite('cake.tif', lung_layer) #outputs desired layer to current working dir
I tried running the script with threshold values set arbitrarily to 100,150 and got the following result, from which you could select the largest continuous element using dilation and segmentation techniques (http://docs.opencv.org/master/d3/db4/tutorial_py_watershed.html#gsc.tab=0).
Also, I suggest you crop the bottom and top X pixels to cut out text since no lung will fill the top or bottom of the picture.
Use tif instead of jpg format to avoid compression related artifact.
I know you noted that you'd like the medullar(?) white matter, too. Would be glad to help with that, but could you first explain in plain english how your shared matlab code works? Seems to work pretty well for the WM.
Hope this helps!

To imread Parula image in Matlab without losing resolution

There is no bijection between RGB and Parula, discussed here.
I am thinking how to do well the image processing of files in Parula.
This challenge has been developed from this thread about removing black color from ECG images by extending the case to a generalized problem with Parula colors.
Data:
which is generated by
[X,Y,Z] = peaks(25);
imgParula = surf(X,Y,Z);
view(2);
axis off;
It is not the point of this thread to use this code in your solution to read the second image.
Code:
[imgParula, map, alpha] = imread('http://i.stack.imgur.com/tVMO2.png');
where map is [] and alpha is a completely white image. Doing imshow(imgParula) gives
where you see a lot of interference and lost of resolution because Matlab reads images as RGB, although the actual colormap is Parula.
Resizing this picture does not improve resolution.
How can you read image into corresponding colormap in Matlab?
I did not find any parameter to specify the colormap in reading.
The Problem
There is a one-to-one mapping from indexed colors in the parula colormap to RGB triplets. However, no such one-to-one mapping exists to reverse this process to convert a parula indexed color back to RGB (indeed there are an infinite number ways to do so). Thus, there is no one-to-one correspondence or bijection between the two spaces. The plot below, which shows the R, G, and B values for each parula index, makes this clearer.
This is the case for most indexed colors. Any solution to this problem will be non-unique.
A Built-in Solution
I after playing around with this a bit, I realized that there's already a built-in function that may be sufficient: rgb2ind, which converts RGB image data to indexed image data. This function uses dither (which in turn calls the mex function ditherc) to perform the inverse colormap transformation.
Here's a demonstration that uses JPEG compression to add noise and distort the colors in the original parula index data:
img0 = peaks(32); % Generate sample data
img0 = img0-min(img0(:));
img0 = floor(255*img0./max(img0(:))); % Convert to 0-255
fname = [tempname '.jpg']; % Save file in temp directory
map = parula(256); % Parula colormap
imwrite(img0,map,fname,'Quality',50); % Write data to compressed JPEG
img1 = imread(fname); % Read RGB JPEG file data
img2 = rgb2ind(img1,map,'nodither'); % Convert RGB data to parula colormap
figure;
image(img0); % Original indexed data
colormap(map);
axis image;
figure;
image(img1); % RGB JPEG file data
axis image;
figure;
image(img2); % rgb2ind indexed image data
colormap(map);
axis image;
This should produce images similar to the first three below.
Alternative Solution: Color Difference
Another way to accomplish this task is by comparing the difference between the colors in the RGB image with the RGB values that correspond to each colormap index. The standard way to do this is by calculating ΔE in the CIE L*a*b* color space. I've implemented a form of this in a general function called rgb2map that can be downloaded from my GitHub. This code relies on makecform and applycform in the Image Processing Toolbox to convert from RGB to the 1976 CIE L*a*b* color space.
The following code will produce an image like the one on the right above:
img3 = rgb2map(img1,map);
figure;
image(img3); % rgb2map indexed image data
colormap(map);
axis image;
For each RGB pixel in an input image, rgb2map calculates the color difference between it and every RGB triplet in the input colormap using the CIE 1976 standard. The min function is used to find the index of the minimum ΔE (if more than one minimum value exists, the index of the first is returned). More sophisticated means can be used to select the "best" color in the case of multiple ΔE minima, but they will be more costly.
Conclusions
As a final example, I used an image of the namesake Parula bird to compare the two methods in the figure below. The two results are quite different for this image. If you manually adjust rgb2map to use the more complex CIE 1994 color difference standard, you'll get yet another rendering. However, for images that more closely match the original parula colormap (as above) both should return more similar results. Importantly, rgb2ind benefits from calling mex functions and is almost 100 times faster than rgb2map despite several optimizations in my code (if the CIE 1994 standard is used, it's about 700 times faster).
Lastly, those who want to learn more about colormaps in Matlab, should read this four-part MathWorks blog post by Steve Eddins on the new parula colormap.
Update 6-20-2015: rgb2map code described above updated to use different color space transforms, which improves it's speed by almost a factor of two.

How to display a Gray scale image using boundary defined in another binary image

I have a original gray scale image(I m using mammogram image with labels outside image).
I need to remove some objects(Labels) in that image, So i converted that grayscale image to a binary image. Then i followed the answer method provided in
How to Select Object with Largest area
Finally i extracted an Object with largest area as binary image. I want that region in gray scale for accessing and segmenting small objects within that. For example. Minor tissues in region and also should detect its edge.
**
How can i get that separated object region as grayscale image or
anyway to get the largest object region from gray scale directly
without converting to binary or any other way.?
**
(I am new to matlab. I dono whether i explained it correctly or not. If u cant get, I ll provide more detail)
If I understood you correctly, you are looking to have a gray image with only the biggest blob being highlighted.
Code
img = imread(IMAGE_FILEPATH);
BW = im2bw(img,0.2); %%// 0.2 worked to get a good area for the biggest blob
%%// Biggest blob
[L, num] = bwlabel(BW);
counts = sum(bsxfun(#eq,L(:),1:num));
[~,ind] = max(counts);
BW = (L==ind);
%%// Close the biggest blob
[L,num] = bwlabel( ~BW );
counts = sum(bsxfun(#eq,L(:),1:num));
[~,ind] = max(counts);
BW = ~(L==ind);
%%// Original image with only the biggest blob highlighted
img1 = uint8(255.*bsxfun(#times,im2double(img),BW));
%%// Display input and output images
figure,
subplot(121),imshow(img)
subplot(122),imshow(img1)
Output
If I understand your question correctly, you want to use the binary map and access the corresponding pixel intensities in those regions.
If that's the case, then it's very simple. You can use the binary map to identify the spatial co-ordinates of where you want to access the intensities in the original image. Create a blank image, then copy over these intensities over to the blank image using those spatial co-ordinates.
Here's some sample code that you can play around with.
% Assumptions:
% im - Original image
% bmap - Binary image
% Where the output image will be stored
outImg = uint8(zeros(size(im)));
% Find locations in the binary image that are white
locWhite = find(bmap == 1);
% Copy over the intensity values from these locations from
% the original image to the output image.
% The output image will only contain those pixels that were white
% in the binary image
outImg(locWhite) = im(locWhite);
% Show the original and the result side by side
figure;
subplot(1,2,1);
imshow(im); title('Original Image');
subplot(1,2,2);
imshow(outImg); title('Extracted Result');
Let me know if this is what you're looking for.
Method #2
As suggested by Rafael in his comments, you can skip using find all together and use logical statements:
outImg = img;
outImg(~bmap) = 0;
I decided to use find as it less obfuscated for a beginner, even though it is less efficient to do so. Either method will give you the correct result.
Some food for thought
The extracted region that you have in your binary image has several holes. I suspect you would want to grab the entire region without any holes. As such, I would recommend that you fill in these holes before you use the above code. The imfill function from MATLAB works nicely and it accepts binary images as input.
Check out the documentation here: http://www.mathworks.com/help/images/ref/imfill.html
As such, apply imfill on your binary image first, then go ahead and use the above code to do your extraction.

Resources