Accurately detect color regions in an image using K-means clustering

与世无争的帅哥 提交于 2019-12-04 18:35:30

This is my approach to count the number of pixels in every region. Given that (as discussed in the comments):

  • the value (RGB) and the number (K) of colors are known a priori
  • compression artifacts and anti-aliasing generated additional colors, that must be considered as the nearest-neighbor among the K know colors.

Since you know a priori the colors, you don't need k-means. It could actually lead to bad results as in your question. The approach of @crowdedComputeeer take care of this aspect.

You can compute nearest neighbor with pdist2 directly on the pixel values. There's no need to use the really slow function that looks for the color name.

Here is the code. You can change the number and values of colors simply modifying the variable colors. This will compute the number of pixels in each color, and output the masks.

img =  (imread('path_to_image'));

colors = [  0 0 0;    % black
            0 1 0;    % green
            1 1 1];   % white


% % You can change the colors        
% colors = [  0 0 1;    % red
%             1 1 0;    % yellow
%             1 0 0;    % blue
%             1 1 1];   % white


% Find nearest neighbour color
list = double(reshape(img, [], 3)) / 255;
[~, IDX] = pdist2(colors, list, 'euclidean', 'Smallest', 1);
% IDX contains the indices to the nearest element


N = zeros(size(colors, 1), 1);
for i = 1 : size(colors, 1)
    % Count the number of pixels for each color
    N(i) = sum( IDX == i );
end

% This will display the number of pixels for each color
disp(N);



% Eventually build the masks
indices = reshape(IDX, [size(img,1), size(img,2)]);

figure();
szc = size(colors,1);
for i = 1 : szc
    subplot(1,szc,i);
    imagesc(indices == i);
end

Resulting counts:

97554     % black
16894     % green
31852     % white

Resulting masks:

I thought this problem was very interesting, so I apologize ahead of time if the answer is a little overboard. In short, k-means is the right strategy, in general, for problems where you want to segment an image into a discrete color space. But, your example image, which contains primarily only three colors, each of which is well separated in color space, is easily segmented using only a histogram. See below for segmenting using thresholds.

You can easily get the pixel counts by summing each matrix. e.g., bCount = sum(blackPixels(:))

filename = '379NJ.png';
x = imread(filename); 
x = double(x); % cast to floating point
x = x/max(max(max(x))); % normalize

% take histogram of green dimension
g = x(:, :, 2);
c = hist(g(:), 2^8);

% smooth the hist count 
c = [zeros(1, 10), c, zeros(1, 10)];
N = 4;
for i = N+1:length(c) - N; 
   d(i - N) = mean(c(i -N:i)); 
end
d = circshift(d, [1, N/2]);

% as seen in histogram, the three colors fall nicely into 3 peaks
figure, plot(c, '.-');
[~, clusterCenters] = findpeaks(d, 'MinPeakHeight', 1e3);

% set the threshold halfway between peaks 
boundaries = [floor((clusterCenters(2) - clusterCenters(1))/2), ...
                 clusterCenters(2) + floor((clusterCenters(3) - clusterCenters(2))/2)];
thresh1 = boundaries(1)*ones(size(g))/255;
thresh2 = boundaries(2)*ones(size(g))/255;

% categorize based on threshold
blackPixels = g < thresh1;
greenPixels = g >= thresh1 & g < thresh2;
whitePixels = g >= thresh2;

Maybe this project could help, please take a try.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!