如何以自己的色调显示颜色通道?

时间:2015-02-16 17:48:57

标签: c# opencv emgucv

从图像中提取特定颜色通道时,以下代码用于EMGU CV:

EMGU.CV.UI.ImageBox originalImage;
EMGU.CV.UI.ImageBox bChannel;
...
Image<Bgra, Byte> image = new Image<Bgra, Byte>(filename);
originalImage = image;
bChannel = image[0]; // Index 0 is the blue channel (for BGRA).

不幸的是,分配给ImageBox bChannel的结果图像是灰度,不是蓝色。如何以自己的色调显示图像(在本例中为蓝色)?

最终我想提出一个类似于以下内容的用户界面:


PS:另外用&#34; OpenCV&#34;标记这个问题。标签,基于EMGU是该库的包装器的原因。 :)

2 个答案:

答案 0 :(得分:1)

这真的不是我的领域,但没有一个正确理解的人已经引起了兴趣,所以不幸的是你现在一直困扰着我。

有多种“正确”的方法可以做到这一点。您的第一次尝试,将“关闭色调”通道设置为零,简单明了,毫无疑问具有隔离颜色通道的效果,但它与沙漠拱示例完全不同。视觉效果非常不同。

为简单起见,我只讨论“隔离”红色通道。同样的原则大致适用于其他两个原则(后面会详细介绍)。

沙漠拱示例的作用大致是保留整体亮度:原始图像中的白色像素最终作为结果中的白色像素;黑色像素同样如此。 R = 128的像素应为红色(依此类推,以获得平滑的曲线)。

如何红色?

对于我们这里的目的,当我们说颜色是“红色”时,我们的意思是G和B通道是相同的,并且该值小于R.也许少一点,也许少一点。当我们在低端接近黑色时,值会向零收敛,当我们在高端接近白色时,值会收敛到255.在中间,它们发散相当大的数量(如果有规范正确金额,我希望知道比我更多的人在某个时候出现。它们越分散,你的红色就越饱和。曲线在这里看起来像这种高度复杂的技术数据可视化:

Highly sophisticated technical data visualization

下曲线描述函数f(R),其中f(0)= 0,f(255)= 255,f(128)等于小于128的某个数。如果你想要颜色是明显是红色,最好少得多。

当我数到十岁时,我脱掉了鞋子,所以我将上面的图片通过电子邮件发送给了一位坚持不懈的朋友。他提出了以下“幂律”公式:

x = 1.5
f(R) = 255 * Math.Pow(R/255, x);

...其中x是指数。如果是x = 1.0,那么f(R)=R就没用多少了。从1.52.5的x值对我有用。您可以将x调整为零以获得正确的视觉效果。

所以如果你要隔离R,

G = B = f(R)

如果您正在隔离G,

R = B = f(G)

如果您正在隔离B,

R = G = f(B)
然而,事实证明,在实践中,三个通道并不是完全对称的。粗略地说,#00ff00比#ff0000更亮,它比#0000ff更亮。从摆弄上述功能,我认为产生你的例子的算法可能会考虑到这一点。

如果我是你,我会根据您“隔离”哪个频道分别调整x,以最佳地逼近原始图像。在一些快速摆弄中,我最终使用x = 2.0表示R,x = 2.5表示G,x = 1.6表示B.

我很确定它仍然是错误的曲线,但这是正确的总体思路。根据你有多接近这个东西,你可能想要谷歌“幂律”并尝试不同的曲线功能。

答案 1 :(得分:0)

在发布我的问题之后,我意识到我可以通过将所有其他通道值设置为0(黑色)来隔离通道。这是我的代码:

Image<Bgra, Byte> img = new Image<Bgra, Byte>(filename);
Image<Bgra, Byte> blue = img.Copy();
Image<Bgra, Byte> green = img.Copy();
Image<Bgra, Byte> red = img.Copy();
Bgra tmp;
double saveGreen;
double saveRed;

for (int ii = 0; ii < img.Height; ii++)
{
    for (int jj = 0; jj < img.Width; jj++)
    {
        tmp = img[ii,jj];
        saveGreen = tmp.Green;
        saveRed = tmp.Red;

        // Blue
        tmp.Green = 0;
        tmp.Red = 0;
        blue[ii,jj] = tmp;

        // Green
        tmp.Green = saveGreen;
        tmp.Blue = 0;
        green[ii, jj] = tmp;

        // Red
        tmp.Red = saveRed;
        tmp.Green = 0;
        red[ii,jj] = tmp;
    }
}

imageBox1.Image = img;
imageBox2.Image = blue;
imageBox3.Image = green;
imageBox4.Image = red;

生成的UI如下:

如果您认为生成的频道图片看起来不正确,或者您可以考虑更快的方法而无需访问每个像素,请发布回答或评论。

编辑#1在ED PLUNKETT的评论之后:

我实现了以下用于设置通道颜色的代码:

Bgra tmp;
Bgra save;

for (int ii = 0; ii < img.Height; ii++)
{
    for (int jj = 0; jj < img.Width; jj++)
    {
        tmp = img[ii,jj];
        save = tmp;

        // Red channel.

        if (tmp.Red == 255)
        {
            tmp.Blue = tmp.Green = 255;
        }
        else
            if (tmp.Red == 0)
            {
                tmp.Blue = tmp.Green = 0;
            }
            else
                if (tmp.Red < 128)
                {
                    tmp.Blue = tmp.Green = tmp.Red / 2;
                }

        red[ii, jj] = tmp;

        // Green channel.

        tmp = save;

        if (tmp.Green == 255)
        {
            tmp.Blue = tmp.Red = 255;
        }
        else
            if (tmp.Green == 0)
            {
                tmp.Blue = tmp.Red = 0;
            }
            else
                if (tmp.Green < 128)
                {
                    tmp.Blue = tmp.Red = tmp.Green / 2;
                }

        green[ii, jj] = tmp;

        // Blue channel.

        tmp = save;

        if (tmp.Blue == 255)
        {
            tmp.Green = tmp.Red = 255;
        }
        else
            if (tmp.Blue == 0)
            {
                tmp.Green = tmp.Red = 0;
            }
            else
                if (tmp.Blue < 128)
                {
                    tmp.Green = tmp.Red = tmp.Blue / 2;
                }

        blue[ii, jj] = tmp;
    }
}

此外,我使用了沙漠图像并移动了通道,因此顺序与样本图像中的RGB顺序相匹配。这是输出:

它不完全相同(红色通道太像原始图像)但整体看起来更像是沙漠样本中使用的分割。

编辑#2在ED PLUNKETT的回答之后:

这里输出的是pow = 1.5:

pow = 2.0的输出:

pow = 2.5的输出:

粉末的输出=红色为2.0,绿色为2.5,蓝色为1.6: