如何将 RGB 图像转换为灰度图像但保留一种颜色?

发布于 2024-09-30 00:04:24 字数 1929 浏览 0 评论 0原文

我正在尝试创建一种类似于罪恶之城或其他电影的效果,它们会删除图像中除一种颜色之外的所有颜色。

我有一个 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);

I am trying to create an effect similar to Sin City or other movies where they remove all colors except one from an image.

I have an RGB image which I want to convert to grayscale but I want to keep one color.

This is my picture:

alt text

I want to keep the red color. The rest should be grayscale.

This is what my code outputs so far (you can see that the areas are correct, I don't know why they are white instead of red though):

alt text

Here is my code so far:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

日久见人心 2024-10-07 00:04:24

极大提高生成图像质量的一种选择是转换为不同的色彩空间,以便更轻松地选择颜色。特别是,HSV 颜色空间 根据色调(颜色)、饱和度定义像素颜色(颜色的量)和值(颜色的亮度)。

例如,您可以使用函数 rgb2hsv 将 RGB 图像转换为 HSV 空间,找到色调跨越您想要定义为“非红色”颜色的像素(例如,20 度到 340 度),将这些像素的饱和度设置为 0(这样它们是灰度),然后使用函数 hsv2rgb 将图像转换回 RGB 空间

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的解决方案,您可以轻松保持花朵上的浅粉色色调。还请注意,茎和地面上的棕色色调也消失了。

有关根据颜色属性从图像中选择对象的一个​​很酷的示例,您可以查看 Steve Eddins 博客文章 The Two Amigos 描述了 MathWorks 的 Brett Shoelson 提出的一种解决方案,用于从图像中提取一个“amigo”。

关于选择颜色范围的说明...

您可以做的另一件事可以帮助您选择颜色范围,那就是查看色调的直方图(即 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

这是结果像素颜色直方图:

alt text

请注意原始图像主要包含红色、绿色和黄色像素(还有一些橙色像素)的)。几乎没有青色、蓝色、靛蓝色或洋红色像素。另请注意,我在上面选择的范围(20 到 340 度)很好地排除了不属于两端两个大红色簇的大部分内容。

One option which greatly improves the quality of the resulting image is to convert to a different color space in order to more easily select your colors. In particular, the HSV color space defines pixel colors in terms of their hue (the color), saturation (the amount of color), and value (the brightness of the color).

For example, you can convert your RGB image to HSV space using the function rgb2hsv, find pixels with hues that span what you want to define as "non-red" colors (like, say, 20 degrees to 340 degrees), set the saturation for those pixels to 0 (so they are grayscale), then convert the image back to RGB space using the function 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

And here is the resulting image:

alt text

Notice how, compared to the solution from zellus, you can easily maintain the light pink tones on the flowers. Notice also that brownish tones on the stem and ground are gone as well.

For a cool example of selecting objects from an image based on their color properties, you can check out Steve Eddins blog post The Two Amigos which describes a solution from Brett Shoelson at the MathWorks for extracting one "amigo" from an image.

A note on selecting color ranges...

One additional thing you can do which can help you select ranges of colors is to look at a histogram of the hues (i.e. hPlane from above) present in the pixels of your HSV image. Here's an example that uses the functions histc (or the recommended histcounts, if available) and 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

And here's the resulting pixel color histogram:

alt text

Notice how the original image contains mostly red, green, and yellow colored pixels (with a few orange ones). There are almost no cyan, blue, indigo, or magenta colored pixels. Notice also that the ranges I selected above (20 to 340 degrees) do a good job of excluding most everything that isn't a part of the two large red clusters at either end.

黎夕旧梦 2024-10-07 00:04:24
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)

替代文本

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

∞琼窗梦回ˉ 2024-10-07 00:04:24

我真的不知道 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;
}

可能有更好的方法来检测红色并获得平均灰色,但这是一个开始;)

I don't really know how matlab works so I can't really comment on the code, but maybe this will help explain a bit how RGB colors work.

When using RGB colors a gray scale can be made by making sure the values for R,G and B are all the same. So basically what you want to do is detect if a pixel is red, when not just make R,G and B the same (you can use an average of the 3 for a rudimentary result).

Harder part is how to detect if a pixel is actually red, you can't just check if a pixel is high in the R value since it can still be another color, and a low R value can just mean a darker red.

so you could do something like this: (I don't have matlab, so assuming syntax):

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

There are probably better ways to detect red and to get an average gray, but it's a start ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文