¿Cómo puedo convertir una imagen RGB a escala de grises pero conservar un color?

Resuelto Richard Knop asked hace 14 años • 3 respuestas

Estoy intentando crear un efecto similar a Sin City u otras películas donde eliminan todos los colores excepto uno de una imagen.

Tengo una imagen RGB que quiero convertir a escala de grises pero quiero conservar un color.

Esta es mi foto:

texto alternativo

Quiero mantener el color rojo. El resto debería ser en escala de grises.

Esto es lo que genera mi código hasta ahora (puedes ver que las áreas son correctas, aunque no sé por qué son blancas en lugar de rojas):

texto alternativo

Aquí está mi código hasta ahora:

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);
Richard Knop avatar Oct 31 '10 23:10 Richard Knop
Aceptado

Una opción que mejora enormemente la calidad de la imagen resultante es convertir a un espacio de color diferente para poder seleccionar más fácilmente los colores. En particular, el espacio de color HSV define los colores de los píxeles en términos de su tono (el color), saturación (la cantidad de color) y valor (el brillo del color).

Por ejemplo, puede convertir su imagen RGB a espacio HSV usando la función rgb2hsv, buscar píxeles con tonos que abarquen lo que desea definir como colores "no rojos" (como, por ejemplo, de 20 grados a 340 grados), configurar la saturación para esos píxeles a 0 (para que sean escala de grises), luego convierta la imagen nuevamente al espacio RGB usando la función 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

Y aquí está la imagen resultante:

texto alternativo

Observa cómo, en comparación con la solución de zellus , puedes mantener fácilmente los tonos rosa claro de las flores. Observe también que los tonos marrones en el tallo y el suelo también han desaparecido.

Para ver un buen ejemplo de selección de objetos de una imagen en función de sus propiedades de color, puede consultar la publicación del blog de Steve Eddins, The Two Amigos , que describe una solución de Brett Shoelson en MathWorks para extraer un "amigo" de una imagen.


Una nota sobre la selección de gamas de colores...

Una cosa adicional que puede hacer y que puede ayudarle a seleccionar rangos de colores es mirar un histograma de los tonos (es decir, hPlanedesde arriba) presentes en los píxeles de su imagen HSV. A continuación se muestra un ejemplo que utiliza las funciones histc(o las recomendadas histcounts, si están disponibles) y bar:

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

Y aquí está el histograma de color de píxeles resultante:

texto alternativo

Observe cómo la imagen original contiene principalmente píxeles de colores rojos, verdes y amarillos (con algunos de color naranja). Casi no hay píxeles de color cian, azul, índigo o magenta. Observe también que los rangos que seleccioné anteriormente (20 a 340 grados) hacen un buen trabajo al excluir casi todo lo que no forma parte de los dos grandes grupos rojos en cada extremo.

gnovice avatar Oct 31 '2010 17:10 gnovice
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)

texto alternativo

zellus avatar Oct 31 '2010 17:10 zellus

Realmente no sé cómo funciona Matlab, así que no puedo comentar sobre el código, pero tal vez esto ayude a explicar un poco cómo funcionan los colores RGB.

Cuando se utilizan colores RGB, se puede crear una escala de grises asegurándose de que los valores de R, G y B sean todos iguales. Básicamente, lo que quieres hacer es detectar si un píxel es rojo, cuando no simplemente hacer que R, G y B sean iguales (puedes usar un promedio de 3 para obtener un resultado rudimentario).

La parte más difícil es cómo detectar si un píxel es realmente rojo; no se puede simplemente verificar si un píxel tiene un valor R alto, ya que todavía puede ser de otro color, y un valor R bajo puede simplemente significar un rojo más oscuro.

entonces podrías hacer algo como esto: (no tengo matlab, así que asumiendo la sintaxis):

rojo = cdata( y, x, 1 );
verde = cdata( y, x, 2 );
azul = cdata(y, x, 3);

if (rojo < (azul * 1.4) || rojo < (verde * 1.4) )
{
    promedio = (rojo + verde + azul) / 3;
    cdata(y, x, 1) = promedio;
    cdata(y, x, 2) = promedio;
    cdata(y, x, 3) = promedio;
}

Probablemente haya mejores formas de detectar el rojo y obtener un gris promedio, pero es un comienzo;)

Doggett avatar Oct 31 '2010 17:10 Doggett