Я пытаюсь создать эффект, подобный Sin City или другим фильмам, где они удаляют все цвета, кроме одного из изображения.
У меня есть изображение RGB, которое я хочу преобразовать в оттенки серого, но хочу сохранить один цвет.
Это моя фотография:
Я хочу сохранить красный цвет. Остальное должно быть в оттенках серого.
Это то, что выводит мой код до сих пор (вы можете видеть, что области верны, я не знаю, почему они белые, а не красные):
Вот мой код:
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);
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)
Один из вариантов, который значительно улучшает качество результирующего изображения, - это преобразование в другое цветовое пространство, чтобы легче выбирать цвета. В частности, цветовое пространство HSV определяет пиксельные цвета с точки зрения их оттенка (цвета), насыщенности (количества цвета) и значения (яркость цвета).
Например, вы можете преобразовать изображение RGB в пространство HSV, используя функцию rgb2hsv
, найти пиксели с оттенками, которые охватывают то, что вы хотите определить как "не красные" цвета (например, от 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
И вот получившееся изображение:
Обратите внимание, что по сравнению с решением от zellus вы можете легко поддерживать светло-розовые тона на цветах. Обратите также внимание на то, что коричневатые тона на стебле и почве также исчезли.
Для прохладного примера выбора объектов из изображения, основанного на их свойствах цвета, вы можете проверить сообщение блога Стива Эддинса The Two Amigos который описывает решение от Бретта Шоелсона в MathWorks для извлечения одного "амиго" из изображения.
Заметка о выборе цветовых диапазонов...
Еще одна вещь, которую вы можете сделать, которая может помочь вам выбрать диапазоны цветов, - это посмотреть на гистограмму оттенков (т.е. hPlane
сверху), присутствующих в пикселях вашего изображения HSV. Вот пример, который использует функции histc
(или рекомендуемый histcounts
, если доступно) и 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
И вот итоговая гистограмма цвета пикселя:
Обратите внимание, что исходное изображение содержит в основном красные, зеленые и желтые цветные пиксели (с несколькими оранжевыми). Почти нет голубых, синих, индиго или пурпурных цветных пикселей. Обратите внимание также, что диапазоны, выбранные мной выше (от 20 до 340 градусов), делают хорошую работу по исключению всего всего, что не является частью двух больших красных кластеров с обоих концов.
Я не знаю, как работает 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; }
Есть, вероятно, лучшие способы обнаружить красный цвет и получить средний серый цвет, но это начало;)