Weird Behaviour of Parallel Processing when Using BlockProc
You are now following this question
- You will see updates in your followed content feed.
- You may receive emails, depending on your communication preferences.
An Error Occurred
Unable to complete the action because of changes made to the page. Reload the page to see its updated state.
Show older comments
0 votes
Share a link to this question
Hello, I a trying to speed up some analysis of an image (4700 v 128 pixels) that needs breaking up into smaller regions (20x128 pixels) and for each region having a calculation done such as the standard deviation (std2)
I thought the combination of Blockproc and the parallel toolbox would be ideal for this.
However, when I run my analysis, I sometmes get a fast completion time of 0.25s, but other times I run it it can take upto 3s.
1: When I start up my matlab session, i start a parallel pool:
% For parallel Processing (init parpool here)
delete(gcp('nocreate')); % but delete any that are already running
c=parcluster('local');
numwks=c.NumWorkers;
ReportMessage(app,['Parallel processing: NumWorkers = ',num2str(numwks)]);
parpool('Processes',numwks);
p=gcp;
conn=p.Connected;
ReportMessage(app,['...Connected ',num2str(conn)]);
2: I then run my fucntion from a push button callback.
My function first gets an image off a uiaxes, crops it and then performs the blockproc.
% Fast Analysis (SD)
tstart=tic;
ax=app.UIAxes;
IM=getimage(ax);
[sy,sx]=size(IM);
xl=5950; xr=10680; yb=0; yt=0; dx=xr-xl; % This is for image cropping
rect=[xl, yb, xr-xl,sy-yt-yb]; %[x, y, width, height]
IM2=imcrop(IM,rect);
IM=IM2-min(IM2(:)); % Background subtract
[sy,sx]=size(IM);
ColSize=20; % Number of pixels in each ROI - 20 default
nCols=floor(sx/ColSize);
RowSize = 128;
%BlockProc with Parallel
bss = [RowSize,ColSize]; % Each blockproc region
fh = @(bs) std2(bs.data); % perform this on each block
J = blockproc(IM, bss, fh,'UseParallel',true);
sd=J'; % Column vector
ReportMessage(app,num2str(toc(tstart)));
Is there something I am doing wrong? Do I need to close and reopen the parpool everytime or something.
Also, if I want the fh function to be a functio that has more than 1 argument, how do I do the syntax.
For example, I have a function (that works fine by itself)
Brenner=fmeasure(Image,'BREN',[]); %Focus metric
And I have tried to call it into Blockproc via:
fh = @(bs) fmeasure(bs.data,'BREN',[]);
It doesn't work properly.
1 Comment
"Also, if I want the fh function to be a functio that has more than 1 argument, how do I do the syntax."
If you mean this function:
then it works without error using the syntax you showed:
im = rand(128,4700);
s1 = blockproc(im,[128,20],@(bs)fmeasure(bs.data,'BREN',[]))
s1 = 1×235
0.1589 0.1560 0.1617 0.1616 0.1503 0.1561 0.1550 0.1609 0.1597 0.1552 0.1595 0.1543 0.1585 0.1518 0.1560 0.1548 0.1673 0.1553 0.1623 0.1634 0.1598 0.1545 0.1561 0.1593 0.1552 0.1567 0.1624 0.1546 0.1563 0.1514
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Accepted Answer
I doubt that your example is good use of the parallel toolbox. Parallel code can be slower than serial code due to high communication overhead, small task sizes, or data serialization. It may be inefficient when workers send data back to the client frequently or when the overhead of starting a parallel pool outweighs the computation time. And your example has a small task size.
"...analysis of an image (4700 v 128 pixels) that needs breaking up into smaller regions (20x128 pixels)..."
RESHAPE and STD would be simpler and likely faster:
im = rand(128,4700);
tic
jm = reshape(im,128,20,[]);
s0 = std(jm,0,[1,2]);
toc
Elapsed time is 0.027938 seconds.
tic
s1 = blockproc(im,[128,20],@(bs)std2(bs.data));
toc
Elapsed time is 0.105474 seconds.
isequal(s0(:),s1(:))
ans = logical
1
A FOR loop could be useful too:
for k = 1:size(jm,3)
m = jm(:,:,k);
% do whatever with m
end
... which can be trivially converted to a PARFOR:
34 Comments
Oh wow thats amazing. I did a time comparison on your S0 and S1 and get much faster times when using S0 (i.e. the reshape)
Elapsed time is 0.018587 seconds.
Elapsed time is 0.166048 seconds.
why is that, I thought Blockproc was the ultimate - obviously not.!
Could it be the use of std rather than std2?
Also, is there anything obviously wrong with my parallel toolbox approach - why do i sometimes see fast and other times slow.
"why is that, I thought Blockproc was the ultimate - obviously not.!"
BLOCKPROC splits up data, calls functions repeatedly, works with relatively complex data types, and would have to have a lot of input/error handling. Based on that alone I would assume that it is not very fast. Convenient perhaps, but not fast.
"Could it be the use of std rather than std2?"
I doubt it. It is understanding how data is stored, avoiding splitting data up, avoiding calling functions lots of times, avoiding parallel toolbox for small tasks, using RESHAPE to make data convenient to access, maximising speed by performing multiple operations with single (inbuilt, highly optimised) function calls by using their options, and various other things that one learns. These are the native and natural ways to use MATLAB efficiently.
When a user comes onto this forum as writes "my code is slow and I tried to speed it up using the parallel toolbox" then they have usually misunderstood MATLAB as well as what parallel processing requires and when to apply it.
Jason
9 minuten ago
Thankyou!
Sorry, one last question, how do I get the sd values from here into a column vector (without using loops)?
s0 = std(jm,0,[1,2]);
[sy,sx,sz]=size(s0)
idx=(1:sz)'
figure
sd=s0(:,:,idx)
plot(sd)
This didn't work
im = rand(128,4700);
jm = reshape(im,128,20,[]);
s0 = std(jm,0,[1,2]);
size(s0)
ans = 1×3
1 1 235
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
sd = s0(:)
sd = 235×1
0.2883
0.2886
0.2886
0.2895
0.2908
0.2870
0.2869
0.2890
0.2892
0.2878
0.2852
0.2828
0.2901
0.2901
0.2901
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
hold on
plot(sd)
plot(1/sqrt(12)*ones(size(sd)))
hold off

thankyou
Btw, where does this come from?
plot(1/sqrt(12)*ones(size(sd)))
Actually, im really sorry but Im still running into an error using the reshape approach
My actual image is 4730 pixels wide (not 4700) and 128 pixels high.
I want each sub region to be 20 pixels wide and 128 pixels high like in the picture below

I tried this:
[sy,sx]=size(IM);
nRows=floor(sy/128);
nCols=floor(sx/20);
jm = reshape(IM,nRows,nCols,[]); % reshape = rows, then cols
Error using reshape
Product of known dimensions, 236, not divisible into total number of elements, 605568.
You must take nRows and nCols such that their product gives 4730.
factor(4730)
ans = 1×4
2 5 11 43
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Thus nRows = 2, nCols = 5*11*43 or nRows = 2*5, nCols = 11*43 or nRows = 5*11, nCols = 2*43 or ... is allowed.
Or you must cut some part of the image.
Jason
ongeveer 2 uur ago
OK, thankyou.
The error message states:
"Product of known dimensions, 236, not divisible into total number of elements, 605568."
Note that the function RESHAPE simply changes the dimension sizes of an array, without adding or removing any data.
Now consider your image, whch contains NP many pixels (where NP does not match your comment):
im = rand(128,4730);
sz = size(im);
np = prod(sz)
np = 605440
Now lets look at what you attempted with those NP pixels:
[sy,sx] = size(im);
nRows = floor(sy/128)
nRows = 1
nCols = floor(sx/20)
nCols = 236
So you attempted to reshape NP elements into an array with 1 row, 236 columns, and some unknown number of pages. But is NP divisble by 1 and 236 ? Well, 1 is trivial, but 236 lets check if there is any remainder:
mod(np,nCols)
ans = 100
Nope, the number of rows and columns that you attempted to reshape IM into will not work because they cannot evenly divide the number of pixels of IM. That is the cause of the error: RESHAPE cannot create the size array that you requested, because it would require either inventing more data or removing it, which RESHAPE does not do.
Also note that your concept of how to specify the number of rows and columns is unlikely to help you, as should be clear when you perform some basic sanity check, e.g. by asking yourself "why am I reshaping all of the pixels into one row?". You should be counting pixels.
Thankyou for an excellent explanation (and toTorsten).
Ive decided to crop my image so its exactly divisible.
I love how the std you showed uses pages. If i wanted to calculate the Brenner metric instead of the std, how would I do that. It seens std allows for "paging" but its not obvious how the fmeasure function allows for it.
Just to add, you've blown me away with your example being so much faster than blockproc.
"If i wanted to calculate the Brenner metric instead of the std, how would I do that. It seens std allows for "paging" but its not obvious how the fmeasure function allows for it."
I would skip FMEASURE entirely. If speed is important to you then I would start with a simple FOR loop, which is likely to give reasonable speed, give you a good baseline, and be easy to implement:
im = rand(128,4700);
tic
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
pn = size(jm,3);
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
b0(k) = mean2(max(a,b).^2);
end
toc
Elapsed time is 0.014675 seconds.
b0
b0 = 235×1
0.1600
0.1487
0.1606
0.1542
0.1573
0.1644
0.1561
0.1576
0.1584
0.1644
0.1544
0.1584
0.1542
0.1596
0.1567
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
"where does this come from?"
It is the expected standard deviation of a continuous uniform distribution U(a,b) between a=0 and b=1. The variance of the uniform distribution is
which for those a & b gives
. The standard deviation is therefore just the square root of that.
which for those a & b gives
. The standard deviation is therefore just the square root of that.Wow!
Could I ask
1. Why do you suggest not using fmeasure?
2. Could you explain your FOR loop please and what that end metric actually is?
Thankyoy
1) because a) inline code tends to be slightly faster than calling a function (as explained here), b) the code for Brenner's metric is fairly simple (just a few lines), c) you can avoid third-party code dependency, d) you may be able to improve/tailor the implementation, e) you learn something about writing code.... and maybe some other reasons. One code-writing rule-of-thumb is do not reinvent the wheel, but this is balanced aginst other rules-of-thumb, such that if you have different priorities (e.g. speed) then tighter coupling actually becomes more useful. In this case there really is nothing stopping you from replacing the entire content of the FOR-loop with an FMEASURE call (which then gets called multiple times).
2) Which part of the loop? The loop preallocates an output vector b0 with NaNs:
then loops over each page of the RESHAPEd image data, storing it in the temporary variable m. Each m is a matrix with size 128x20, which AFAICT is the subimage that you want to process. To calculate Brenner's metric I just looked at the code in FMEASURE, tweaked it, and checked it with an AI tool (btw, you can also look inside FMEASURE). The end metric is Brenner's metric. The metric value is stored in the preallocated vector, there is a vector of them because you have multiple subimages.
But you should not take my word for it: check everything yourself! You can check the size of m yourself using SIZE. You can perform a sanity check on the content of a subimage by extracting the last subimage yourself:
w = im(:,end-19:end);
isequal(m,w)
ans = logical
1
You can perform a sanity check on the metric by comparing the last metric with the output from FMEASURE:
b1 = fmeasure(m,'BREN',[])
b1 = 0.1605
isequal(b0(end),b1)
ans = logical
1
You can learn more about FOR loops here:
You can practice FOR loops here:
Jason
ongeveer 23 uur ago
Thankyou again. I have taken your advice and performed a sanity check, but am getting a different value using fmeasure (quite a large difference too)
im = rand(128,4700);
% Reshape Way
t1=tic;
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
pn = size(jm,3);
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
b0(k) = mean2(max(a,b).^2);
end
tend1=toc(t1);
ReportMessage(app,['Reshape: in ',num2str(tend1),' s']); % my own function
% Now fmeasure way
t2=tic;
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
for k = 1:pn
m = jm(:,:,k);
FM(k)=fmeasure(m,'BREN',[]); %Focus metric
end
tend2=toc(t2);
ReportMessage(app,['Brenner: in ',num2str(tend2),' s']);
disp(' Reshape:')
head(b0(:))
disp(' Fmeasure:')
FM=FM';
head(FM(:))
size(b0)
size(FM)
And here is the output:
Reshape:
0.1589
0.1504
0.1602
0.1537
0.1655
0.1584
0.1565
0.1558
Fmeasure:
0.4994
0.4920
0.4837
0.5001
0.4974
0.4898
0.4979
0.4873
ans =
235 1
ans =
235 1
Using this function here:
and running on this forum I get:
im = rand(128,4700);
% reshape
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
pn = size(jm,3);
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
b0(k) = mean2(max(a,b).^2);
end
% fmeasure
b1 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
b1(k) = fmeasure(m,'BREN',[]);
end
% comparison:
b0
b0 = 235×1
0.1556
0.1557
0.1571
0.1562
0.1526
0.1576
0.1575
0.1608
0.1640
0.1650
0.1478
0.1568
0.1575
0.1584
0.1592
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
b1
b1 = 235×1
0.1556
0.1557
0.1571
0.1562
0.1526
0.1576
0.1575
0.1608
0.1640
0.1650
0.1478
0.1568
0.1575
0.1584
0.1592
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
isequal(b0,b1)
ans = logical
1
Jason
ongeveer 22 uur ago
Hi, Im using exactly the same fmeasure, and here is the "Bren" part. This has foxed me!
case 'BREN' % Brenner's (Santos97)
[M N] = size(Image);
DH = Image;
DV = Image;
DH(1:M-2,:) = diff(Image,2,1);
DV(:,1:N-2) = diff(Image,2,2);
FM = max(DH, DV);
FM = FM.^2;
FM = mean2(FM);
It occured to me that you might be providing integer class image data for your actual data, in which case convert it to DOUBLE before running the FOR loop, for example:
jm = reshape(double(im),rn,cn,[]);
I just did some reading about Brenner's metric, and it seems that the implementation in FMEASURE is flawed due to its MAX after squaring. It should square the values before the MAX (or adding them, or whatever).
Consider this carefully-picked example:
im = [10,0,10; 0,0,0; 5,0,5]
im = 3×3
10 0 10
0 0 0
5 0 5
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
fmeasure(im,'BREN',[]) % ZERO !!!!
ans = 0
This occurs because the MAX silently ignores large negative values:
% DH(:,1) = Image(:,3) - Image(:,1)
DH(:,1) = [10-10, 0-0, 5-5] = [0, 0, 0] % First col only; rest stay 0
% → DH is all zeros
% DV(1,:) = Image(3,:) - Image(1,:)
DV(1,:) = [5-10, 0-0, 5-10] = [-5, 0, -5] % First row only; rest stay 0
% → DV has real, significant gradients — but NEGATIVE
FM = max(DH, DV)
= max(0, -5) = 0 % ← the bug! -5 gets silently ignored
FM = mean2(0.^2) = 0
To avoid this square the values before the MAX:
a = im(3:end,:)-im(1:end-2,:);
b = im(:,3:end)-im(:,1:end-2);
a(3,1) = 0;
b(1,3) = 0;
mean2(max(a,b).^2) % wrong!
ans = 0
mean2(max(a.^2,b.^2)) % right!
ans = 5.5556
Some other divergences of FMEASURE from Santos et al:
- it does not implement the gradient threshold v,
- it uses MEAN2 in place of summation.
So I guess that adds another point to the list: f) because random code from the internet is not always reliable: you always need to check it thoroughly.
"This has foxed me!"
Step-by-step debugging: print critical values from each approach, work backwards until you pinpoint where they diverge.
Torsten
ongeveer 22 uur ago
It occured to me that you might be providing integer class image data
im = rand(128,4700);
?
Hmm, So I see the error you have pointed out with the Brenner calculation in the fmeasure function.
But 2 things bug me:
1: How come in your example above, you get the same values for b0 and b1?
im = rand(128,4700);
% reshape
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
pn = size(jm,3);
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
b0(k) = mean2(max(a,b).^2);
end
% fmeasure
b1 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
b1(k) = fmeasure(m,'BREN',[]);
end
% comparison:
b0
2: I spent all day trying to track down why Im not getting the same values as you but can't see anything wrong.
im = rand(128,4700);
% Reshape Way
rn = 128;
cn = 20;
jm = reshape(double(im),rn,cn,[]);
pn = size(jm,3);
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
b0(k) = mean2(max(a,b).^2);
end
% Now fmeasure way
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
b1 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
b1(k)=fmeasure((m),'BREN',[]); %Focus metric
end
disp(' Reshape:')
head(b0)
disp(' Fmeasure:')
head(b1)
Reshape:
0.1586
0.1559
0.1567
0.1555
0.1621
0.1543
0.1642
0.1622
Fmeasure:
0.7821
0.7611
0.7665
0.7806
0.7833
0.7503
0.7840
0.7819
Nick
ongeveer 16 uur ago
Your implementation looks different from Brenner's row version:
function fm = brenner_measure(img)
% Brenner's formula: sum of squared differences 2 pixels apart
diffs = (img(1:end-2, :) - img(3:end, :)).^2;
fm = mean(diffs(:));
end
When you come across negative row and/or column differences your method can break down.
You may try to replace
b0(k) = mean2(max(a,b).^2);
with
b0(k) = mean2(max([a,b,0]).^2);
% or if you want to get upper bound
b0(k) = mean2(max([a,-a,b,-b]).^2);
and compare results
Thanks Nick, so I did as you suggested
(Are you saying the formula that Stephen kindly gave me is wrong)?
% Stephen's Brenner
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
% b0(k) = mean2(max(a,b).^2); % Stephen's Original formula
b0(k) = mean2(max([a,b,0]).^2); % Nick's suggestion
end
Error using horzcat
Dimensions of arrays being concatenated are not consistent.
Error in HTS_TestSoftware/TestingButtonPushed (line 28012)
b0(k) = mean2(max([a,b,0]).^2);
"(Are you saying the formula that Stephen kindly gave me is wrong)?"
I did not give you any formula! Exactly as I wrote in this comment here, I copied the algorithm from FMEASURE. And FMEASURE quotes Santos et al., which I also downloaded. You can find it here:
You selected FMEASURE (which uses Santos et al's algorithm, although perhaps with some bugs), which my code exactly replicates (as demonstrated here).
"Your implementation looks different from Brenner's row version"
Of course it looks different, because it is different! Santos et al explicitly stated their text: "The first three functions (
,
,
) were slightly modified to consider both the horizontal and the vertical gradients". Brenner only considered the horizontal gradients, not the horiztonal gradients. But Santos et al stated that they modified this algorithm!
If you do not even know what algorithm you are using, how do you expect to verify its output?
Have you read Santos et al's paper yet?
"You may try to replace b0(k) = mean2(max(a,b).^2); with b0(k) = mean2(max([a,b,0]).^2);"
No, that won't work because it attempts to concatenate arrays with incompatible sizes. It also attempts to implement something quite different from what Santos et al specified.
" I spent all day trying to track down why Im not getting the same values as you but can't see anything wrong."
You will not find anything by looking all day at some code hoping to see something "wrong". That is not how debugging works.
One obvious start is to compare intermediate values. Have you done that? It should take about two minutes.
Jason
ongeveer 14 uur ago
Edited: Walter Roberson
ongeveer 9 uur ago
Hi Stephen, OK thanks.
BTW, I have already showed above which "Brenner" algo Im using from the fmeasure function
case 'BREN' % Brenner's (Santos97)
[M N] = size(Image);
DH = Image;
DV = Image;
DH(1:M-2,:) = diff(Image,2,1);
DV(:,1:N-2) = diff(Image,2,2);
FM = max(DH, DV);
FM = FM.^2;
FM = mean2(FM);
Im sorry if i incorrectly suggested "your" Brenner formula. What I mean't was after you kindly showed how to use reshape with std function, I asked how to also incorportate the fmeasure "Brenner" formula
You did mention to skip it and gve me what i assumed was something similar (and I just called it Stephen's Brenner)
"If i wanted to calculate the Brenner metric instead of the std, how would I do that. It seens std allows for "paging" but its not obvious how the fmeasure function allows for it."
I would skip FMEASURE entirely. If speed is important to you then I would start with a simple FOR loop, which is likely to give reasonable speed, give you a good baseline, and be easy to implement:
im = rand(128,4700);
tic
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
pn = size(jm,3);
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
b0(k) = mean2(max(a,b).^2);
end
toc
@Jason: please run your code in this forum. Then I can help you to debug it.
"How come in your example above, you get the same values for b0 and b1?"
Because the code inside FOR-loop simply replicates that of FMEASURE.
Nick
ongeveer 13 uur ago
sorry for sloppy comment — only idea, the code was not correct, just illustration…
a few details follow
for k = 1:pn
m = jm(:,:,k);
a = abs(m(3:end,2:end-1)-m(1:end-2,2:end-1)); % get absolute values
b = abs(m(2:end-1,3:end)-m(2:end-1,1:end-2)); % the same for columns
% a(rn,1) = 0;
% b(1,cn) = 0;
b0(k) = mean2(max(a,b).^2); % now it is correct, but not Brenner
% b0(k) = mean2(max([a,b,0]).^2); — my fault, was careless :
% were not scalar type
end
The original Brenner resembles function brenner_measure() in my previous post
Stephen23
ongeveer 12 uur ago
Note that Nicks code discards data without warning. It also has the wrong denominator inside MEAN2.
Consider an image which has gradient only in the first row:
format long G
rn = 3;
cn = 3;
m = [0,4,8; 4,4,4; 4,4,4]
m = 3×3
0 4 8
4 4 4
4 4 4
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
0) Inline code, fixed FMEASURE bug:
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
fm0 = mean2(max(a.^2,b.^2))
fm0 =
8.88888888888889
1) inline code, same bug as FMEASURE:
fm1 = mean2(max(a,b).^2)
fm1 =
7.11111111111111
2) FMEASURE
fm2 = fmeasure(m,'BREN',[])
fm2 =
7.11111111111111
3) Not like anything else:
a = abs(m(3:end,2:end-1)-m(1:end-2,2:end-1)); % get absolute values
b = abs(m(2:end-1,3:end)-m(2:end-1,1:end-2)); % the same for columns
fm3 = mean2(max(a,b).^2)
fm3 =
0
So even though there is a valid gradient Nick's code does not detect it. The incorrect denominator is slightly more subtle, but is left as an exercise.
Jason
ongeveer 11 uur ago
@Jason: please run your code in this forum. Then I can help you to debug it.
"How come in your example above, you get the same values for b0 and b1?"
Because the code inside FOR-loop simply replicates that of FMEASURE.
How do I run my code in this forum?
Ive never done that before
"How do I run my code in this forum? Ive never done that before"
Write the code in the message:

Optionally upload Mfiles:

Then click the RUN button:

Hi Stephen, thanks.
Here you go - Im getting as I do on my PC - different values, where as you got the same
im = rand(128,4700);
% Reshape Way Using Stephens FMEASURE replication
t1=tic;
rn = 128;
cn = 20;
jm = reshape(double(im),rn,cn,[]);
pn = size(jm,3);
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
% b0(k) = mean2(max(a,b).^2); % Replicate BUG in FMEASURE
b0(k)=mean2(max(a.^2,b.^2)); % Corrected BUG
end
tend1=toc(t1);
head(b0)
0.2403
0.2413
0.2524
0.2523
0.2462
0.2418
0.2350
0.2534
% Now fmeasure way - need to define fmeasure (with BUG Corrected)
function FM=fmeasure(Image)
[M N] = size(Image);
DH = Image;
DV = Image;
DH(1:M-2,:) = diff(Image,2,1);
DV(:,1:N-2) = diff(Image,2,2);
% Added Stephen53 Matlab
% Bug where -ve values are ignored. To avoid this square the values before the MAX:
% FM = max(DH, DV);
% FM = FM.^2;
% FM = mean2(FM);
% % mean2(max(a.^2,b.^2)) % right!
FM = mean2(max(DH.^2,DV.^2));
end
t2=tic;
rn = 128;
cn = 20;
b1 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
b1(k)=fmeasure(m); %Focus metric
end
tend2=toc(t2);
head(b1)
0.7403
0.7493
0.7757
0.7901
0.7768
0.7976
0.7314
0.7754
"Now fmeasure way - need to define fmeasure (with BUG Corrected)"
No, that is not the "fmeasure way", it has at least two bugs:
1) using the entire image to preallocate instead of using zeros, so you are performing all following operations on different values than those which FMEASURE uses.
2) using DIFF which calculates the difference between adjacent elements, not ones separated by some arbitrary step size. As the DIFF documentation clearly states, the 2nd input specifies how many times that this is applied recursively, not some arbitrary step-size. DIFF does not have a step-size option.
After fixing those bugs the code produces exactly the same results as my FOR-loop and the same as the bug-fixed FMEASURE :
im = rand(128,4700);
% reshape
rn = 128;
cn = 20;
jm = reshape(im,rn,cn,[]);
pn = size(jm,3);
% inline
b0 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
a(rn,1) = 0;
b(1,cn) = 0;
b0(k) = mean2(max(a.^2,b.^2));
end
% function (fixed MAX bug):
b1 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
b1(k) = fmeasure1(m,'BREN',[]);
end
% code FIXED by me:
b2 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
b2(k) = fmeasureFixed(m);
end
% code BROKEN:
b3 = nan(pn,1);
for k = 1:pn
m = jm(:,:,k);
b3(k) = fmeasureBroken(m);
end
% Compare
b0 % inline
b0 = 235×1
0.2449
0.2510
0.2453
0.2389
0.2422
0.2500
0.2478
0.2514
0.2428
0.2382
0.2467
0.2478
0.2427
0.2525
0.2428
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
b1 % FMEASURE
b1 = 235×1
0.2449
0.2510
0.2453
0.2389
0.2422
0.2500
0.2478
0.2514
0.2428
0.2382
0.2467
0.2478
0.2427
0.2525
0.2428
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
●
b2 % code FIXED by me
b2 = 235×1
0.2449
0.2510
0.2453
0.2389
0.2422
0.2500
0.2478
0.2514
0.2428
0.2382
0.2467
0.2478
0.2427
0.2525
0.2428
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
b3 % code BROKEN
b3 = 235×1
0.7906
0.7848
0.7642
0.7780
0.7756
0.7766
0.7623
0.8022
0.7663
0.7526
0.7761
0.7883
0.7732
0.7648
0.7749
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
function FM = fmeasureFixed(Image)
[M,N] = size(Image);
DH = 0*Image; % !!! fixed bug !!!
DV = 0*Image; % !!! fixed bug !!!
DV(1:M-2,:) = Image(3:end,:)-Image(1:end-2,:); % !!! fixed bug !!!
DH(:,1:N-2) = Image(:,3:end)-Image(:,1:end-2); % !!! fixed bug !!!
FM = mean2(max(DH.^2,DV.^2));
end
function FM=fmeasureBroken(Image)
[M,N] = size(Image);
DH = Image; % buggy
DV = Image; % buggy
DH(1:M-2,:) = diff(Image,2,1); % buggy
DV(:,1:N-2) = diff(Image,2,2); % buggy
FM = mean2(max(DH.^2,DV.^2));
end
Also note that your comment "Bug where -ve values are ignored." is incorrect. I never wrote that all negative values are ignored.
Jason
ongeveer 4 uur ago
Hi Stephen, you've done it!! You've worked out why I wasn't matching what you had. Its because the version of fmeasure I was using was pre 2016 .... where DH=image and the diff function was used. You must be using a latest version

One last query (I understand if you've had enough now - and I really appreciate the massive effort you have put into this).
How can "some" -ve values be ignored and not all?
Stephen23
ongeveer 3 uur ago
"How can "some" -ve values be ignored and not all?"
max(-3,-9)
ans = -3
Nick
21 minuten ago
Resume:
@Stephen23 analyzed codes for "Brenner's" variant of fmeasure() function:
0) buggy code: wrong results as a rule; better forget it
2) no code to analyze
3) corrected by skipping boundary gradients, looks like not what expected
To use a more robust version of the function with boundaries taken into account, try:
m = [0,4,8; 4,4,4; 4,4,4; 4,0,4]'
a = m(3:end,:)-m(1:end-2,:);
b = m(:,3:end)-m(:,1:end-2);
fm4 = mean2([a(:).^2;b(:).^2])
P.S. The code only demonstrates an approach to get "mean gradient" inside domain (without the boundary). That is why no warnings and error handlers assumed. BTW denominator is correct.
More Answers (0)
Categories
Find more on Tables in Help Center and File Exchange
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)