# extracting mean pixel value from an image

31 views (last 30 days)
OLUWAFEMI AKINMOLAYAN on 19 May 2022
Commented: DGM on 1 Jul 2022
I would like to extract the mean pixel value on the image.
I would like to do it automatic by locating the brightess point next to the green rectangular point coordinates, cropping the new coordinates and finding its mean pixel vaules

DGM on 20 May 2022
Edited: DGM on 20 May 2022
This might be a start, but bear in mind how fragile this will be if the colors change or the swatch becomes rotated or something.
Ahsv = rgb2hsv(A);
[H S V] = imsplit(Ahsv);
mask = S>0.9 & H>0.15; % create mask
% use a large strel to bridge dotted lines
% this assumes that lines are roughly grid-aligned
stlen = 200;
% erode to avoid including any green bits on edges
% this assumes that the largest blob is the ROI
% build an Mx3 color table from the pixels in the ROI
for c = 1:3 % assumes A is RGB
thischan = A(:,:,c);
end
% find the mean color
meancolor = mean(roipix,1)
meancolor = 1×3
220.5722 176.5078 21.8325
% for visualization of the nonsquare ROI
% fill non-ROI with black
rgbpict = A;
rgbpict(repmat(~mask,[1 1 3])) = 0;
% pad the modified image with a color swatch
sz = size(A);
meanswatch = repmat(permute(meancolor,[1 3 2]),[50 sz(2) 1]);
meanswatch = uint8(meanswatch);
outpict = [rgbpict; meanswatch];
imshow(outpict)
Note that's just the simple arithmetic mean. It might not be what you want, considering how it will be influenced by the light bleed and the vignetting. Alternatively, you might try using median() instead of mean.
See this answer for other options. Based on that answer,
% get some different image stats from the ROI
cc = imstats(ctflop(roipix),'mean','median','mode','modecolor','modefuzzy','moderange','nmost',10);
% use those stats to construct a swatch chart for single-output stats
labels = {'mean','median','mode','modecolor','modefuzzy'};
sz = imsize(rgbpict,2);
ntiles = (numel(labels));
tilesz = [round(sz(1)/ntiles) 100];
block1 = zeros([tilesz 3 ntiles],'uint8');
for k = 1:ntiles
thistile = colorpict([tilesz 3],cc(k,:),'uint8'); % colored swatch
thislabel = im2uint8(1-textim(labels{k},'ibm-iso-16x9')); % text label image
thistile = im2uint8(imstacker({thislabel thistile},'padding',1)); % match geometry
block1(:,:,:,k) = mergedown(thistile,1,'linearburn'); % blend label and swatch
end
block1 = imtile(block1,[ntiles 1]); % vertically arrange tiles
block1 = imresize(block1,[sz(1) tilesz(2)]); % make sure it's the right size
% create another chart for moderange's multiple outputs
ntiles = (size(cc,1)-ntiles);
tilesz = [round(sz(1)/ntiles) 100];
block2 = zeros([tilesz 3 ntiles],'uint8');
for k = 1:ntiles
thistile = colorpict([tilesz 3],cc(k+4,:),'uint8'); % colored swatch
thislabel = im2uint8(1-textim(num2str(k),'ibm-iso-16x9')); % text label image
thistile = im2uint8(imstacker({thislabel thistile},'padding',1)); % match geometry
block2(:,:,:,k) = mergedown(thistile,1,'linearburn'); % blend label and swatch
end
block2 = imtile(block2,[ntiles 1]); % vertically arrange tiles
block2 = imresize(block2,[sz(1) tilesz(2)]); % make sure it's the right size
% show the combined images
imshow([rgbpict block1 block2])
Make of that what you will. See the linked answer for more details on these options.
DGM on 2 Jun 2022
Edited: DGM on 4 Jun 2022
This might not actually be too bad. It works for all the given images so far without any consideration of the marker dots. You can tweak the angle tolerance quite a bit. I modified one of the images to demonstrate that it still works even if there's quite a bit of distortion.
% the input image
% get mask of all light peaks
peakmask = mean(A,3)>210; % HSI intensity
% get centroids
Cpeaks = vertcat(S.Centroid);
% find the peak closest to the image center
D = sqrt(sum((Cpeaks-Cimg).^2,2));
[~,idx] = min(D);
Ccenter = Cpeaks(idx,:);
% mark the center peak
plot(Ccenter(1),Ccenter(2),'r*','linewidth',2)
% find the 8 peaks closest to this peak
D = sqrt(sum((Cpeaks-Ccenter).^2,2));
[~,idx] = mink(D,9);
Cnh = Cpeaks(idx(2:end),:); % ignore self-distance
% find points which are oriented roughly 45
thtol = 16; % allowable angle deviation from 45
thc = atan2d(Cnh(:,2)-Cimg(1,2),Cnh(:,1)-Cimg(1,1));
th = mod(thc,90);
idxc = th>(45-thtol) & th<(45+thtol); % find angles which are roughly multiples of 45
Ccorners = Cnh(idxc,:); % there should only be 4 rows here
thc = thc(idxc);
% walk outwards along those diagonal trajectories
sqsize = 9; % the square is 9x9 points
for kann = 1:floor(sqsize/2)-1
for k = 1:4 % assuming Ccorners has 4 rows
% find the 8 peaks closest to this peak
thiscorner = Ccorners(k,:);
D = sqrt(sum((Cpeaks-thiscorner).^2,2));
[~,idx] = mink(D,9);
Cnh = Cpeaks(idx(2:end),:); % ignore self-distance
% find point which is oriented roughly in the same direction
% as the prior point was oriented WRT to the image center
th = atan2d(Cnh(:,2)-thiscorner(1,2),Cnh(:,1)-thiscorner(1,1));
[~,idx] = min(abs(th-thc(k)));
Ccorners(k,:) = Cnh(idx,:); % there should only be one row here
thc(k) = th(idx);
end
end
% show where the estimated corners are
plot(Ccorners(:,1),Ccorners(:,2),'mo','linewidth',2,'markersize',20)
% sort by angle
[~,idx] = sort(thc,'ascend');
Ccorners = Ccorners(idx,:);
% create polygonal ROI object
ROI = images.roi.Polygon(gca);
ROI.Position = Ccorners;
At that point, you could use createMask() as before to get a logical mask from ROI, or whatever else is needed.

### More Answers (3)

OLUWAFEMI AKINMOLAYAN on 3 Jun 2022
Great work DGM. Thanks alot. This works for most of the images. I just needed to change the peakmask for some.
However, very few of the images (one of the images and outcome is attached) have their grid lines shifted a bit to one side. This somehow affected the outcome. I tried to tweak the code but it gave me errors. I think one would have to use the grid line to mask the ROI in this case. I might be wrong. You will best know how to tweak the code to get the desire result if its possible.
Also, I have a question. In an attempt to crop out the ROI to give the rectangular image "B", do you know what information might be lost?
I hope i am not troubling you with too much of my questions? lols
For some reason i couldn't add comment to previous answer. I had to comment under more answers. I hope you see this.
Thanks once again

DGM on 4 Jun 2022
I was assuming that the green marks weren't going to be the ROI delimiters anymore, but if they still are, you can't use image geometry to estimate the ROI center. You still have to deal with finding the marks -- but maybe you can get away without needing to do as much masking.
Still, finding the region center is questionable if there's significant perspective distortion. I slapped it in a loop and made it at least try to fix itself if it runs into the edge of the image, but I don't doubt that this can still break easily. It works for all the images so far at least. It doesn't seem too picky about the masking yet.
% the input image
sqsize = 9; % the square is 9x9 points
thtol = 15; % allowable angle deviation from 45
% try to find dark-ish areas that might be marks
V = max(A,[],3);
% find the point that's furthest away from any dark spots
[mx,idx] = max(D(:));
[r c] = ind2sub(size(D),idx);
Cimg = [c r];
% get mask of all light peaks
peakmask = mean(A,3)>200; % HSI intensity
% get centroids
Cpeaks = vertcat(S.Centroid);
% distance from every object to every other object
D = sqrt((Cpeaks(:,1)-Cpeaks(:,1).').^2 + (Cpeaks(:,2)-Cpeaks(:,2).').^2);
D(D<1E-6) = NaN; % remove self-distances
Dmean = mean(min(D,[],2)); % average grid spacing
% try to find corners
% if we run into the image edges, adjust center and try again
% adjust at most twice before continuing/failing
Ccorrection = 0;
for attempt = 1:3
[Ccorners thc Ccorrection] = findcorners(Cpeaks,Cimg,sqsize,thtol);
if Ccorrection == 0; break; end
if attempt == 1
% distance from every object to every other object
D = sqrt((Cpeaks(:,1)-Cpeaks(:,1).').^2 + (Cpeaks(:,2)-Cpeaks(:,2).').^2);
D(D<1E-6) = NaN; % remove self-distances
Dmean = mean(min(D,[],2)); % average grid spacing
end
% adjust estimate of image center
Cimg(2) = Cimg(2) - Ccorrection*Dmean;
end
% show where the estimated corners are
plot(Ccorners(:,1),Ccorners(:,2),'mo','linewidth',2,'markersize',20)
% show ROI on original image
figure
imshow(A); hold on
% sort by angle
[~,idx] = sort(thc,'ascend');
Ccorners = Ccorners(idx,:);
% create polygonal ROI object
ROI = images.roi.Polygon(gca);
ROI.Position = Ccorners;
function [Ccorners thc Ccorrection] = findcorners(Cpeaks,Cimg,sqsize,thtol)
% find the peak closest to the image center
D = sqrt(sum((Cpeaks-Cimg).^2,2));
[~,idx] = min(D);
Ccenter = Cpeaks(idx,:);
% mark the center peak
plot(Ccenter(1),Ccenter(2),'c*')
% find the 8 peaks closest to this peak
D = sqrt(sum((Cpeaks-Ccenter).^2,2));
[~,idx] = mink(D,9);
Cnh = Cpeaks(idx(2:end),:); % ignore self-distance
% find points which are oriented roughly 45
thc = atan2d(Cnh(:,2)-Cimg(1,2),Cnh(:,1)-Cimg(1,1));
th = mod(thc,90);
idxc = th>(45-thtol) & th<(45+thtol); % find angles which are roughly multiples of 45
Ccorners = Cnh(idxc,:); % there should only be 4 rows here
thc = thc(idxc);
% walk outwards along those diagonal trajectories
for kann = 1:floor(sqsize/2)-1
for k = 1:4 % assuming Ccorners has 4 rows
% find the 8 peaks closest to this peak
thiscorner = Ccorners(k,:);
D = sqrt(sum((Cpeaks-thiscorner).^2,2));
[~,idx] = mink(D,9);
Cnh = Cpeaks(idx(2:end),:); % ignore self-distance
% find point which is oriented roughly in the same direction
% as the prior point was oriented WRT to the image center
th = atan2d(Cnh(:,2)-thiscorner(1,2),Cnh(:,1)-thiscorner(1,1));
[minth idx] = min(abs(th-thc(k)));
% if there are no neighboring points in this direction,
% that's probably because we just ran into the image edge
if minth > thtol
if thc(k)<0
% if walking downward, try shifting initial center upwards
Ccorrection = -1;
else
% if walking upward, try shifting initial center downward
Ccorrection = 1;
end
return;
else
Ccorrection = 0;
end
% update outputs
Ccorners(k,:) = Cnh(idx,:); % there should only be one row here
thc(k) = th(idx);
end
end
end
If you're cropping out B (and transforming it, etc), you obviously lose information outside of the ROI. You're also losing the exact original values within the ROI, since it's being interpolated. Whether that interpolation is meaningful for your analysis, I don't know.
OLUWAFEMI AKINMOLAYAN on 4 Jun 2022
@Image Analyst, None of them works.

DGM on 4 Jun 2022
It seems to work for me. The figure in which the ROI object is created needs to be present and needs to contain only one image.
% the input image
sqsize = 9; % the square is 9x9 points
thtol = 15; % allowable angle deviation from 45
% try to find dark-ish areas that might be marks
V = max(A,[],3);
% find the point that's furthest away from any dark spots
[mx,idx] = max(D(:));
[r c] = ind2sub(size(D),idx);
Cimg = [c r];
% get mask of all light peaks
peakmask = mean(A,3)>200; % HSI intensity
% get centroids
Cpeaks = vertcat(S.Centroid);
% distance from every object to every other object
D = sqrt((Cpeaks(:,1)-Cpeaks(:,1).').^2 + (Cpeaks(:,2)-Cpeaks(:,2).').^2);
D(D<1E-6) = NaN; % remove self-distances
Dmean = mean(min(D,[],2)); % average grid spacing
% try to find corners
% if we run into the image edges, adjust center and try again
% adjust at most twice before continuing/failing
Ccorrection = 0;
for attempt = 1:3
[Ccorners thc Ccorrection] = findcorners(Cpeaks,Cimg,sqsize,thtol);
if Ccorrection == 0; break; end
if attempt == 1
% distance from every object to every other object
D = sqrt((Cpeaks(:,1)-Cpeaks(:,1).').^2 + (Cpeaks(:,2)-Cpeaks(:,2).').^2);
D(D<1E-6) = NaN; % remove self-distances
Dmean = mean(min(D,[],2)); % average grid spacing
end
% adjust estimate of image center
Cimg(2) = Cimg(2) - Ccorrection*Dmean;
end
% show where the estimated corners are
plot(Ccorners(:,1),Ccorners(:,2),'mo','linewidth',2,'markersize',20)
% show ROI on original image
figure
imshow(A); hold on
% sort by angle
[~,idx] = sort(thc,'ascend');
Ccorners = Ccorners(idx,:);
% create polygonal ROI object
ROI = images.roi.Polygon(gca);
ROI.Position = Ccorners;
% create mask from ROI object
figure
function [Ccorners thc Ccorrection] = findcorners(Cpeaks,Cimg,sqsize,thtol)
% find the peak closest to the image center
D = sqrt(sum((Cpeaks-Cimg).^2,2));
[~,idx] = min(D);
Ccenter = Cpeaks(idx,:);
% mark the center peak
plot(Ccenter(1),Ccenter(2),'c*')
% find the 8 peaks closest to this peak
D = sqrt(sum((Cpeaks-Ccenter).^2,2));
[~,idx] = mink(D,9);
Cnh = Cpeaks(idx(2:end),:); % ignore self-distance
% find points which are oriented roughly 45
thc = atan2d(Cnh(:,2)-Cimg(1,2),Cnh(:,1)-Cimg(1,1));
th = mod(thc,90);
idxc = th>(45-thtol) & th<(45+thtol); % find angles which are roughly multiples of 45
Ccorners = Cnh(idxc,:); % there should only be 4 rows here
thc = thc(idxc);
% walk outwards along those diagonal trajectories
for kann = 1:floor(sqsize/2)-1
for k = 1:4 % assuming Ccorners has 4 rows
% find the 8 peaks closest to this peak
thiscorner = Ccorners(k,:);
D = sqrt(sum((Cpeaks-thiscorner).^2,2));
[~,idx] = mink(D,9);
Cnh = Cpeaks(idx(2:end),:); % ignore self-distance
% find point which is oriented roughly in the same direction
% as the prior point was oriented WRT to the image center
th = atan2d(Cnh(:,2)-thiscorner(1,2),Cnh(:,1)-thiscorner(1,1));
[minth idx] = min(abs(th-thc(k)));
% if there are no neighboring points in this direction,
% that's probably because we just ran into the image edge
if minth > thtol
if thc(k)<0
% if walking downward, try shifting initial center upwards
Ccorrection = -1;
else
% if walking upward, try shifting initial center downward
Ccorrection = 1;
end
return;
else
Ccorrection = 0;
end
% update outputs
Ccorners(k,:) = Cnh(idx,:); % there should only be one row here
thc(k) = th(idx);
end
end
end
DGM on 1 Jul 2022
I'm sure anything is possible, but at this point it's not really clear how that would change the task requirements. The last incarnation of the script finds an ROI as a irregular quadrilateral with vertices located on image features. In what manner is the ROI size to be fixed (e.g. area/height/width)? Does fixing the size also fix the shape or orientation? What parts of the fixed ROI would correspond to the image features?