extracting mean pixel value from an image
20 views (last 30 days)
Show older comments
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
0 Comments
Accepted Answer
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.
A = imread('https://www.mathworks.com/matlabcentral/answers/uploaded_files/1004350/Picture1.png');
Ahsv = rgb2hsv(A);
[H S V] = imsplit(Ahsv);
mask = S>0.9 & H>0.15; % create mask
mask = bwareaopen(mask,100); % despeckle
% use a large strel to bridge dotted lines
% this assumes that lines are roughly grid-aligned
stlen = 200;
mask = imopen(~mask,ones(stlen));
% erode to avoid including any green bits on edges
mask = imerode(mask,ones(10));
% this assumes that the largest blob is the ROI
mask = bwareafilt(mask,1);
imshow(mask)
% build an Mx3 color table from the pixels in the ROI
roipix = zeros(nnz(mask),3,class(A));
for c = 1:3 % assumes A is RGB
thischan = A(:,:,c);
roipix(:,c) = thischan(mask);
end
% find the mean color
meancolor = mean(roipix,1)
% 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.
% 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.
25 Comments
DGM
on 2 Jun 2022
... maybe. It would depend on how you consistent the framing/scale is between photos. Let's consider the image above. If you assume that the ROI is defined by a 9x9 grid of dots in the center ...
I have an idea. It'll probably be flaky, but give me a bit to throw something together...
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
A = imread('10A_22_skew.jpg');
% get mask of all light peaks
peakmask = mean(A,3)>210; % HSI intensity
peakmask = imclearborder(peakmask);
peakmask = bwareaopen(peakmask,100);
imshow(peakmask); hold on
% get centroids
S = regionprops(peakmask,'centroid');
Cpeaks = vertcat(S.Centroid);
Cimg = fliplr(size(peakmask))/2;
% 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)
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
%A = imread('Picture1.png');
%A = imread('picture4.jpg');
%A = imread('40A_12.jpg');
%A = imread('25A_13.jpg');
%A = imread('10A_22.jpg');
%A = imread('10A_22_skew.jpg');
A = imread('40A_23.jpg');
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);
roimask = V<100;
roimask = bwareaopen(roimask,100); % despeckle
% find the point that's furthest away from any dark spots
D = bwdist(roimask);
[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
peakmask = imclearborder(peakmask);
peakmask = bwareaopen(peakmask,100);
imshow(peakmask); hold on
% get centroids
S = regionprops(peakmask,'centroid');
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.
3 Comments
Image Analyst
on 4 Jun 2022
Try
mask = ROI.createMask;
or else
mask = poly2mask(Ccorners(:, 1), Ccorners(:, 2), imageRows, imageColumns);
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
%A = imread('Picture1.png');
%A = imread('picture4.jpg');
%A = imread('40A_12.jpg');
%A = imread('25A_13.jpg');
%A = imread('10A_22.jpg');
%A = imread('10A_22_skew.jpg');
A = imread('40A_23.jpg');
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);
roimask = V<100;
roimask = bwareaopen(roimask,100); % despeckle
% find the point that's furthest away from any dark spots
D = bwdist(roimask);
[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
peakmask = imclearborder(peakmask);
peakmask = bwareaopen(peakmask,100);
imshow(peakmask); hold on
% get centroids
S = regionprops(peakmask,'centroid');
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
mask = createMask(ROI);
% show mask
figure
imshow(mask)
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
13 Comments
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?
See Also
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!