Weird Behaviour of Parallel Processing when Using BlockProc
显示 更早的评论
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 个评论
"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>
采纳的回答
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:
19 个评论
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.
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.
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:
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
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.
It occured to me that you might be providing integer class image data
im = rand(128,4700);
?
更多回答(0 个)
类别
在 帮助中心 和 File Exchange 中查找有关 Blocked Images 的更多信息
另请参阅
选择网站
选择网站以获取翻译的可用内容,以及查看当地活动和优惠。根据您的位置,我们建议您选择:。
您也可以从以下列表中选择网站:
如何获得最佳网站性能
选择中国网站(中文或英文)以获得最佳网站性能。其他 MathWorks 国家/地区网站并未针对您所在位置的访问进行优化。
美洲
- América Latina (Español)
- Canada (English)
- United States (English)
欧洲
- 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)
