Как преобразовать изображение RGB в оттенки серого, но сохранить один цвет?

Я пытаюсь создать эффект, похожий на Город грехов или другие фильмы, в которых удаляются все цвета, кроме одного, из изображения.

У меня есть изображение RGB, которое я хочу преобразовать в оттенки серого, но я хочу сохранить один цвет.

Это моя картина:

alt text

Я хочу сохранить красный цвет. Остальное должно быть в оттенках серого.

Вот что выводит мой код (вы можете видеть, что области правильные, хотя я не знаю, почему они белые, а не красные):

alt text

Вот мой код:

filename = 'roses.jpg';

[cdata,map] = imread( filename );
% convert to RGB if it is indexed image
if ~isempty( map ) 
   cdata = idx2rgb( cdata, map ); 
end

%imtool('roses.jpg');

imWidth = 685;
imHeight = 428;

% RGB ranges of a color we want to keep
redRange = [140 255];
greenRange = [0 40];
blueRange = [0 40];

% RGB values we don't want to convert to grayscale
redToKeep = zeros(imHeight, imWidth);
greenToKeep = zeros(imHeight, imWidth);
blueToKeep = zeros(imHeight, imWidth);

for x=1:imWidth

    for y=1:imHeight

        red = cdata( y, x, 1 );
        green = cdata( y, x, 2 );
        blue = cdata( y, x, 3 );

        if (red >= redRange(1) && red <= redRange(2) && green >= greenRange(1) && green <= greenRange(2) && blue >= blueRange(1) && blue <= blueRange(2))
            redToKeep( y, x ) = red;
            greenToKeep( y, x ) = green;
            blueToKeep( y, x ) = blue;
        else
            redToKeep( y, x ) = 999;
            greenToKeep( y, x ) = 999;
            blueToKeep( y, x ) = 999;
        end

    end 

end 

im = rgb2gray(cdata);
[X, map] = gray2ind(im);
im = ind2rgb(X, map);

for x=1:imWidth

    for y=1:imHeight

        if (redToKeep( y, x ) < 999)
            im( y, x, 1 ) = 240;
        end
        if (greenToKeep( y, x ) < 999)
            im( y, x, 2 ) = greenToKeep( y, x );
        end
        if (blueToKeep( y, x ) < 999)
            im( y, x, 3 ) = blueToKeep( y, x );
        end

    end 

end 

imshow(im);

person Richard Knop    schedule 31.10.2010    source источник
comment
Кажется, что Matlab предоставляет решение, но было бы интересно увидеть гольф-код этого ...   -  person gary    schedule 01.11.2010


Ответы (3)


figure
pic = imread('EcyOd.jpg');

for mm = 1:size(pic,1)
    for nn = 1:size(pic,2)
        if pic(mm,nn,1) < 80 || pic(mm,nn,2) > 80 || pic(mm,nn,3) > 100
            gsc = 0.3*pic(mm,nn,1) + 0.59*pic(mm,nn,2) + 0.11*pic(mm,nn,3);
            pic(mm,nn,:) = [gsc gsc gsc];
        end
    end
end
imshow(pic)

alt text

person zellus    schedule 31.10.2010
comment
Спасибо, это намного проще. Но как вы получили эти коэффициенты (0,3, 0,59, 0,11)? Я этого не понимаю. - person Richard Knop; 31.10.2010
comment
@Richard Knop: Эта формула используется RGB2GRAY, поскольку перечислено в документации. - person gnovice; 31.10.2010
comment
@ Ричард Кноп: mathworks.com/help/toolbox/images/ref/ rgb2gray.html прокрутите немного вниз, пока не дойдете до пункта «Алгоритм». Но в сети есть и другие. - person zellus; 31.10.2010
comment
Re: (0.3, 0.59, 0.11) Человеческая зрительная система гораздо более чувствительна к зеленому, чем к красному, более чувствительна к красному, чем к синему. Эти числа являются приближением отклика HVS в видимом спектре. [en.wikipedia.org/wiki/Spectral_sensitivity] - person David Poole; 23.10.2011

Один из вариантов, который значительно улучшает качество результирующего изображения, - это преобразование в другое цветовое пространство, чтобы было легче выбирать цвета. В частности, цветовое пространство HSV определяет цвета пикселей с точки зрения их оттенка (цвета), насыщенности (количество цвета) и значение (яркость цвета).

Например, вы можете преобразовать изображение RGB в пространство HSV с помощью функции _1 _ найдите пиксели с оттенками, которые охватывают то, что вы хотите определить как «не красные» цвета (например, от 20 до 340 градусов), установите для этих пикселей насыщенность 0 (чтобы они были в оттенках серого), затем преобразуйте изображение обратно в пространство RGB с помощью функции hsv2rgb:

cdata = imread('EcyOd.jpg');       % Load image
hsvImage = rgb2hsv(cdata);         % Convert the image to HSV space
hPlane = 360.*hsvImage(:, :, 1);   % Get the hue plane scaled from 0 to 360
sPlane = hsvImage(:, :, 2);        % Get the saturation plane
nonRedIndex = (hPlane > 20) & ...  % Select "non-red" pixels
              (hPlane < 340);
sPlane(nonRedIndex) = 0;           % Set the selected pixel saturations to 0
hsvImage(:, :, 2) = sPlane;        % Update the saturation plane
rgbImage = hsv2rgb(hsvImage);      % Convert the image back to RGB space

И вот получившееся изображение:

alt text

Обратите внимание, как по сравнению с решением от zellus вы можете легко сохранить светло-розовые тона на цветах. Также обратите внимание, что коричневатые тона на стебле и земле также исчезли.

Классный пример выбора объектов на изображении на основе их цветовых свойств можно найти в записи блога Стива Эддинса Два амиго, в котором описывается решение Бретта Шулсона из MathWorks для извлечения одного« амиго »из изображения.


Примечание о выборе цветовых диапазонов ...

Еще одна вещь, которую вы можете сделать, чтобы помочь вам выбрать диапазоны цветов, - это посмотреть на гистограмму оттенков (т.е. hPlane сверху), присутствующих в пикселях вашего изображения HSV. Вот пример, в котором используются функции histc (или рекомендуемый _ 6_, если доступно) и _ 7_:

binEdges = 0:360;    % Edges of histogram bins
hFigure = figure();  % New figure

% Bin pixel hues and plot histogram:
if verLessThan('matlab', '8.4')
  N = histc(hPlane(:), binEdges);  % Use histc in older versions
  hBar = bar(binEdges(1:end-1), N(1:end-1), 'histc');
else
  N = histcounts(hPlane(:), binEdges);
  hBar = bar(binEdges(1:end-1), N, 'histc');
end

set(hBar, 'CData', 1:360, ...            % Change the color of the bars using
          'CDataMapping', 'direct', ...  %   indexed color mapping (360 colors)
          'EdgeColor', 'none');          %   and remove edge coloring
colormap(hsv(360));                      % Change to an HSV color map with 360 points
axis([0 360 0 max(N)]);                  % Change the axes limits
set(gca, 'Color', 'k');                  % Change the axes background color
set(hFigure, 'Pos', [50 400 560 200]);   % Change the figure size
xlabel('HSV hue (in degrees)');          % Add an x label
ylabel('Bin counts');                    % Add a y label

А вот итоговая гистограмма цвета пикселей:

alt text

Обратите внимание, что исходное изображение содержит в основном пиксели красного, зеленого и желтого цветов (с несколькими оранжевыми). Пикселей голубого, синего, индиго или пурпурного цвета практически нет. Также обратите внимание, что диапазоны, которые я выбрал выше (от 20 до 340 градусов), хорошо исключают почти все, что не является частью двух больших красных кластеров на обоих концах.

person gnovice    schedule 31.10.2010
comment
Спасибо. Я попробую это. Тем временем я обновил свой вопрос. Не могли бы вы это проверить? :) - person Richard Knop; 31.10.2010
comment
+1 за то, что оценили ваше решение. Превосходный результат и код. - person zellus; 02.11.2010

Я действительно не знаю, как работает Matlab, поэтому я не могу комментировать код, но, возможно, это поможет немного объяснить, как работают цвета RGB.

При использовании цветов RGB можно создать шкалу серого, убедившись, что значения для R, G и B одинаковы. Итак, в основном то, что вы хотите сделать, - это определить, является ли пиксель красным, а не просто сделать R, G и B одинаковыми (вы можете использовать среднее значение 3 для элементарного результата).

Более сложная часть состоит в том, как определить, действительно ли пиксель красный, вы не можете просто проверить, имеет ли пиксель высокое значение R, поскольку он все еще может быть другого цвета, а низкое значение R может просто означать более темный красный цвет.

чтобы вы могли сделать что-то вроде этого: (у меня нет Matlab, поэтому предполагаю синтаксис):

red = cdata( y, x, 1 );
green = cdata( y, x, 2 );
blue = cdata(y, x, 3);

if (red < (blue * 1.4) || red < (green * 1.4) )
{
    avg = (red + green + blue) / 3;
    cdata(y, x, 1) = avg;
    cdata(y, x, 2) = avg;
    cdata(y, x, 3) = avg;
}

Вероятно, есть более эффективные способы обнаружить красный цвет и получить средний серый цвет, но это только начало;)

person Doggett    schedule 31.10.2010
comment
Спасибо. Я немного изменил свой код, и у меня уже есть результат, но области, которые должны быть красными, вместо этого становятся белыми. Проверьте мой обновленный вопрос. - person Richard Knop; 31.10.2010
comment
Ваши цвета белые, потому что вы удалили исходные значения зеленого и синего для пикселей, которые хотите сохранить. Вот почему в примере он изменяет матрицу только для пикселей, которые вы хотите сделать серыми, а остальные просто не трогает. - person Doggett; 31.10.2010
comment
Вроде реализовал ваш алгоритм, не заметив вашего поста. Надеюсь, ты не против. - person zellus; 31.10.2010
comment
@Zellus Вовсе нет, я вижу, что мое полное незнание Matlab в любом случае сделало мой пример немного длиннее, чем необходимо;) - person Doggett; 31.10.2010