Run Length Smoothing Algorithm(RLSA) in MATLAB - algorithm

I have following RLSA code in matlab.The jist of this algorithm is that it tries to connect area with specified spacing(threshold) for example in identifying text areas(As text has mostly fixed specific spacing most of the times on images) it tries to join them so that when some morphological operations are performed they do well in identifying those areas.
1.function result=RLSA(image,hor_thresh)
2. zeros_count=0;
3. one_flag=0;
4. hor_image=image;
5. [m,n]=size(image);
6. for i=1:m
7. for j=1:n
8. if(image(i,j)==1)
9. if(one_flag==1)
10. if(zeros_count<=hor_thresh)
11. hor_image(i,j-zeros_count:j-1)=1;
12. else
13. one_flag=0;
14. end
15. zeros_count=0;
16. end
17. one_flag=1;
18. else
19. if(one_flag==1)
20. zeros_count=zeros_count+1;
21. end
22. end
23. end
24. end
25. result= hor_image;
26. end
The above MATLAB code was taken from the following sites
answers.opencv.org
Attempt to implementation Running Length Smoothing Algorithm in C++
More detailed description is here
Working of RLSA
The above code does not work for all threshold values.For example on putting threshold value=20 I got following error
Subscript indices must either be real positive integers or logicals.
Error in RLSA (line 11)
hor_image(i,j-zeros_count:j-1)=1;
Can somebody explain me the working of algorithm on images with text and as to why this code doesn't work for all of the threshold values?
Moreover,Do I need to take two pass(as described in the links given) of this code one for horizontal and one for vertical and logically and them or the mentioned code has already taken care of it?

Well it all depends on the image you pass in along with the Hor_Thresh. I would guess that the problem is that j-zeros_count can be negative. That would be possible if one column of an image has a 1, but within 20 pixels of another column, that has a 1. Then the range that is being set to 1 will span 2 columns.
I don't really know what the algorithm is trying to do, but it appears to be dealing with binary images (but not explicitly). It is setting spans of zeros (book-ended by ones) that count < Hor_Threshold, to all be 1. So if there is "short" runs of zeros, they will be set to 1.
Guessing from the name of the parameter, I would expect to see some reset code between the 2 for loops. The Hor_Threshold implies Horizontal threshold, i.e. it gets reset each and every column.
So between the i and j loop, I think you need to reset your 2 flags:
zeros_count = 0;
one_flag = 0;
It has been a long time since I have used Matlab, I have been using a similar language, Igor Pro for a while, so I tested things out in Igor. This is the translated function:
Function/Wave RLSA(image,hor_thresh)
Wave Image
Variable Hor_Thresh
Variable zeros_count = 0;
Variable one_flag = 0;
Duplicate/FREE Image, hor_image
Variable m = DimSize(Image,0)
Variable n = DimSize(Image,1)
Variable i, J
for (i = 0;i < m;i += 1)
for (j = 0;j < n;j += 1)
if (image[i][j] == 1)
if (one_flag == 1)
if (zeros_count <= hor_thresh)
hor_image[i][j-zeros_count,j-1] = 1;
else
one_flag = 0;
endif
zeros_count = 0;
endif
one_flag = 1;
else
if (one_flag == 1)
zeros_count += 1;
endif
endif
endfor
endfor
return hor_image;
end //RLSA
Igor, like C++ is zero based indexing.
So I could get the same error with an image that I created:
Make/N=(24,24) Image
Image = 0
image[0][22] = 1
image[1][2] = 1
RLSA(image,20)
Throws the same error as you have.
So I have 2 ones in the image, that are separated by 3 zeros (< second argument), but are in different columns.

Related

Create a Program Displaying Number of Numbers Greater Than 0.8 in Matrix

New programmer here,
I got a task to make a program that picks and displays how many numbers greater than 0.8 there is in a random matrix 5x6 with 2 for loops and would appreciate help if someone knows how to make it.
Thanks in advance.
I'm writing in Octave,
this is how the code starts:
clear
clc
rand(5,6);
for row =….
Hopefully, this MATLAB code carries over to Octave. The first method uses two for-loops to scan/traverse through the matrix. If the value retrieved from the matrix during scanning Matrix(Row,Column) is greater than 0.8 then the variable Number is incremented. This process repeats until the whole matrix is checked. Numbers_Greater_Than is used to store all the numbers that are greater than 0.8.
Using For-Loops:
clear;
clc;
Matrix = rand(5,6);
Numbers_Greater_Than = [];
[Number_Of_Rows,Number_Of_Columns] = size(Matrix);
Number = 0;
for Row = 1: Number_Of_Rows
for Column = 1: Number_Of_Columns
if(Matrix(Row,Column) > 0.8)
Number = Number + 1;
Numbers_Greater_Than = [Numbers_Greater_Than Matrix(Row,Column)];
end
end
end
fprintf("The are %d numbers greater than 0.8 in the matrix\n",Number);
Numbers_Greater_Than
Extension:
Alternatively:
Scanning through the elements using single number indexing.
clear;
clc;
Matrix = rand(5,6);
Number = 0;
for Element = 1: numel(Matrix)
if(Matrix(Element) > 0.8)
Number = Number + 1;
end
end
fprintf("The are %d numbers greater than 0.8 in the matrix\n",Number);
Using a Logical Array:
This method creates a logical array based on the condition > 0.8. The Logical_Array is set to "1" when the condition is true and set to "0" when the condition is false. By taking the sum afterwards the number of times that the condition is true in the matrix can be counted.
clear;
clc;
Matrix = rand(5,6);
Logical_Array = Matrix > 0.8;
Number = sum(Logical_Array,'all');
fprintf("The are %d numbers greater than 0.8 in the matrix\n",Number);
Ran using MATLAB R2019b

Grouping data using loops (signal processing in MATLAB)

I am working in MATLAB with a signal data that consist of consecutive dips as shown below. I am trying to write a code which sorts the contents of each dip into a separate group. How should the general structure of such a code look like?
The following is my data. I am only interested in the portion of the signal that lies below a certain threshold d (the red line):
And here is the desired grouping:
Here is an unsuccessful attempt:
k=0; % Group number
for i = 1 : length(signal)
if signal(i) < d
k=k+1;
while signal(i) < d
NewSignal(i, k) = signal(i);
i = i + 1;
end
end
end
The code above generated 310 groups instead of the desired 12 groups.
Any explanation would be greatly appreciated.
Taking Benl generated data you can do the following:
%generate data
x=1:1000;
y=sin(x/20);
for ii=1:9
y=y+-10*exp(-(x-ii*100).^2./10);
end
y=awgn(y,4);
%set threshold
t=-4;
%threshold data
Y = char(double(y<t) + '0'); %// convert to string of zeros and ones
%search for start and ends
This idea is taken from here
[s, e] = regexp(Y, '1+', 'start', 'end');
%and now plot and see that each pair of starts and end
% represents a group
plot(x,y)
hold on
for k=1:numel(s)
line(s(k)*ones(2,1),ylim,'Color','k','LineStyle','--')
line(e(k)*ones(2,1),ylim,'Color','k','LineStyle','-')
end
hold off
legend('Data','Starts','Ends')
Comments: First of all I choose an arbitrary threshold, it is up to you to find the "best" one in your data. Additionally I didn't group the data explicitly but rather this approach gives you the start and end of each epoch with a dip (you might call it group). So you could say that each index is the grouping index. Finally I did not debug this approach for corner cases, when dips fall on starts and ends...
In MATLAB you cannot change the loop index of a for loop. A for loop:
for i = array
loops over each column of array in turn. In your code, 1 : length(signal) is an array, each of its elements is visited in turn. Inside this loop there is a while loop that increments i. However, when this while loop ends and the next iteration of the for loop runs, i is reset to the next item in the array.
This code therefore needs two while loops:
i = 1; % Index
k = 0; % Group number
while i <= numel(signal)
if signal(i) < d
k = k + 1;
while signal(i) < d
NewSignal(i,k) = signal(i);
i = i + 1;
end
end
i = i + 1;
end
Easy, the function you're looking for is bwlabel, which when combined with logical indexing makes this simple.
To start I made some fake data which resembled your data
x=1:1000;
y=sin(x/20);
for ii=1:9
y=y+-10*exp(-(x-ii*100).^2./10);
end
y=awgn(y,4);
plot(x,y)
Then set your threshold and use 'bwlabel'
d=-4;% set the threshold
groupid=bwlabel(y<d);
bwlabel labels connected groups in a black and white image, what we've effectively done here is make a black and white (logical 0 & 1) 1D image in the logical vector y<d. bwlabel returns the number of the region at the index of the region. We're not interested in the 0 region, so to get the x values or y values of the nth region, simply use x(groupid==n), for example with my test data
x_4=x(groupid==4)
y_4=y(groupid==4)
x_4 = 398 399 400 401 402
y_4 = -5.5601 -7.8280 -9.1965 -7.9083 -5.8751

How can I avoid if else statements within a for loop?

I have a code that yields a solution similar to the desired output, and I don't know how to perfect this.
The code is as follows.
N = 4; % sampling period
for nB = -30:-1;
if rem(nB,N)==0
xnB(abs(nB)) = -(cos(.1*pi*nB)-(4*sin(.2*pi*nB)));
else
xnB(abs(nB)) = 0;
end
end
for nC = 1:30;
if rem(nC,N)==0
xnC(nC) = cos(.1*pi*nC)-(4*sin(.2*pi*nC));
else
xnC(nC) = 0;
end
end
nB = -30:-1;
nC = 1:30;
nD = 0;
xnD = 0;
plot(nA,xnA,nB,xnB,'r--o',nC,xnC,'r--o',nD,xnD,'r--o')
This produces something that is close, but not close enough for proper data recovery.
I have tried using an index that has the same length but simply starts at 1 but the output was even worse than this, though if that is a viable option please explain thoroughly, how it should be done.
I have tried running this in a single for-loop with one if-statement but there is a problem when the counter passes zero. What is a way around this that would allow me to avoid using two for-loops? (I'm fairly confident that, solving this issue would increase the accuracy of my output enough to successfully recover the signal.)
EDIT/CLARIFICATION/ADD - 1
I do in fact want to evaluate the signal at the index of zero. The if-statement cannot handle an index of zero which is an index that I'd prefer not to skip.
The goal of this code is to be able to sample a signal, and then I will build a code that will put it through a recovery filter.
EDIT/UPDATE - 2
nA = -30:.1:30; % n values for original function
xnA = cos(.1*pi*nA)-(4*sin(.2*pi*nA)); % original function
N = 4; % sampling period
n = -30:30;
xn = zeros(size(n));
xn(rem(n,N)==0) = -(cos(.1*pi*n)-(4*sin(.2*pi*n)));
plot(nA,xnA,n,xn,'r--o')
title('Original seq. x and Sampled seq. xp')
xlabel('n')
ylabel('x(n) and xp(n)')
legend('original','sampled');
This threw an error at the line xn(rem(n,N)==0) = -(cos(.1*pi*n)-(4*sin(.2*pi*n))); which read: In an assignment A(I) = B, the number of elements in B and I must be the same. I have ran into this error before, but my previous encounters were usually the result of faulty looping. Could someone point out why it isn't working this time?
EDIT/Clarification - 3
N = 4; % sampling period
for nB = -30:30;
if rem(nB,N)==0
xnB(abs(nB)) = -(cos(.1*pi*nB)-(4*sin(.2*pi*nB)));
else
xnB(abs(nB)) = 0;
end
end
The error message resulting is as follows: Attempted to access xnB(0); index must be a positive integer or logical.
EDIT/SUCCESS - 4
After taking another look at the answers posted, I realized that the negative sign in front of the cos function wasn't supposed to be in the original coding.
You could do something like the following:
nB = -30:1
nC = 1:30
xnB = zeros(size(nB));
remB = rem(nB,N)==0;
xnB(remB) = -cos(.1*pi*nB(remB))-(4*sin(.2*pi*nB(remB));
xnC = zeros(size(nC));
remC = rem(nC,N)==0;
xnC(remC) = cos(.1*pi*nC(remC))-(4*sin(.2*pi*nC(remC)));
This avoids the issue of having for-loops entirely. However, this would produce the exact same output as you had before, so I'm not sure that it would fix your initial problem...
EDIT for your most recent addition:
nB = -30:30;
xnB = zeros(size(nB));
remB = rem(nB,N)==0;
xnB(remB) = -(cos(.1*pi*nB(remB))-(4*sin(.2*pi*nB(remB)));
In your original post you had the sign dependent on the sign of nB - if you wanted to maintain this functionality, you would do the following:
xnB(remB) = sign(nB(remB).*(cos(.1*pi*nB(remB))-(4*sin(.2*pi*nB(remB)));
From what I understand, you want to iterate over all integer values in [-30, 30] excluding 0 using a single for loop. this can be easily done as:
for ii = [-30:-1,1:30]
%Your code
end
Resolution for edit - 2
As per your updated code, try replacing
xn(rem(n,N)==0) = -(cos(.1*pi*n)-(4*sin(.2*pi*n)));
with
xn(rem(n,N)==0) = -(cos(.1*pi*n(rem(n,N)==0))-(4*sin(.2*pi*n(rem(n,N)==0))));
This should fix the dimension mismatch.
Resolution for edit - 3
Try:
N = 4; % sampling period
for nB = -30:30;
if rem(nB,N)==0
xnB(nB-(-30)+1) = -(cos(.1*pi*nB)-(4*sin(.2*pi*nB)));
else
xnB(nB-(-30)+1) = 0;
end
end

Partition an image into 8 rows via Matlab, not all partitions shown

I wish to ask if anybody out there knows how to partition an image into 8 different rows and 1 column? I have tried using mat2cell() and using the demo on their wiki as a reference, I tried partitioning the image into 8 rows, however not all image partition rows are displayed.
If you see the image below, 2, 4, 6, 8 is not displayed. I am also not sure why is it of 16 blocks.
Can somebody help me check my code? I am not really used to the MatLab syntax and language. I trying my best to understand now.
My code for splitting the blocks are as follows:
blockSizeR = 50; % Rows in block.
blockSizeC = 512; % Columns in block.
wholeBlockRows = floor(rows / blockSizeR);
blockVectorR = [blockSizeR * ones(1, wholeBlockRows), rem(rows, blockSizeR)];
wholeBlockCols = floor(columns / blockSizeC);
blockVectorC = [blockSizeC * ones(1, wholeBlockCols), rem(columns, blockSizeC)];
if numberOfColorBands > 1
% It's a color image.
ca = mat2cell(rgbImage, blockVectorR, blockVectorC, numberOfColorBands);
else
ca = mat2cell(rgbImage, blockVectorR, blockVectorC);
end
% Now display all the blocks.
plotIndex = 1;
numPlotsR = size(ca, 1);
numPlotsC = size(ca, 2);
for r = 1 : numPlotsR
for c = 1 : numPlotsC
fprintf('plotindex = %d, c=%d, r=%d\n', plotIndex, c, r);
% Specify the location for display of the image.
subplot(numPlotsR, 1, plotIndex);
% Extract the numerical array out of the cell
% just for tutorial purposes.
rgbBlock = ca{r,c};
imshow(rgbBlock); % Could call imshow(ca{r,c}) if you wanted to.
[rowsB columnsB numberOfColorBandsB] = size(rgbBlock);
% Make the caption the block number.
caption = sprintf('Block #%d of %d\n%d rows by %d columns', ...
plotIndex, numPlotsR*numPlotsC, rowsB, columnsB);
title(caption);
drawnow;
% Increment the subplot to the next location.
plotIndex = plotIndex + 1;
end
end
I am new to MatLab, so is there is a simpler method to do this that I missed out, please do suggest or better still, if there are references that I can refer to. Many thanks (:
If you know the dimensions of your matrix, you can do the math to figure out how to divide the number of rows into 4 equal parts:
e.g. If: size(rockinsMatrix) == [10 20] (a 10row x 20column) matrix,
then you could split it into a set of 4 sub-matrices, two with 3 rows, and 2 with 2 columns.
If you want the matrices in a cell array then you can do that at that time.
I managed to solve already, the error lies in the for loop. I changed the for r = 1 : numPlotsR into r = 1 : (number of rows I want) for c = 1 : numPlotsC into c= 1: 1(as I only want one column), and used subplot(8,1,k) or (8,2,k) where k is the plot index. Just answering this in case anybody encounter such problem in future and want to use my code as a reference. Cheers!

Using non-continuous integers as identifiers in cells or structs in Matlab

I want to store some results in the following way:
Res.0 = magic(4); % or Res.baseCase = magic(4);
Res.2 = magic(5); % I would prefer to use integers on all other
Res.7 = magic(6); % elements than the first.
Res.2000 = 1:3;
I want to use numbers between 0 and 3000, but I will only use approx 100-300 of them. Is it possible to use 0 as an identifier, or will I have to use a minimum value of 1? (The numbers have meaning, so I would prefer if I don't need to change them). Can I use numbers as identifiers in structs?
I know I can do the following:
Res{(last number + 1)} = magic(4);
Res{2} = magic(5);
Res{7} = magic(6);
Res{2000} = 1:3;
And just remember that the last element is really the "number zero" element.
In this case I will create a bunch of empty cell elements [] in the non-populated positions. Does this cause a problem? I assume it will be best to assign the last element first, to avoid creating a growing cell, or does this not have an effect? Is this an efficient way of doing this?
Which will be most efficient, struct's or cell's? (If it's possible to use struct's, that is).
My main concern is computational efficiency.
Thanks!
Let's review your options:
Indexing into a cell arrays
MATLAB indices start from 1, not from 0. If you want to store your data in cell arrays, in the worst case, you could always use the subscript k + 1 to index into cell corresponding to the k-th identifier (k ≥ 0). In my opinion, using the last element as the "base case" is more confusing. So what you'll have is:
Res{1} = magic(4); %// Base case
Res{2} = magic(5); %// Corresponds to identifier 1
...
Res{k + 1} = ... %// Corresponds to indentifier k
Accessing fields in structures
Field names in structures are not allowed to begin with numbers, but they are allowed to contain them starting from the second character. Hence, you can build your structure like so:
Res.c0 = magic(4); %// Base case
Res.c1 = magic(5); %// Corresponds to identifier 1
Res.c2 = magic(6); %// Corresponds to identifier 2
%// And so on...
You can use dynamic field referencing to access any field, for instance:
k = 3;
kth_field = Res.(sprintf('c%d', k)); %// Access field k = 3 (i.e field 'c3')
I can't say which alternative seems more elegant, but I believe that indexing into a cell should be faster than dynamic field referencing (but you're welcome to check that out and prove me wrong).
As an alternative to EitanT's answer, it sounds like matlab's map containers are exactly what you need. They can deal with any type of key and the value may be a struct or cell.
EDIT:
In your case this will be:
k = {0,2,7,2000};
Res = {magic(4),magic(5),magic(6),1:3};
ResMap = containers.Map(k, Res)
ResMap(0)
ans =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
I agree with the idea in #wakjah 's comment. If you are concerned about the efficiency of your program it's better to change the interpretation of the problem. In my opinion there is definitely a way that you could priorotize your data. This prioritization could be according to the time you acquired them, or with respect to the inputs that they are calculated. If you set any kind of priority among them, you can sort them into an structure or cell (structure might be faster).
So
Priority (Your Current Index Meaning) Data
1 0 magic(4)
2 2 magic(5)
3 7 magic(6)
4 2000 1:3
Then:
% Initialize Result structure which is different than your Res.
Result(300).Data = 0; % 300 the maximum number of data
Result(300).idx = 0; % idx or anything that represent the meaning of your current index.
% Assigning
k = 1; % Priority index
Result(k).idx = 0; Result(k).Data = magic(4); k = k + 1;
Result(k).idx = 2; Result(k).Data = magic(5); k = k + 1;
Result(k).idx = 7; Result(k).Data = magic(6); k = k + 1;
...

Resources