MATLAB Array Iteration & Fieldname - matlab

I'm really struggling to do what I want with this so any help will be greatly appreciated.
I'm looping through an array X number of times, displaying an array of images in a randomised manner. What I want to do is retrieve the name of each image that is being currently displayed in the random order, and the array it belongs to, as each image is displayed in the loop, and store the two in separate variables.
images.blue = imread('blue.bmp');
images.red = imread('red.bmp');
images.green = imread('green.bmp');
images.yellow = imread('yellow.bmp');
colours = {images.blue, images.red, images.green, images.yellow}
cata = {images.blue, images.red}
catb = {images.green, images.yellow}
So for example if images.blue is being displayed on the screen while the loop is iterating through the array, I want the name blue to be saved in variable CurrentFieldname.
for count = 1;
names = (fieldnames(images));
while count <= 5
for n = randperm(length(colours));
d =(colours{n});
imshow(d);
pause(1)
CurrentFieldName = (names {n});
end
count = count+1;
end
break
end
What happens with the above code is that after all the images have been displayed, every iteration up till the condition is satisfied, the field name of the last displayed image is stored in CurrentFieldname.
The above is not what I want. After every time an image is displayed in the iterating loop, I want the field CurrentFieldname, to contain the name of the image that is being displayed. Then, during each interation through the loop, I would like to compare the CurrentFieldName with cata and catb, to see which array CurrentFieldName belongs to. Then, record this into a separate variable, CurrCat. E.g.
if CurrentFieldname == cata
CurrCat = a;
I would just like to have both of these variables, CurrentFieldName and CurrCat, contain the relevant information at the end of every iteration. Then they would both be overwritten by the information corresponding to the next image that is randomly displayed and so forth.
I hope this all makes sense.
Thanks

Lets try:
colors = {'red', 'green', 'blue', 'yellow' }; % existing colors, assuming for each color there is a .bmp image
NC = numel( colors ); % number of colors
% read the images once
for ci=1:NC
img.(colors{ci}) = imread( [colors{ci}, '.bmp' ] );
end
NumIters = 1; % number of iterations over all colors
% reapeat as many times
for itr = 1:NumIters
ord = randperm( NC ); % permutation for this iteration
for ci = ord(:)', %'
CurrentFieldName = colors{ci};
imshow( img.(colors{ci}) ); % display the current color image
title( colors{ci} );
tic;
% your code here processing img.(colors{ci})
Response = myFun( img.(colors{ci}) );
ResponseTime = toc; % record timing of processing
save( sprintf('data_for_itr%03d_color%02d.mat', itr, ci ),...
'CurrentFieldName', 'Response', 'ResponseTime' );
end
end

Related

Reading Irregular Text Files with MATLAB

In short, I'm having a headache in multiple languages to read a txt file (linked below). My most familiar language is MATLAB so for that reason I'm using that in this example. I've found a way to read this file in ~ 5 minutes, but given I'll have tons and tons of data from my instrument shortly as it measures all day every 30 seconds this just isn't feasible.
I'm looking for a way to quickly read these irregular text files so that going forward I can knock these out with less of a time burden.
You can find my exact data at this link:
http://lb3.pandonia.net/BostonMA/Pandora107s1/L0/Pandora107s1_BostonMA_20190814_L0.txt.bz2
I've been using the "readtable" function in matlab and I have achieved a final product I want but I'm looking to increase the speed
Below is my code!
clearvars -except pan day1; % Clearing all variables except for the day and instrument variables.
close all;
clc;
pan_mat = [107 139 155 153]; % Matrix of pandora numbers for file-choosing
reasons.
pan = pan_mat(pan); % pandora number I'm choosing
pan = num2str(pan); % Turning Pandora number into a string.
%pan = '107'
pandora = strcat('C:\Users\tadams15\Desktop\Folders\Counts\Pandora_Dta\',pan)
% string that designates file location
%date = '90919'
month = '09'; % Month
day2 = strcat('0',num2str(day1)) % Creating a day name for the figure I ultimately produce
cd(pandora)
d2 = strcat('2019',num2str(month),num2str(day2)); % The final date variable
for the figure I produce
%file_pan = 'Pandora107s1_BostonMA_20190909_L0';
file_pan = strcat('Pandora',pan,'s1_BostonMA_',d2,'_L0'); % File name string
%Try reading it in line by line?
% Load in as a string and then convert the lines you want as numbers into
% number.
delimiterIn = '\t';
headerlinesIn = 41;
A = readtable(file_pan,'HeaderLines', 41, 'Delimiter', '\t'); %Reading the
file as a table
A = table2cell(A); % Converting file to a cell
A = regexp(A, ' ', 'split'); % converting cell to a structure matrix.
%%
A= array2table(A); % Converting Structure matrix back to table
row_num = 0;
pan_mat_2 = zeros(2359,4126);
datetime_mat = zeros(2359,2);
blank = 0;
%% Converting data to proper matrices
[length width] = size(A);
% The matrix below is going through "A" and writing from it to a new
% matrix, "pan_mat_2" which is my final product as well as singling out the
% rows that contain non-number variables I'd like to keep and adding them
% later.
tic
%flag1
for i = 1:length; % Make second number the length of the table, A
blank = 0;
b = table2array(A{i,1});
[rows, columns] = size(b);
if columns > 4120 && columns < 4140
row_num = row_num + 1;
blank = regexp(b(2), 'T', 'split');
blank2 = regexp(blank{1,1}(2), 'Z', 'split');
datetime_mat(row_num,1) = str2double(blank{1,1}(1));
datetime_mat(row_num,2) = str2double(blank2{1,1}(1));
for j = 1:4126;
pan_mat_2(row_num,j) = str2double(b(j));
end
end
end
toc
%flag2
In short, I'm already getting the result I want but the part of the code where I'm writing to a new array "flag 1" to "flag 2" is taking roughly 222 seconds while the entire code only takes about 248 seconds. I'd like to find a better way to create the data there than to write it to a new array and take a whole bunch of time.
Any suggestions?
Note:
There are a quite a few improvments you can make for speed but there are also corrections. You preallocate you final output variable with hard coded values:
pan_mat_2 = zeros(2359,4126);
But later you populate it in a loop which run for i = 1:length.
length is the full number of lines picked from the file. In your example file there are only 784 lines. So even if all your line were valid (ok to be parsed), you would only ever fill the first 784 lines of the total 2359 lines you allocated in your pan_mat_2. In practice, this file has only 400 valid data lines, so your pan_mat_2 could definitely be smaller.
I know you couldn't know you had only 400 line parsed before you parsed them, but you knew from the beginning that you had only 784 line to parse (you had the info in the variable length). So in case like these pre-allocate to 784 and only later discard the empty lines.
Fortunately, the solution I propose does not need to pre-allocate larger then discard. The matrices will end up the right size from the start.
The code:
%%
file_pan = 'Pandora107s1_BostonMA_20190814_L0.txt' ;
delimiterIn = '\t';
headerlinesIn = 41;
A = readtable(file_pan,'HeaderLines', 41, 'Delimiter', '\t'); %Reading the file as a table
A = table2cell(A); % Converting file to a cell
A = regexp(A, ' ', 'split'); % converting cell to a structure matrix.
%% Remove lines which won't be parsed
% Count the number of elements in each line
nelem = cell2mat( cellfun( #size , A ,'UniformOutput',0) ) ;
nelem(:,1) = [] ;
% find which lines does not have enough elements to be parsed
idxLine2Remove = ~(nelem > 4120 & nelem < 4140) ;
% remove them from the data set
A(idxLine2Remove) = [] ;
%% Remove nesting in cell array
nLinesToParse = size(A,1) ;
A = reshape( [A{:}] , [], nLinesToParse ).' ;
% now you have a cell array of size [400x4126] cells
%% Now separate the columns with different data type
% Column 1 => [String] identifier
% Column 2 => Timestamp
% Column 3 to 4125 => Numeric values
% Column 4126 => empty cell created during the 'split' operation above
% because of a trailing space character.
LineIDs = A(:,1) ;
TimeStamps = A(:,2) ;
Data = A(:,3:end-1) ; % fetch to "end-1" to discard last empty column
%% now extract the values
% You could do that directly:
% pan_mat = str2double(Data) ;
% but this takes a long time. A much computationnaly faster way (even if it
% uses more complex code) would be:
dat = strjoin(Data) ; % create a single long string made of all the strings in all the cells
nums = textscan( dat , '%f' , Inf ) ; % call textscan on it (way faster than str2double() )
pan_mat = reshape( cell2mat( nums ) , nLinesToParse ,[] ) ; % reshape to original dimensions
%% timestamps
% convert to character array
strTimeStamps = char(TimeStamps) ;
% convert to matlab own datetime numbering. This will be a lot faster if
% you have operations to do on the time stamps later
ts = datenum(strTimeStamps,'yyyymmddTHHMMSSZ') ;
%% If you really want them the way you had it in your example
strTimeStamps(:,9) = ' ' ; % replace 'T' with ' '
strTimeStamps(:,end) = ' ' ; % replace 'Z' characters with ' '
%then same again, merge into a long string, parse then reshape accordingly
strdate = reshape(strTimeStamps.',1,[]) ;
tmp = textscan( strdate , '%d' , Inf ) ;
datetime_mat = reshape( double(cell2mat(tmp)),2,[]).' ;
The performance:
As you can see on my machine your original code takes ~102 seconds to execute, with 80% of that (81s) spent on calling the function str2double() 3,302,400 times!
My solution, run on the same input file, takes ~5.5 seconds, with half of the time spent on calling strjoin() 3 times.
When you read the code above, try to understand how I limited the repetition of function call in lengthy loops by trying to keep everything as vectorised as possible.
Using the profiler, you can see that you call str2double 3302400 times in a run which takes about 80% of the total time on my pc. Now thats suboptimal, as each time you only translate 1 value and as far as your code goes you dont need the values as string again. I added this under you original code:
row_num = 0;
pan_mat_2_b = cell(2359,4126);
datetime_mat_b = cell(2359,2);%not zeros
blank = 0;
tic
%flag1
for i = 1:length % Make second number the length of the table, A
blank = 0;
b = table2array(A{i,1});
[rows, columns] = size(b);
if columns > 4120 && columns < 4140
row_num = row_num + 1;
blank = regexp(b(2), 'T', 'split');
blank2 = regexp(blank{1,1}(2), 'Z', 'split');
%datetime_mat(row_num,1) = str2double(blank{1,1}(1));
%datetime_mat(row_num,2) = str2double(blank2{1,1}(1));
datetime_mat_b(row_num,1) = blank{1,1}(1);
datetime_mat_b(row_num,2) = blank2{1,1}(1);
pan_mat_2_b(row_num,:) = b;
% for j = 1:4126
% pan_mat_2(row_num,j) = str2double(b(j));
% end
end
end
datetime_mat_b = datetime_mat_b(~all(cellfun('isempty',datetime_mat_b),2),:);
pan_mat_2_b=pan_mat_2_b(~all(cellfun('isempty',pan_mat_2_b),2),:);
datetime_mat_b=str2double(string(datetime_mat_b));
pan_mat_2_b=str2double(pan_mat_2_b);
toc
Still not great, but better. If you want to speed this up further i recommend you take a closer look at the readtable part. As you can save up quite some time if you start with reading in the format as doubles right from the beginning

how to sum the number of objects in different images in Matlab?

this is my entire code. What i did here is to try to find matching points between an image and a template i substracted from the image.
The first loop and second loop extract all the card from the original image(first image with set of card)
The next part of the code is just finding the matching point between extracted card and the template.
The last for loop i created, is based on range of the matching points count I selected to discard cards that have less matching with the template.(selectCard)
Then I created the for loop to (selectCard) and I applied connected components to count the objects remaining on the card when They pass the selection criteria.
So I need to get the total number of object of all the card that pass the selection criteria I have made. Basically, I got a result of each card. Not all the card. I need to compute the result of All those cards.
for j=1:max(max(LabeledImage))
[row, col] = find(LabeledImage==j);
len=max(row)-min(row)+2;
breadth=max(col)-min(col)+2;
Img=uint8(zeros([len breadth 3] ));
sy=min(col)-1;
sx=min(row)-1;
for i=1:size(row,1)
x=row(i,1)-sx;
y=col(i,1)-sy;
Img(x,y,:)=grayImage(row(i,1),col(i,1),:);
end
mytitle=strcat('Card Number:',num2str(j));
% figure,imshow(Img);title(mytitle);
Img=rgb2gray(Img);
pointsForSpade1 = detectHarrisFeatures(Img);
pointsForSpade2 = detectHarrisFeatures(Template_for_spade);
%extract neighborhood features
[featuresForSpade1,valid_pointsForSpade1] =
extractFeatures(Img,pointsForSpade1);
[featuresForSpade2,valid_pointsForSpade2] =
extractFeatures(Template_for_spade,pointsForSpade2);
%Match the features
indexPairs = matchFeatures(featuresForSpade1,featuresForSpade2);
%retrieve the locations of the corresponding points for each image.
matchedPointsForSpade1 = valid_pointsForSpade1(indexPairs(:,1),:);
matchedPointsForSpade2 = valid_pointsForSpade2(indexPairs(:,2),:);
% visualize the corresponding points.
figure,subplot(5,5,j)
showMatchedFeatures(Img,Template_for_spade,matchedPointsForSpade1,.....
matchedPointsForSpade2, 'montage');
count1 = matchedPointsForSpade1.Count;
%disp(matchedPoints1);
selectCard4 = find(count1>10);
Total = 0;
for e4= 1: selectCard4
figure, subplot(5,5,j)
showMatchedFeatures(Img(e4),Template_for_spade,matchedPointsForSpade1,...
matchedPointsForSpade2, 'montage');
title(Name);
% eliminate not needed hole on the card.
level = 0.57;
Img2 = imbinarize(Img, level);
%Morphological operation
ImgComp = imcomplement(Img2);
se = strel('disk', 10);
Iopened = imopen(ImgComp, se);
%figure;
%imshow(Iopened);
[Label3, numObject4] = bwlabel(Iopened);
TotalSpade = sum(numObject4);
s = sprintf('\n card number: of this set has #%s spades',j,
num2str(TotalSpade));
disp(s);
end
end
I want to display the sum of connected objects based on selected card.
Thank you.
There are three main problems here. First, you initialize Total = 0; and then change the variable name to TotalSpade. Change the initialization to:
TotalSpade = 0;
Second, the assignment to TotalSpade is overwriting the previous value. In order to accumulate the total, you need to add to TotalSpade instead. Also, numObject4 is a scalar, so sum doesn't do anything.
TotalSpade = TotalSpade + numObject4;
Finally, if you only want to print the total of the objects, you need to take your print statement outside the loop. You can also use fprintf instead of sprintf+disp since fprintf prints to the console if you don't specify a file descriptor.
for e4 = 1:selectCard4
...
end
fprintf('\n card number: of this set has #%s spades', j,
num2str(TotalSpade));

how do i mask labeled object based on some specified threshold value for each objects area,majoraxis and minoraxis?

i am currently using bwconnomp to label each connected object and regionpropsto find area, majoraxis, minoraxis of each labeled object respectively. i am also displaying each labeled object its area,majoraxis and minoraxis. now i want to set some threshold for area,majoraxis and minoraxis and if the value of area,majoraxis and minoraxis is above specified threshold then that object has to be masked.how this can be done??
here is my code
clc
clear all
close all
Index = 1;
scrsz = get(0,'ScreenSize');
%read an image
while Index ~= 0
% Open a dialog and select an image file
[FileName,FilePath,Index] = uigetfile('*.png', 'Open Imagefile ');
if Index == 0
disp('Procedure Done')
break;
end
inimage = imread([num2str(FilePath) FileName]);
D=inimage;
A=inimage;
subplot(2,3,1);
imshow(inimage);
title('original image');
%labeling algorithm
B=im2bw(inimage);
C=imfill(B,'holes');
label=bwlabel(C);
max(max(label))
CC = bwconncomp(B);
data = regionprops(CC,'all');
for j=1:max(max(label))
[row, col] = find(label==j);
len=max(row)-min(row)+2;
breadth=max(col)-min(col)+2;
target=uint8(zeros([len breadth] ));
sy=min(col)-1;
sx=min(row)-1;
for i=1:size(row,1)
x=row(i,1)-sx;
y=col(i,1)-sy;
target(x,y)=A(row(i,1),col(i,1));
end
mytitle=strcat('Object Number:' ,num2str(j),'area:', num2str(data(j).Area),'MajorAxis: ',num2str(data(j).MajorAxisLength),'MinorAxis: ',num2str(data(j).MinorAxisLength));
figure,imshow(target);title(mytitle);
a=size(target);
ax=a(1);
ay=a(2);
pos=[1,1,ay,ax];
rectangle('Position',pos,'EdgeColo','r')
end
next = input('next image? press Enter: ');
if next == 0
channelactivity = 0;
break
else
close all
disp('==================================')
pause(0.2)
continue
end
end
Here is a way to do it. The code is commented so easy to follow; the important line is the following:
AboveAreaIndices = find(vertcat(data.Area) > SomeValue)
In which you store the indices of the objects whose area is larger than SomeValue. In the example I color them red but you can do whatever you want with them or remove them altogether from the data structure.
You can also use logical operators to combine multiple conditions for example using the MinorAxis and MajorAxis properties. Note that I used AllArea as anew variable to store the concatenated areas to make things clearer, but you can keep them as vertcat(data.Area).
AboveIndices = find(vertcat(data.Area) > SomeValue & vertcat(data. MinorAxis) > SomeValue & Bla bla bla...);
Whole code:
clear
clc
close all
%// Read and clean up sample image
A = imread('rice.png');
A = im2bw(A,.5);
A = bwareaopen(A,50);
CC = bwconncomp(A);
%// Same as you.
data = regionprops(CC,'all');
%// Concatenate all the areas into an array.
AllArea = vertcat(data.Area);
%//========================================
%//==== Apply threshold on area here \\====
AboveAreaIndices = find(AllArea > 150);
%// If you wish to remove the entries from the data structure
% data(AllArea>150) = [];
%//========================================
%// Same for centroids...for display purposes
AllCentroids = vertcat(data.Centroid);
%// Display original and thresholded objects. Use the indices calculated
%// above to "mask" large areas if you want
imshow(A);
hold on
scatter(AllCentroids(:,1),AllCentroids(:,2),40,'b','filled')
scatter(AllCentroids(AboveAreaIndices,1),AllCentroids(AboveAreaIndices,2),40,'r','filled')
And sample output:

Matlab get vector of specific pixels

I am pretty new to Matlab and encountered a problem when working with images.
I want to get a pixel that is in a specific colour (blue) in the following image:
image
My current code looks something like this:
function p = mark(image)
%// display image I in figure
imshow(image);
%// first detect all blue values higher 60
high_blue = find(image(:,:,3)>60);
%cross elements is needed as an array later on, have to initialize it with 0
cross_elements = 0;
%// in this iteration the marked values are reduced to the ones
%where the statement R+G < B+70 applies
for i = 1:length(high_blue)
%// my image has the size 1024*768, so to access the red/green/blue values
%// i have to call the i-th, i+1024*768-th or i+1024*768*2-th position of the "array"
if ((image(high_blue(i))+image(high_blue(i)+768*1024))<...
image(high_blue(i)+2*768*1024)+70)
%add it to the array
cross_elements(end+1) = high_blue(i);
end
end
%// delete the zero element, it was only needed as a filler
cross_elements = cross_elements(cross_elements~=0);
high_vector = zeros(length(cross_elements),2);
for i = 1:length(cross_elements)
high_vector(i,1) = ceil(cross_elements(i)/768);
high_vector(i,2) = mod(cross_elements(i), 768);
end
black = zeros(768 ,1024);
for i = 1:length(high_vector)
black(high_vector(i,2), high_vector(i,1)) = 1;
end
cc = bwconncomp(black);
a = regionprops(cc, 'Centroid');
p = cat(1, a.Centroid);
%// considering the detection of the crosses:
%// RGB with B>100, R+G < 100 for B<150
%// consider detection in HSV?
%// close the figure
%// find(I(:,:,3)>150)
close;
end
but it is not optimized for Matlab, obviously.
So i was wondering if there was a way to search for pixels with specific values,
where the blue value is larger than 60 (not hard with the find command,
but at the same time the values in the red and green area not too high.
Is there a command I am missing?
Since English isn't my native language, it might even help if you gave me some suitable keywords for googling ;)
Thanks in advance
Based on your question at the end of the code, you could get what you want in a single line:
NewImage = OldImage(:,:,1) < SomeValue & OldImage(:,:,2) < SomeValue & OldImage(:,:,3) > 60;
imshow(NewImage);
for example, where as you see you provide a restriction for each channel using logical operators, that you can customize of course (eg. using | as logical OR). Is this what you are looking for? According to your code you seem to be looking for specific regions in the image like crosses or coins is that the case? Please provide more details if the code I gave you is completely off the track :)
Simple example:
A = imread('peppers.png');
B = A(:,:,3)>60 & A(:,:,2)<150 & A(:,:,1) < 100;
figure;
subplot(1,2,1);
imshow(A);
subplot(1,2,2)
imshow(B);
Giving this:

Rolling window for averaging using MATLAB

I have the following code, pasted below. I would like to change it to only average the 10 most recently filtered images and not the entire group of filtered images. The line I think I need to change is: Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;, but how do I do it?
j=1;
K = 1:3600;
window = zeros(1,10);
Yout = zeros(10,column,row);
figure;
y = 0; %# Preallocate memory for output
%Load one image
for i = 1:length(K)
disp(i)
str = int2str(i);
str1 = strcat(str,'.mat');
load(str1);
D{i}(:,:) = A(:,:);
%Go through the columns and rows
for p = 1:column
for q = 1:row
if(mean2(D{i}(p,q))==0)
x = 0;
else
if(i == 1)
meanvalue = mean2(D{i}(p,q));
end
%Calculate the temporal mean value based on previous ones.
meanvalue = (meanvalue+D{i}(p,q))/2;
x = double(D{i}(p,q)/meanvalue);
end
%Filtering for 10 bands, based on the previous state
for k = 1:10
[y, ZState{k}] = filter(bCoeff{k},aCoeff{k},x,ZState{k});
Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;
end
end
end
% for k = 2:10
% subplot(5,2,k)
% subimage(Yout(k)*5000, [0 100]);
% colormap jet
% end
% pause(0.01);
end
disp('Done Loading...')
The best way to do this (in my opinion) would be to use a circular-buffer to store your images. In a circular-, or ring-buffer, the oldest data element in the array is overwritten by the newest element pushed in to the array. The basics of making such a structure are described in the short Mathworks video Implementing a simple circular buffer.
For each iteration of you main loop that deals with a single image, just load a new image into the circular-buffer and then use MATLAB's built in mean function to take the average efficiently.
If you need to apply a window function to the data, then make a temporary copy of the frames multiplied by the window function and take the average of the copy at each iteration of the loop.
The line
Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;
calculates a kind of Moving Average for each of the 10 bands over all your images.
This line calculates a moving average of meanvalue over your images:
meanvalue=(meanvalue+D{i}(p,q))/2;
For both you will want to add a buffer structure that keeps only the last 10 images.
To simplify it, you can also just keep all in memory. Here is an example for Yout:
Change this line: (Add one dimension)
Yout = zeros(3600,10,column,row);
And change this:
for q = 1:row
[...]
%filtering for 10 bands, based on the previous state
for k = 1:10
[y, ZState{k}] = filter(bCoeff{k},aCoeff{k},x,ZState{k});
Yout(i,k,p,q) = y.^2;
end
YoutAvg = zeros(10,column,row);
start = max(0, i-10+1);
for avgImg = start:i
YoutAvg(k,p,q) = (YoutAvg(k,p,q) + Yout(avgImg,k,p,q))/2;
end
end
Then to display use
subimage(Yout(k)*5000, [0 100]);
You would do sth. similar for meanvalue