I have an image which I want to use it in MATLAB. But, I am looking for a method by which I be able to automatically find that my image is binary (0 and 1) or continuous. Is there any solution of piece of code?
For starters you cannot formally talk about binary or continuous images. Digital images have a discrete set of values, taken from a finite value set depending on their format and pixel bit-wise representation.
For example a "binary" image would have 2 levels of gray (white and black), represented by 0 or 1 or any other combination of values, e.g. an image of levels 0, 255 is still "binary". A grayscale image for an 8-bit representation (i.e. 8 bits per pixel) will have 2^8 discrete levels of intensity from min 0 (black) to max 255 (white).
Thus you can test for the number of unique levels of gray, i.e. unique values in your input image:
I = imread(image_filename);
if length(unique(I))==2,
flag_binary = true
end
Examples:
I = imread('cameraman.tif');
>> disp(flag_binary)
0
I = imread('circles.png');
>> disp(flag_binary)
1
From your question I'm guessing you are dealing only with images of the logical or double class. The first should be used for real binary images but unfortunately, that's not always the case when using code out in the wild.
It seems to me your problem is to distinguish between a real image of double class (all values between 0 and 1) or a binary image as class double (all values are 0 or 1). The best way to do it is the following which returns true if the image only has the values 1 and 0:
bool = all ((image(:) == 1) + (image(:) == 0));
This is a line from isbw() in Octave image package where you can use isbw (img, "non-logical")
Calculate the histogram using imhist. If there are more than two distinct grayvalues in the histogram your image is not binary.
Related
I have just started learning image processing by myself, and I am using MATLAB. I have been getting myself familiarized with basic image operations. When I read the below image(res: 225x300), which is supposed to be an 8-bit color image, I expected the resultant matrix to have 3 dimensions with one each for RGB. A simple web search about 8-bit color image led me to Wikipedia with the following information:
The simplest form of quantization frequently called 8-bit truecolor is to simply assign 3 bits to red, 3 to green and 2 to blue (the human eye is less sensitive to blue light) to create a 3-3-2.
Therefore, I expected the image matrix to have 225x300x3 dimensions with the above distribution of bits b/w RGB. But after I checked the dimensions of the matrix of the image, I found it to be 225x300 unit8, which is what one expects from an 8-bit grayscale image. But the image is a color image, as seen by any image viewer. So what is that I lack in knowledge or doing wrong? Is the problem with how I read the image?
Also, it occurred to me that uint8 is the smallest unsigned integer class. So how can we have colored images of 4,8,10, etc., bits represented and created?
My code:
>> I_8bit = imread('input_images\8_bit.png');
>> size(I_8bit)
ans =
225 300
>> class(I_8bit)
ans =
'uint8'
>> I_24bit = imread('input_images\24_bit.png');
>> size(I_24bit)
ans =
225 300 3
>> class(I_24bit)
ans =
'uint8'
(source: https://en.wikipedia.org/wiki/Color_depth#/media/File:8_bit.png)
Matlab supports several types of images, including
RGB images, which allow arbitrary colors, stored in terms of R,G,B components. The image is defined by a 3D m×n×3 array
Indexed images, in which each pixel is defined by an index to a colormap. The image is defined by a 2D m×n array and a c×3 colormap, where c si the number of colors.
It looks like the image you are loading is indexed. So you need the two-output version of imread to get the 2D array and the colormap:
[I_8bit, cmap] = imread('input_images\8_bit.png');
To display the image you need to specify the 2D array and the colormap:
imshow(I_8bit, cmap)
You can see the effect of changing the colormap, for example
cmap_wrong = copper(size(cmap, 1)); % different colormap with the same size
imshow(I_8bit, cmap_wrong)
To convert to an RGB image, use ind2rgb:
I_8bit_RGB = ind2rgb(I_8bit, cmap);
which then you can display as
imshow(I_8bit_RGB)
I am learning image analysis and trying to average set of color images and get standard deviation at each pixel
I have done this, but it is not by averaging RGB channels. (for ex rchannel = I(:,:,1))
filelist = dir('dir1/*.jpg');
ims = zeros(215, 300, 3);
for i=1:length(filelist)
imname = ['dir1/' filelist(i).name];
rgbim = im2double(imread(imname));
ims = ims + rgbim;
end
avgset1 = ims/length(filelist);
figure;
imshow(avgset1);
I am not sure if this is correct. I am confused as to how averaging images is useful.
Also, I couldn't get the matrix holding standard deviation.
Any help is appreciated.
If you are concerned about finding the mean RGB image, then your code is correct. What I like is that you converted the images using im2double before accumulating the mean and so you are making everything double precision. As what Parag said, finding the mean image is very useful especially in machine learning. It is common to find the mean image of a set of images before doing image classification as it allows the dynamic range of each pixel to be within a normalized range. This allows the training of the learning algorithm to converge quickly to the optimum solution and provide the best set of parameters to facilitate the best accuracy in classification.
If you want to find the mean RGB colour which is the average colour over all images, then no your code is not correct.
You have summed over all channels individually which is stored in sumrgbims, so the last step you need to do now take this image and sum over each channel individually. Two calls to sum in the first and second dimensions chained together will help. This will produce a 1 x 1 x 3 vector, so using squeeze after this to remove the singleton dimensions and get a 3 x 1 vector representing the mean RGB colour over all images is what you get.
Therefore:
mean_colour = squeeze(sum(sum(sumrgbims, 1), 2));
To address your second question, I'm assuming you want to find the standard deviation of each pixel value over all images. What you will have to do is accumulate the square of each image in addition to accumulating each image inside the loop. After that, you know that the standard deviation is the square root of the variance, and the variance is equal to the average sum of squares subtracted by the mean squared. We have the mean image, now you just have to square the mean image and subtract this with the average sum of squares. Just to be sure our math is right, supposing we have a signal X with a mean mu. Given that we have N values in our signal, the variance is thus equal to:
Source: Science Buddies
The standard deviation would simply be the square root of the above result. We would thus calculate this for each pixel independently. Therefore you can modify your loop to do that for you:
filelist = dir('set1/*.jpg');
sumrgbims = zeros(215, 300, 3);
sum2rgbims = sumrgbims; % New - for standard deviation
for i=1:length(filelist)
imname = ['set1/' filelist(i).name];
rgbim = im2double(imread(imname));
sumrgbims = sumrgbims + rgbim;
sum2rgbims = sum2rgbims + rgbim.^2; % New
end
rgbavgset1 = sumrgbims/length(filelist);
% New - find standard deviation
rgbstdset1 = ((sum2rgbims / length(filelist)) - rgbavgset.^2).^(0.5);
figure;
imshow(rgbavgset1, []);
% New - display standard deviation image
figure;
imshow(rgbstdset1, []);
Also to make sure, I've scaled the display of each imshow call so the smallest value gets mapped to 0 and the largest value gets mapped to 1. This does not change the actual contents of the images. This is just for display purposes.
I have two black and white images and I need to calculate the mutual information.
Image 1 = X
Image 2 = Y
I know that the mutual information can be defined as:
MI = entropy(X) + entropy(Y) - JointEntropy(X,Y)
MATLAB already has built-in functions to calculate the entropy but not to calculate the joint entropy. I guess the true question is: How do I calculate the joint entropy of two images?
Here is an example of the images I'd like to find the joint entropy of:
X =
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Y =
0 0 0 0 0 0
0 0 0.38 0.82 0.38 0.04
0 0 0.32 0.82 0.68 0.17
0 0 0.04 0.14 0.11 0
0 0 0 0 0 0
To calculate the joint entropy, you need to calculate the joint histogram between two images. The joint histogram is essentially the same as a normal 1D histogram but the first dimension logs intensities for the first image and the second dimension logs intensities for the second image. This is very similar to what is commonly referred to as a co-occurrence matrix. At location (i,j) in the joint histogram, it tells you how many intensity values we have encountered that have intensity i in the first image and intensity j in the second image.
What is important is that this logs how many times we have seen this pair of intensities at the same corresponding locations. For example, if we have a joint histogram count of (7,3) = 2, this means that when we were scanning both images, when we encountered the intensity of 7, at the same corresponding location in the second image, we encountered the intensity of 3 for a total of 2 times.
Constructing a joint histogram is very simple to do.
First, create a 256 x 256 matrix (assuming your image is unsigned 8-bit integer) and initialize them to all zeroes. Also, you need to make sure that both of your images are the same size (width and height).
Once you do that, take a look at the first pixel of each image, which we will denote as the top left corner. Specifically, take a look at the intensities for the first and second image at this location. The intensity of the first image will serve as the row while the intensity of the second image will serve as the column.
Find this location in the matrix and increment this spot in the matrix by 1.
Repeat this for the rest of the locations in your image.
After you're done, divide all entries by the total number of elements in either image (remember they should be the same size). This will give us the joint probability distribution between both images.
One would be inclined to do this with for loops, but as it is commonly known, for loops are notoriously slow and should be avoided if at all possible. However, you can easily do this in MATLAB in the following way without loops. Let's assume that im1 and im2 are the first and second images you want to compare to. What we can do is convert im1 and im2 into vectors. We can then use accumarray to help us compute the joint histogram. accumarray is one of the most powerful functions in MATLAB. You can think of it as a miniature MapReduce paradigm. Simply put, each data input has a key and an associated value. The goal of accumarray is to bin all of the values that belong to the same key and do some operation on all of these values. In our case, the "key" would be the intensity values, and the values themselves are the value of 1 for every intensity value. We would then want to add up all of the values of 1 that map to the same bin, which is exactly how we'd compute a histogram. The default behaviour for accumarray is to add all of these values. Specifically, the output of accumarray would be an array where each position computes the sum of all values that mapped to that key. For example, the first position would be the summation of all values that mapped to the key of 1, the second position would be the summation of all values that mapped to the key of 2 and so on.
However, for the joint histogram, you want to figure out which values map to the same intensity pair of (i,j), and so the keys here would be a pair of 2D coordinates. As such, any intensities that have an intensity of i in the first image and j in the second image in the same spatial location shared between the two images go to the same key. Therefore in the 2D case, the output of accumarray would be a 2D matrix where each element (i,j) contains the summation of all values that mapped to key (i,j), similar to the 1D case that was mentioned previously which is exactly what we are after.
In other words:
indrow = double(im1(:)) + 1;
indcol = double(im2(:)) + 1; %// Should be the same size as indrow
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
With accumarray, the first input are the keys and the second input are the values. A note with accumarray is that if each key has the same value, you can simply assign a constant to the second input, which is what I've done and it's 1. In general, this is an array with the same number of rows as the first input. Also, take special note of the first two lines. There will inevitably be an intensity of 0 in your image, but because MATLAB starts indexing at 1, we need to offset both arrays by 1.
Now that we have the joint histogram, it's really simple to calculate the joint entropy. It is similar to the entropy in 1D, except now we are just summing over the entire joint probability matrix. Bear in mind that it will be very likely that your joint histogram will have many 0 entries. We need to make sure that we skip those or the log2 operation will be undefined. Let's get rid of any zero entries now:
indNoZero = jointHistogram ~= 0;
jointProb1DNoZero = jointProb(indNoZero);
Take notice that I searched the joint histogram instead of the joint probability matrix. This is because the joint histogram consists of whole numbers while the joint probability matrix will lie between 0 and 1. Because of the division, I want to avoid comparing any entries in this matrix with 0 due to numerical roundoff and instability. The above will also convert our joint probability matrix into a stacked 1D vector, which is fine.
As such, the joint entropy can be calculated as:
jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero));
If my understanding of calculating entropy for an image in MATLAB is correct, it should calculate the histogram / probability distribution over 256 bins, so you can certainly use that function here with the joint entropy that was just calculated.
What if we have floating-point data instead?
So far, we have assumed that the images that you have dealt with have intensities that are integer-valued. What if we have floating point data? accumarray assumes that you are trying to index into the output array using integers, but we can still certainly accomplish what we want with this small bump in the road. What you would do is simply assign each floating point value in both images to have a unique ID. You would thus use accumarray with these IDs instead. To facilitate this ID assigning, use unique - specifically the third output from the function. You would take each of the images, put them into unique and make these the indices to be input into accumarray. In other words, do this instead:
[~,~,indrow] = unique(im1(:)); %// Change here
[~,~,indcol] = unique(im2(:)); %// Change here
%// Same code
jointHistogram = accumarray([indrow indcol], 1);
jointProb = jointHistogram / numel(indrow);
indNoZero = jointHistogram ~= 0;
jointProb1DNoZero = jointProb(indNoZero);
jointEntropy = -sum(jointProb1DNoZero.*log2(jointProb1DNoZero));
Note that with indrow and indcol, we are directly assigning the third output of unique to these variables and then using the same joint entropy code that we computed earlier. We also don't have to offset the variables by 1 as we did previously because unique will assign IDs starting at 1.
Aside
You can actually calculate the histograms or probability distributions for each image individually using the joint probability matrix. If you wanted to calculate the histograms / probability distributions for the first image, you would simply accumulate all of the columns for each row. To do it for the second image, you would simply accumulate all of the rows for each column. As such, you can do:
histogramImage1 = sum(jointHistogram, 1);
histogramImage2 = sum(jointHistogram, 2);
After, you can calculate the entropy of both of these by yourself. To double check, make sure you turn both of these into PDFs, then compute the entropy using the standard equation (like above).
How do I finally compute Mutual Information?
To finally compute Mutual Information, you're going to need the entropy of the two images. You can use MATLAB's built-in entropy function, but this assumes that there are 256 unique levels. You probably want to apply this for the case of there being N distinct levels instead of 256, and so you can use what we did above with the joint histogram, then computing the histograms for each image in the aside code above, and then computing the entropy for each image. You would simply repeat the entropy calculation that was used jointly, but apply it to each image individually:
%// Find non-zero elements for first image's histogram
indNoZero = histogramImage1 ~= 0;
%// Extract them out and get the probabilities
prob1NoZero = histogramImage1(indNoZero);
prob1NoZero = prob1NoZero / sum(prob1NoZero);
%// Compute the entropy
entropy1 = -sum(prob1NoZero.*log2(prob1NoZero));
%// Repeat for the second image
indNoZero = histogramImage2 ~= 0;
prob2NoZero = histogramImage2(indNoZero);
prob2NoZero = prob2NoZero / sum(prob2NoZero);
entropy2 = -sum(prob2NoZero.*log2(prob2NoZero));
%// Now compute mutual information
mutualInformation = entropy1 + entropy2 - jointEntropy;
Hope this helps!
To perform K means clustering with k = 3 (segments). So I:
1) Converted the RGB img into grayscale
2) Casted the original image into a n X 1, column matrix
3) idx = kmeans(column_matrix)
4) output = idx, casted back into the same dimensions as the original image.
My questions are :
A
When I do imshow(output), I get a plain white image. However when I do imshow(output[0 5]), it shows the output image. I understand that 0 and 5 specify the display range. But why do I have to do this?
B)
Now the output image is meant to be split into 3 segments right. How do I threshold it such that I assign a
0 for the clusters of region 1
1 for clusters of region 2
2 for clusters of region 3
As the whole point of me doing this clustering is so that I can segment the image into 3 regions.
Many thanks.
Kind Regards.
A: Given that your matrix output contains scalar values ranging from 1 to 3, imshow(output) is treating this as a grayscale matrix and assuming that the full range of values is 0 to 255. This is why constraining the color limits is necessary as otherwise your image is all white or almost all white.
B: output = output - 1
As pointed out by Ryan, your problem is probably just how you display the image. Here's a working example:
snow = rand(256, 256);
figure;
imagesc(snow);
nClusters = 3;
clusterIndices = kmeans(snow(:), nClusters);
figure;
imagesc(reshape(clusterIndices, [256, 256]));
I need to Binarize an image in matlab with a static threshold of 10% of mean intensity. I find mean intensity using mean2(Image) and this returns a mean let say 15.10 in one of the image. Thus my mean threshold is 1.51.im2bw(image,level) takes threshold between 0 to 1. How to binarize my image in this case in matlab?
1) you can first convert the original image to double format using im2double(). Then all the pixels values will be between 0 and 1. Then you can use im2bw(im,level).
2) If you do not want to convert the image to double, then you can do it in this way. Let's say the threshold is 10 % of the the mean value, say threshold = 1.51. Let's denote the image you have is im. Then im(im<threshold) = 0; im(im>=threshold)=1. After these two operations, im will become a binary image.
You can binarize the image with a simple logical statement. For completeness, I've added the threshold determination as well.
threshold = mean(Image(:));
binaryMask = Image > 0.1 * threshold;
You need to normalize the result of the mean vs the max intensity of the image if you want to use im2bw (the other solutions mentioned are of course correct and work):
ImageN=Image./max(Image(:))
t = mean2(ImageN) * 0.1 % Find your threshold value
im2bw(Image,t)
Let's say your image is a matrix img, you can do the following:
t = mean2(img) * 0.1 % Find your threshold value
img(img < t) = 0 % Set everything below the treshold value to 0
img(img ̃= 0) = 1 % Set the rest to 1