Binarization of an image with desired threshold - matlab

I am trying to make a function, that would be given a grayscale image, which it will binarize depending on the set threshold. I've been at this all day but no success.
Here is the code I implemented. With the image being a specific image; Not a variable.
function [ output_args ] = thresholdImg(X)
A=imread('car_gray.jpg');
B=zeros(size(A,1),size(A,2));
for l=1:size(A,1)
for m=1:size(A,2)
if(sum(A(l,m,:))>X)
B(l,m)=1;
end
end
end
B=logical(B);
figure,imshow(B);
I don't want to use imbinarize. I want this to be preformed manually, and the code to be as simple as possible. The output of the image looks like it is binary, but when you print the matrix, you see that the values are not only 0s and 1s or 255s
What am I doing wrong?

It'd be faster to get rid of the loop altogether:
function [] = thresholdImg(X)
A=imread('car_gray.jpg');
tmp=sum(A,3); % sum along the third dimension
B = zeros(size(tmp));
B(tmp>X) = 1; % Set all values above the threshold to be one
B=logical(B);
figure,imshow(B);
Using the following conditions: A = rand(100,100,3);X=0.6; we get this picture:
We see that this is indeed, as we expected, fully binary. Additionally, you can check this by calling whos B, which tells us B is indeed of type logical and hence has only zeros and ones.
whos B
Name Size Bytes Class Attributes
B 100x100 10000 logical

Your problem appears when you save the image. If you check the description of imwrite, you will see that if you want to save your image as binary, you are supposed to choose BMP, PNG or TIFF. These are lossless formats. JPEG on the other hand is a lossy compression format.
In addition, I made your code very compact, according to Adriaan's answer:
function [] = thresholdImg(X)
A=imread('car_gray.jpg');
B = sum(A, 3) > X
figure,imshow(B);

Related

How to define multiple objects under a class in a for loop in matlab? [duplicate]

I have 200 time points. For each time point, there is an image, the size of which is 40*40 double, corresponds to this time point. For example, image 1 corresponds to time point 1; image k corresponds to time point k (k = 1,2,...,200).
The time points are T = 1:200, with the images named as Image_T, thus Image_1, Image_2 etc.
I want to put all these 200 images together. The final size is 40*40*200 double. The final image looks like fMRI image (fmri_szX = 40, fmri_szY = 40 and fmri_szT = 200). How to achieve that?
Thanks!
Dynamic variables
Note that whilst this is possible, it's considered to be bad programming (see for instance here, or this blog by Loren and even the Mathworks in their documentation tell you not to do this). It would be much better to load your images directly into either a 3D array or a cell structure, avoiding dynamic variable names. I just posted this for completeness; if you ever happen to have to use this solution, you should change to a (cell-) array immediately.
The gist of the linked articles as to why eval is such a bad idea, is that MATLAB can no longer predict what the outcome of the operation will be. For instance A=3*(2:4) is recognised by MATLAB to output a double-array. If you eval stuff, MATLAB can no longer do this. MATLAB is an interpreted language, i.e. each line of code is read then ran, without compiling the entire code beforehand. This means that each time MATLAB encounters eval, it has to stop, evaluate the expression, then check the output, store that, and continue. Most of the speed-engines employed by MATLAB (JIT/MAGMA etc) can't work without predicting the outcome of statements, and will therefore shut down during the eval evaluation, rendering your code very slow.
Also there's a security aspect to the usage of eval. Consider the following:
var1 = 1;
var2 = 2;
var3 = 3;
varnames = {'var1', 'var2; disp(''GOTCHA''); %', 'var3'};
accumvar = [];
for k = 1:numel(varnames)
vname = varnames{k};
disp(['Reading from variable named ' vname]); eval(['accumvar(end+1) = ' vname ';']);
end
Now accumvar will contain the desired variable names. But if you don't set accumvar as output, you might as well not use a disp, but e.g. eval('rm -rf ~/*') which would format your entire disk without even telling you it's doing so.
Loop approach
for ii = 200:-1:1
str = sprintf('Image_%d',ii);
A(:, :, :, ii) = eval(str);
end
This creates your matrix. Note that I let the for loop run backwards, so as to initialise A in its largest size.
Semi-vectorised approach
str = strsplit(sprintf('image_%d ',1:200),' '); % Create all your names
str(end) = []; % Delete the last entry (empty)
%Problem: eval cannot handle cells, loop anyway:
for ii = 200:-1:1
A(:, :, :, ii) = eval(str{ii});
end
eval does not support arrays, so you cannot directly plug the cellarray strin.
Dynamic file names
Despite having a similar title as above, this implies having your file names structured, so in the file browser, and not MATLAB. I'm assuming .jpg files here, but you can add every supported image extension. Also, be sure to have all images in a single folder and no additional images with that extension, or you have to modify the dir() call to include only the desired images.
filenames = dir('*.jpg');
for ii = length(filenames):-1:1
A(:,:,:,ii) = imread(filenames{ii});
end
Images are usually read as m*n*3 files, where m*n is your image size in pixels and the 3 stems from the fact that they're read as RGB by imread. Therefore A is now a 4D matrix, structured as m*n*3*T, where the last index corresponds to the time of the image, and the first three are your image in RGB format.
Since you do not specify how you obtain your 40*40 double, I have left the 4D matrix. Could be you read them and then switch to using a uint16 interpretation of RGB, which is a single number, which would result in a m*n*1*T variable, which you can reduce to a 3D variable by calling A = squeeze(A(:,:,1,:));

Looping through a list of elements from the workspace in Matlab [duplicate]

I have 200 time points. For each time point, there is an image, the size of which is 40*40 double, corresponds to this time point. For example, image 1 corresponds to time point 1; image k corresponds to time point k (k = 1,2,...,200).
The time points are T = 1:200, with the images named as Image_T, thus Image_1, Image_2 etc.
I want to put all these 200 images together. The final size is 40*40*200 double. The final image looks like fMRI image (fmri_szX = 40, fmri_szY = 40 and fmri_szT = 200). How to achieve that?
Thanks!
Dynamic variables
Note that whilst this is possible, it's considered to be bad programming (see for instance here, or this blog by Loren and even the Mathworks in their documentation tell you not to do this). It would be much better to load your images directly into either a 3D array or a cell structure, avoiding dynamic variable names. I just posted this for completeness; if you ever happen to have to use this solution, you should change to a (cell-) array immediately.
The gist of the linked articles as to why eval is such a bad idea, is that MATLAB can no longer predict what the outcome of the operation will be. For instance A=3*(2:4) is recognised by MATLAB to output a double-array. If you eval stuff, MATLAB can no longer do this. MATLAB is an interpreted language, i.e. each line of code is read then ran, without compiling the entire code beforehand. This means that each time MATLAB encounters eval, it has to stop, evaluate the expression, then check the output, store that, and continue. Most of the speed-engines employed by MATLAB (JIT/MAGMA etc) can't work without predicting the outcome of statements, and will therefore shut down during the eval evaluation, rendering your code very slow.
Also there's a security aspect to the usage of eval. Consider the following:
var1 = 1;
var2 = 2;
var3 = 3;
varnames = {'var1', 'var2; disp(''GOTCHA''); %', 'var3'};
accumvar = [];
for k = 1:numel(varnames)
vname = varnames{k};
disp(['Reading from variable named ' vname]); eval(['accumvar(end+1) = ' vname ';']);
end
Now accumvar will contain the desired variable names. But if you don't set accumvar as output, you might as well not use a disp, but e.g. eval('rm -rf ~/*') which would format your entire disk without even telling you it's doing so.
Loop approach
for ii = 200:-1:1
str = sprintf('Image_%d',ii);
A(:, :, :, ii) = eval(str);
end
This creates your matrix. Note that I let the for loop run backwards, so as to initialise A in its largest size.
Semi-vectorised approach
str = strsplit(sprintf('image_%d ',1:200),' '); % Create all your names
str(end) = []; % Delete the last entry (empty)
%Problem: eval cannot handle cells, loop anyway:
for ii = 200:-1:1
A(:, :, :, ii) = eval(str{ii});
end
eval does not support arrays, so you cannot directly plug the cellarray strin.
Dynamic file names
Despite having a similar title as above, this implies having your file names structured, so in the file browser, and not MATLAB. I'm assuming .jpg files here, but you can add every supported image extension. Also, be sure to have all images in a single folder and no additional images with that extension, or you have to modify the dir() call to include only the desired images.
filenames = dir('*.jpg');
for ii = length(filenames):-1:1
A(:,:,:,ii) = imread(filenames{ii});
end
Images are usually read as m*n*3 files, where m*n is your image size in pixels and the 3 stems from the fact that they're read as RGB by imread. Therefore A is now a 4D matrix, structured as m*n*3*T, where the last index corresponds to the time of the image, and the first three are your image in RGB format.
Since you do not specify how you obtain your 40*40 double, I have left the 4D matrix. Could be you read them and then switch to using a uint16 interpretation of RGB, which is a single number, which would result in a m*n*1*T variable, which you can reduce to a 3D variable by calling A = squeeze(A(:,:,1,:));

For Loop not advancing

I'm trying to read in a large number of jpg files using a for loop. But for some reason, the k index is not advancing. I just get A as a 460x520x3 uint8. Am I missing something?
My goal with this code is to convert all the jpg images to the same size. Since I haven't been able to advance through the images, I can't quite tell if I'm doing it right.
nFrames = length(date); % Number of frames.
for k = 1:nFrames-1 % Number of days
% Set file under consideration
A = imread(['map_EUS_' datestr(cell2mat(date_O3(k)),'yyyy_mm_dd') '_O3_MDA8.jpg']);
% Size of existing image A.
[rowsA, colsA, numberOfColorChannelsA] = size(A);
% Read in and get size of existing image B (the next image).
B = imread(['map_EUS_' datestr(cell2mat(date_O3(k+1)),'yyyy_mm_dd') '_O3_MDA8.jpg']);
[rowsB, colsB, numberOfColorChannelsB] = size(B);
% Size of B does not match A, so resize B to match A's size.
B = imresize(B, [rowsA colsA]);
eval(['print -djpeg map_EUS_' datestr(cell2mat(date_O3(k)),'yyyy_mm_dd') '_O3_MDA8_test.jpg']);
end
end
As you use imread to read the image in, it makes sense to use imwrite to write it out, rather than print/eval (also you should always think twice about using eval in MATLAB, and then think again).
You could also speed up this code somewhat - you just want to resize all the images to the size of whichever the first one is, so you don't need to keep reading in images to measure the size of them. Use imfinfo to get the size, then read in, resize, and write out B only.

How to efficiently loop through matrix elements

I have a matlab script for 8-bit image analysis and I'm trying to enhance objects in the image by subtracting the background with no objects present. What I want to do at a pixel level is:
if B-I>50 then E=I
else E=255-B-I
Where, B is the background, I the image and E my enhanced image. I know I can do this by looping through each element of the image matrix by the following:
diff=imsubtract(B,I);
nrows=1024;
ncols=1360;
for r=1:nrows
for c=1:ncols
if diff(r,c)>50
E=I(r,c);
else
E=255-diff(r,c);
end
end
end
But is this rather slow when going multiple images. I've also tried the follow:
E=255-diff;
E(diff>50)=I;
But receive the following error:
In an assignment A(I) = B, the
number of elements in B and I must
be the same.
Any tips on optimizing this would be greatly apprenticed!
In an assignment A(I) = B, the number of elements in B and I must be
the same.
The reason for this error is that you are trying to assign all the content of I to a subset of E (those pixels where diff>50). You have to specifically tell MATLAB that you want those pixels set to the matching pixels in I.
E(diff>50)=I(diff>50);
Incidentally you should be careful using imsubtract here. For pixels where I has a higher value than B, that will result in zeros (if your values are uint8). It may be okay (not 100% clear if you're looking for the absolute difference or really just where B is larger than I)
What if you use use find()
ind = find(B-I>50)
E(ind) = I(ind)
% And then the ones that are not `B-I>50`
E(~ind) = 255-B(~ind)-I(~ind)
Try this vectorized approach that uses logical indexing. I could not test it out on images though, so would be great if that's taken care of.
Code
diff1=double(imsubtract(B,I));
E = double(I).*(diff1>50) + (255-diff1).*(diff1<=50);
You might be needed to convert the datatype back to unsigned integer formats as used for images.

column to block using sliding window in matlab

using im2col sliding window in matlab i have converted the input image block into column and again by using col2im i do the inverse process but the output is not same as the input image. How can i recover the input image? can anyone please help me.
Here is the code
in=imread('tire.tif');
[mm nn]=size(in);
m=8;n=8;
figure,imshow(in);
i1=im2col(in,[8 8],'sliding');
i2 = reshape( sum(i1),mm-m+1,nn-n+1);
out=col2im(i2,[m n],[mm nn],'sliding');
figure,imshow(out,[]);
thanks in advance...
You didn't specify exactly what the problem is, but I see a few potential sources:
You shouldn't expect the output to be exactly the same as the input, since you're replacing each pixel value with the sum of pixels in an 8-by-8 neighborhood. Also, you will get a shrinkage of the resulting image by 7 pixels in each direction (i.e. [m-1 n-1]) since the 'sliding' option of IM2COL does not pad the array with zeroes to create neighborhoods for pixels near the edges.
These two lines are redundant:
i2 = reshape( sum(i1),mm-m+1,nn-n+1);
out=col2im(i2,[m n],[mm nn],'sliding');
You only need one or the other, not both:
%# Use this:
out = reshape(sum(i1),mm-m+1,nn-n+1);
%# OR this:
out = col2im(sum(i1),[m n],[mm nn],'sliding');
Image data in MATLAB is typically of type 'uint8', meaning each pixel is represented as an unsigned 8-bit integer spanning the range 0 to 255. Assuming this is what in is, when you perform your sum operation you will implicitly end up converting it to type 'double' (since an unsigned 8-bit integer will likely not be big enough to hold the sum totals). When image pixel values are represented with a double type, the pixel values are expected to span the range 0 to 1, so you will want to scale your resulting image by its maximum value to get it to display properly:
out = out./max(out(:));
Lastly, check what kind of input image you are using. For your code, you are essentially assuming in is 2-D (i.e. a grayscale intensity image). If it is a truecolor (i.e. RGB) image, the third dimension is going to cause you some trouble, and you will have to either process each color plane separately and recombine them or convert the RGB image to grayscale. If it is an indexed image (with an associated color map), you will not be able to do the sort of processing you describe above without first converting it to a grayscale representation.
Why are you expecting the output to be the same?
i2 is the result of performing a SUM around a pixel neighborhood (essentially a low-pass filter), which is the final blurry image that you see. i.e you are NOT doing an inverse process with the COL2IM call.
i1 obtained from 'sliding' option has the information that you would get from 'distinct' option as well, which you need to filter out. Now, this may not be the best way to code it up but it works. Assume that mm is a multiple of m and nn is a multiple of n. If this is not the case, then you'll have to zero-pad accordingly to make this the case.
in=imread('tire.tif');
[mm nn]=size(in);
m=8;n=8;
i1 = im2col(in,[m,n],'sliding');
inSel = [];
for k=0:mm/m-1
inSel = [inSel 1:n:nn+(nn-n+1)*n*k];
end
out = col2im(i1(:,inSel),[m,n],[mm,nn],'distinct');