OpenCV:计算新的红色像素值

时间:2014-10-21 06:49:35

标签: c++ opencv image-processing pixel

我目前的目标是调整图像中的红色像素(更具体地说,是用于消除闪光引起的红眼的眼睛区域),这很有效,但我有时会遇到这个问题皮肤上出现绿色斑块。

这是一个很好的结果(之前和之后):

我意识到为什么会发生这种情况,但是当我将阈值调整到更高的值(意味着红色强度必须更强)时,更少的红色像素被拾取和更改,即:

阈值越低,皮肤上出现的绿色就越多。

我想知道我目前在改变红色像素方面是否有其他方法?

int lcount = 0;
for(int y=0;y<lcroppedEye.rows;y++)
{
    for(int x=0;x<lcroppedEye.cols;x++)
    {
        double b = lcroppedEye.at<cv::Vec3b>(y, x)[0];
        double g = lcroppedEye.at<cv::Vec3b>(y, x)[1];
        double r = lcroppedEye.at<cv::Vec3b>(y, x)[2];

        double redIntensity = r / ((g + b) / 2);

        //currently causes issues with non-red-eye images
        if (redIntensity >= 1.8)
        {
            double newRedValue = (g + b) / 2;
            cv::Vec3b pixelColor(newRedValue,g,b);
            lroi.at<cv::Vec3b>(cv::Point(x,y)) = pixelColor;
            lcount++;
        }
    }
}

编辑:我可以添加一个检查以确保新的RGB值足够低,因此R,G,B值是相似/接近的值,因此写出黑色/灰色像素只有...或者有一系列不允许的RGB值(绿色)...会起作用吗?

5 个答案:

答案 0 :(得分:3)

改变

可能更好
double redIntensity = r / ((g + b) / 2);

double redIntensity = r / ((g+b+1) / 2);

因为g + b可以等于0,你就会得到NAN。

另请参阅cv :: floodfill方法。

答案 1 :(得分:3)

调整RGB空间中的颜色需要注意您所面临的绿色区域。将R,G,B值转换为更好的色彩空间,如HSV或LUV。

我建议你去HSV检测并改变红眼颜色。 R /(G + B)不是计算红色强度的好方法。这意味着你正在调用(R = 10,G = 1,B = 0)一种非常红的颜色,但它是致命的黑色。看看下面的比较:

colors

因此,您最好检查饱和度和值是否为高值,红眼颜色就是这种情况。如果遇到其他高强度颜色,可以检查Hue是否在[0-20]和[340-359]之间。但是如果没有这个,你仍然可以安全地对抗白色本身,因为它具有非常低的饱和度并且你无论如何都不会选择白色区域。

那是为了选择,为了改变颜色,最好不要使用RGB,因为当我们感知颜色时,那个空间的变化不是线性的。看看上面的图像,你可以看到降低饱和度和值都是一个好的开始。但是你可以尝试一下,看看看起来更好看。也许你总是可以使用深灰色,这意味着将饱和度设置为零,并将值降低一点。您可能会认为深棕色会更好,但要求低饱和度和值,但将Hue设置为大约30度。

可能对您有帮助的参考资料:

  1. Converting color values in OpenCV
  2. An online tool to experiment with RGB and HSV colors

答案 2 :(得分:1)

  • 准备一个与你的lcroppedEye图像大小相同的面具,这个图像最初都是黑色的(我在这里将这个图像称为maskImage)。
  • 对于通过(redIntensity&gt; = 1.8)条件的lcroppedEye(row,col)中的每个像素,将maskImage(row,col)像素设置为白色。
  • 完成lcroppedEye中的所有像素后,maskImage将使所有类似红眼的像素都为白色。
  • 如果您对此maskImage执行连接组件分析,则应该可以通过考虑圆形或类似磁盘的功能等来过滤掉其他区域。
  • 现在您可以使用此maskImage作为蒙版将颜色转换应用于原始图像的ROI

(在继续进行连通分量分析之前,您可能需要对maskImage进行一些预处理。此外,您可以使用split,divide和threshold函数替换问题中的代码段,除非有特殊原因迭代像素)

答案 3 :(得分:1)

一旦红色区域中的颜色信息被额外的红色值扭曲太多,可能最好忽略红色区域的颜色信息。所以新的价值观可能是:

newRedValue =(g + b)/ 2; newGreenValue = newRedValue; newBlueValue = newRedValue;

即使您检测到错误的红色区域,其饱和度也会比绿色区域产生更好的效果。 您还可以使用morphological closing operations(使用圆形结构元素)来避免红色区域遮罩中的间隙。因此,您需要执行3个步骤:1。找到红色区域并为此创建遮罩2.执行红色区域遮罩形态关闭操作3.使用此遮罩去饱和图像

是的,不要使用&#34; r /((g + b)/ 2)&#34;因为它可以导致除零错误。

答案 4 :(得分:1)

问题似乎是无论是否存在任何红眼都会替换,所以你必须以某种方式测试是否有任何高红色值(比你的皮肤更红)。
我猜测那里有反射的区域也会有特定的蓝色和绿色值,无论是高值还是低值都要检查,以便你需要高红色值和低蓝色和/或低绿色值。

// first pass, getting the highest red value
int highRed = 0;
cv::Point redPos = cv::Point(0,0);

int lcount = 0;
for(int y=0;y<lcroppedEye.rows;y++)
{
    for(int x=0;x<lcroppedEye.cols;x++)
    {
        double r = lcroppedEye.at<cv::Vec3b>(y, x)[2];

        if (redIntensity > highRed)
        {
            highRed = redIntensity ;
            redPos = cv::Point(x,y);
        }
    }
}

// decide if its red enough, need to find a good minRed value.
if (highRed < minRed)
  return;

此处的原始代码包含以下更改。

    // avoid division by zero, code from @AndreySmorodov
    double redIntensity = r / ((g+b+1) / 2);

    // add check for actual red colour.
    if (redIntensity >= 1.8 && r > highRed*0.75) 
    // potential add check for low absolute r/b values.
    {
        double newRedValue = (g + b) / 2;
        cv::Vec3b pixelColor(newRedValue,g,b);
        lroi.at<cv::Vec3b>(cv::Point(x,y)) = pixelColor;
        lcount++;
    }
}