2010-10-31 10 views
33

Próbuję stworzyć efekt podobny do Sin City lub innych filmów, w których usunięto wszystkie kolory poza jednym z obrazka.Jak przekonwertować obraz RGB na skalę szarości, ale zachować jeden kolor?

Mam obraz RGB, który chcę przekonwertować na skalę szarości, ale chcę zachować jeden kolor.

To jest mój obraz:

alt text

chcę zachować kolor czerwony. Reszta powinna być w skali szarości.

To właśnie mój kod wyprowadza tak daleko (widać, że obszary te są poprawne, nie wiem dlaczego oni są białe zamiast czerwonego choć):

alt text

Oto mój kod do tej pory:

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); 
+0

Wydaje Matlab jest dostarczenie rozwiązania, ale byłoby interesujące zobaczyć golf kod ... – gary

Odpowiedz

18
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

+0

Dzięki, o wiele łatwiej jest to zrobić. Jak jednak uzyskać te współczynniki (0,3, 0,59, 0,11)? Nie rozumiem tego. –

+3

@Richard Knop: To formuła używana przez [RGB2GRAY] (http://www.mathworks.com/help/toolbox/images/ref/rgb2gray.html), zgodnie z listą w dokumentacji. – gnovice

+0

@ Richard Knop: http://www.mathworks.com/help/toolbox/images/ref/rgb2gray.html przewiń nieco w dół, aż osiągniesz akapit * "Algorytm" *. Ale są też inne w sieci. – zellus

2

ja naprawdę nie wiem, jak Matlab działa więc nie mogę wypowiedzieć się na temat kodu, ale może to pomoże wyjaśnić co nieco jak działają kolory RGB.

Podczas korzystania z kolorów RGB można uzyskać skalę szarości, upewniając się, że wartości dla R, G i B są takie same. Zasadniczo więc chcesz wykryć, czy piksel jest czerwony, gdy nie tylko sprawiają, że R, G i B są takie same (możesz użyć średniej z 3 dla podstawowego wyniku).

Trudniejszą częścią jest jak wykryć, czy piksel jest rzeczywiście czerwony, nie można po prostu sprawdzić, czy piksel ma wysoką wartość R, ponieważ wciąż może być inny kolor, a niska wartość R może oznaczać ciemniej czerwony.

więc można zrobić coś takiego: (nie mam matlab, więc zakładając składnię):

 
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; 
} 

Są pewnie lepsze sposoby wykrywania czerwony i uzyskać siwe, ale jest to start;)

+0

Dzięki. Zmieniłem trochę swój kod i już otrzymałem wynik, ale obszary, które powinny być czerwone, są białe. Sprawdź moje zaktualizowane pytanie. –

+0

Twoje kolory są białe, ponieważ usunięto oryginalne wartości dla koloru zielonego i niebieskiego dla pikseli, które chcesz zachować. Dlatego w tym przykładzie modyfikuje tylko matrycę dla pikseli, które mają być szare i pozostawia resztę w spokoju. – Doggett

+0

Rodzaj zaimplementowanego algorytmu bez zauważania postu. Mam nadzieję, że nie masz nic przeciwko. – zellus

82

Jedną z opcji, która znacznie poprawia jakość uzyskanego obrazu, jest zamiana na inną przestrzeń kolorów w celu łatwiejszego wybierania kolorów. W szczególności, HSV color space definiuje kolory pikseli w zakresie ich odcienia (koloru), nasycenia (ilość koloru) i wartości (jasność koloru).

Można na przykład przekonwertować obraz RGB na przestrzeń HSV za pomocą funkcji rgb2hsv, wyszukać piksele o odcieniach, które mają być zdefiniowane jako "inne niż czerwone" (np. Od 20 do 340 stopni) ustawić nasycenie tych pikseli 0 (a więc są w skali szarości), a następnie konwertować obraz z powrotem do przestrzeni RGB przy użyciu funkcji 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 

A oto obraz wynikowy:

alt text

Zauważ, jak w porównaniu z the solution from zellus możesz z łatwością utrzymać jasne odcienie na kwiatach. Zauważ również, że brązowawe tony na łodydze i ziemi również zniknęły.

Aby zobaczyć fajny przykład wyboru obiektów z obrazka na podstawie jego właściwości kolorów, możesz zapoznać się z postem Steve'a Eddinsa na blogu The Two Amigos opisującym rozwiązanie Bretta Shoelsona z MathWorks do wyodrębniania jednego "amigo" z obrazu.


Uwaga na doborze zakresów kolorów ...

Jeden dodatkowy, co można zrobić, które mogą pomóc Ci wybrać zakresy kolorów jest, aby spojrzeć na histogram barwy (tj hPlane z góry) obecne w pikselach twojego obrazu HSV. Oto przykład, który używa funkcji histc (lub zalecanego histcounts, jeśli są dostępne) i 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 

A oto powstały histogram kolor piksela:

alt text

Wskazówki jak oryginalny obraz zawiera głównie czerwone, zielone i żółte piksele (z kilkoma pomarańczowymi). Nie ma prawie żadnych pikseli w kolorze cyjan, niebieskim, indygo lub w kolorze karmazynowym. Zauważ również, że zakresy wybrane powyżej (od 20 do 340 stopni) dobrze sobie radzą z wykluczeniem większości wszystkiego, co nie jest częścią dwóch dużych czerwonych klastrów po obu stronach.

+0

Dzięki. Spróbuję tego. Tymczasem zaktualizowałem moje pytanie. Mógłbyś to sprawdzić? :) –

+3

+1 za docenienie twojego rozwiązania. Lepszy wynik i kod. – zellus

+0

Histogramy FTW. – Spike0xff