For Loop not advancing - matlab

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.

Related

How to build filenames in a nested loop

I am trying to access 4k images and crop some ROI based areas (4 ROI in my case) and store them in some directory. So far everything is working okay except the loops handling of the filename.
Below is my code attached. I am accessing N 4k images, crop and resize them to my desired resolution. In the end, when I tried to save the data the images got overwritten.
N=2;
for img = 1:N
x = gTruth.LabelData.crack{img,1}
for i=1:4
Cells = x(i,1:4)
baseFileName = theFiles(img).name;
fullFileName = fullfile(myFolder, baseFileName);
fprintf(1, 'Now reading %s\n', fullFileName);
imageArray = imread(fullFileName);
CroP = imcrop(imageArray,Cells);
imshow(CroP);
B = imresize(CroP,[256 256]);
imshow(B);
imwrite(B,strcat('C:\Users\USER\Desktop\Directory\imagefile_00',num2str(i),'.png'));
end
end
My question is that after loop i runs, it saves 4 images and for img again it saves four values. Now when the code runs it saves only last 4 images and not 8. I should get a i*N number of total images, but I'm getting only 4 and rest are overwritten.
How can I adapt my program to save all files?
imwrite(B,strcat('C:\Users\USER\Desktop\Directory\imagefile_00',num2str(i),'.png'));
is where the problem is. You use num2str(i) to change the number, and i=[1,2,3,4]. Thus, you cannot create a file which is outside numbers 1 to 4. Taking you want something based on img as well, you can use e.g.
imwrite(B,strcat('C:\Users\USER\Desktop\Directory\imagefile_',num2str(img),num2str(i),'.png'));
which will create a file named imagefile_11 for the first image and first region, imagefile_12 for the first image, second region, imagefile_324 for the 32nd image, fourth region etc. Change according to your needs of course.
Example in action:
>> i=3;img=1;
>> strcat('C:\Users\USER\Desktop\Directory\imagefile_',num2str(img),num2str(i),'.png')
ans =
C:\Users\USER\Desktop\Directory\imagefile_13.png
>> i=1;img=2;
>> strcat('C:\Users\USER\Desktop\Directory\imagefile_',num2str(img),num2str(i),'.png')
ans =
C:\Users\USER\Desktop\Directory\imagefile_21.png
Free advice:
i and j are the imaginary unit. It's, in my opinion, preferable not to use them as loop indices.
Your desktop is most likely not the best place to save stuff. Make a folder in for example your Documents folder with an apt name, e.g. C:\Users\USER\Documents\ROIfrom4k\
The declarations of x and Cells would benefit from a closing semicolon on the line, so as to prevent them from outputting to the console, slowing down the program and clogging the command window. The orange wiggles MATLAB puts there are not for festivity, they present a useful warning. (Not to be confused with red wiggles, those present an serious error, because of which MATLAB cannot run at all.)

Good use of memory

If i create a cell with 1000 matrices ( size of each matrix 800*1280), clearing each matrix after using it will speed up calculations ?
Example
A=cell(1000,1);
for i=1:1000
A{i}=rand(800,1280);
end
image=A{1};
image2=A{2}; % I will use image and image2 with other functions
A{1}=[];
A{2}=[];
EDIT
The real use of the cell will be like :
A=cell(1000,1);
parfor i=1:1000
A{i}=function_that_creates_image(800,1280); % image with size 800*1280 px
end
for i=1:number_of_images % number_of_images=1000 in this case
image1=A{1};
image2=A{2};
A{1}=[];
A{2}=[];
% image1 and image 2 will be used then in the next lines
%next lines of code
end
I noticed that calculating components of A in a parfor loop is faster than calculating each component for each loop inside the for
If you want to use less memory and speed up calculations, it's wiser to avoid using cells. Luckily, it's very easy in your case, since all your matrices are the same size, so you could use an ND-array.
A = zeros(800,1280,1000);
for k = 1:size(A,3)
A(:,:,k) = function_that_creates_image(800,1280);
end
image = A(:,:,1);
image2 = A(:,:,2); % I will use image and image2 with other functions
EDIT:
If you want to further process each image, I would save them to a file within the parfor, so you will have 1000 .mat files at the end of the first loop:
parfor k = 1:number_of_images
A = function_that_creates_image(800,1280);
save(['images_dir\image' num2str(k) '.mat'],'A');
end
then you can load them as needed for processing using load:
for k = 1:number_of_images-1
image1 = load(['images_dir\image' num2str(k) '.mat']);
image2 = load(['images_dir\image' num2str(k+1) '.mat'];
% do what you want with those images...
end
This way you only keep 2 images in memory each time, and on the next iteration they a replaced by the next images.
If you fit everything in memory (you need at least 16GB to hold data and do some work on parts of it, to work on the full beast at the same time you should be having 32GB), clearing these won't change anything at all. If you don't, I would assume/hope Matlab and Windows are smart enough to optimize which chunk is held in memory and which is put on the disk, so again deleting won't help. But you might not want to rely on that.
What you can do is to have A{i} = 'path-to-file';, then load it in memory just for the time when needed. Why do you even need to first load all the images and then do work on them one by one? It would be much better for memory to simply have image1 = rand(...);, image2 = rand(...); in the loop itself, and reuse these image1 and image2. No need to even have this A.
In general, tall arrays are your memory friendly solution you should be using if you want to have tons of data at the same time. https://www.mathworks.com/help/matlab/tall-arrays.html

Binarization of an image with desired threshold

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);

Interact with very large Tiff (or rset) images in MATLAB

I have some very large Tiff images that I am trying to use in a MATLAB GUI application. If I try to load the images using imshow, I get an out-of-memory error. (Yes, I know MATLAB is not the best choice for GUIs or loading large images, but there is good reason for using MATLAB in this case).
I can obviously create a reduced resolution data set (rset file) and use imtool to view the image, but this is not helpful as I want a user to be able to interact with the image by clicking on it to extract (x,y) coords into the application. Imshow does not seem to be directly compatible with rset files. Is there a way for me to load an rset'd image in a panable/zoomable figure, or any other way I can achieve the goal?
I looked at the code for imtool but it seems to be using undocumented classes to read rset files and I can't replicate its behaviour.
You can use low-level file I/O functions of MATLAB to read the entirety or parts of the TIFF image in order to avoid the OOM problem.
fileName = 'LargeTiff.tif';
info = imfinfo(fileName)
% Determine number of frames
nFramesStr= regexp(info.ImageDescription, 'images=(\d*)', 'tokens');
nFrames = str2double(nFramesStr{1}{1});
% Use low-level File I/O functions to read the file
fp = fopen(fileName , 'rb');
% The "StripOffsets" field provides the offset to the first strip.
fseek(fp, info.StripOffsets, 'bof');
% Assume that the image format is 16-bit per pixel and is big-endian
% Also assume that the images are stored one after the other
% For example, read the first 100 frames
frameNum = 100;
imData = cell(1, frameNum);
for cnt = 1 : frameNum
imData{cnt} = fread(fp, [info.Width info.Height], 'uint16', 0, 'ieee-be');
end
fclose(fp);
It looks like my problem is that I simply don't have enough memory to load the whole tiff, and that there is no public specification for the rset files format. So I am going to instead solve the problem by creating my own version of a reduced resolution data set. I should be able to load block sections of the image, resave them and then dynamically load and unload only the needed high-res blocks at zoom, and load a reduced resolution overview when zoomed out.
You can write a callback function to get the pixel co-ordinates (X,Y) from imtool then convert to a tile number and tile index using the code below. You can then utilise the readencodedtile function in matlab
function [tileidx,Tile_num] = getTileInfo(tiffile,X,Y)
A = Tiff(tiffile);
tile_width = A.getTag('TileWidth');
tile_length = A.getTag('TileLength');
SizeA = size(A);
tt = sub2ind(SizeA,X,Y);
% Example only
% X = repmat((1:10)',1,10);
% Y = repmat((1:10),10,1);
% A = reshape(1:100,10,10);
% SizeA = size(A);
% tile_width = 3;
% tile_length = 2;
tileidx = rem(tt-(Y-1)*SizeA(1)-1,tile_length)+1 ...
+ tile_length*rem(Y+tile_width-1,tile_width);
Tile_num = ceil(Y/tile_width)+ ...
(ceil(X/tile_length)-1)*ceil(SizeA(2)/tile_width);

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.