访问opencv Mat元素的有效方法

时间:2015-06-20 23:47:43

标签: c++ opencv image-processing

我试图玩一些OpenCV并想出一个有趣的小方案来处理。

基本上,我想拍摄一个像素,从3个相邻像素中添加颜色值(所以(x,y),(x + 1,y)(x,y + 1)和(x + 1,y) +1))并将结果除以4得到平均颜色值。然后我处理的下一组像素是(x + 2,y + 2),其中有3个邻居。

然后我也希望能够做类似的事情,但是有9个像素(选择的坐标可以作为中心)。

最初我开始使用高斯模糊类型屏蔽,但这不是我想要实现的结果。从那些计算中,我只想获得1个像素值。因此输出图像将是1/4或1/9的大小。所以现在我已经让它工作了,我在字面上写了一个for循环中的计算:

for (int i = 1; i < myImage.rows -1; i++)
{
    b = 0;
    for (int k = 1; k < myImage.cols -1; k++)
    {
        //9 pixel radius
        Result.at<Vec3b>(a, b)[1] = (myImage.at<Vec3b>(i-1, k-1)[1]+myImage.at<Vec3b>(i-1, k)[1]+myImage.at<Vec3b>(i+1, k)[1] + myImage.at<Vec3b>(i, k)[1]+myImage.at<Vec3b>(i, k-1)[1]+myImage.at<Vec3b>(i, k+1)[1] + myImage.at<Vec3b>(i + 1, k+1)[1] + myImage.at<Vec3b>(i-1, k + 1)[1] + myImage.at<Vec3b>(i + 1, k - 1)[1]) / 9;
        Result.at<Vec3b>(a, b)[2] = (myImage.at<Vec3b>(i-1, k-1)[2]+myImage.at<Vec3b>(i-1, k)[2]+myImage.at<Vec3b>(i+1, k)[2] + myImage.at<Vec3b>(i, k)[2]+myImage.at<Vec3b>(i, k-1)[2]+myImage.at<Vec3b>(i, k+1)[2] + myImage.at<Vec3b>(i + 1, k+1)[2] + myImage.at<Vec3b>(i-1, k + 1)[2] + myImage.at<Vec3b>(i + 1, k - 1)[2]) / 9;
        Result.at<Vec3b>(a, b)[0] = (myImage.at<Vec3b>(i-1, k-1)[0]+myImage.at<Vec3b>(i-1, k)[0]+myImage.at<Vec3b>(i+1, k)[0] + myImage.at<Vec3b>(i, k)[0]+myImage.at<Vec3b>(i, k-1)[0]+myImage.at<Vec3b>(i, k+1)[0] + myImage.at<Vec3b>(i + 1, k+1)[0] + myImage.at<Vec3b>(i-1, k + 1)[0] + myImage.at<Vec3b>(i + 1, k - 1)[0]) / 9;

        //4 pixel radius
        //      Result.at<Vec3b>(a, b)[1] = (myImage.at<Vec3b>(i, k)[1] + myImage.at<Vec3b>(i + 1, k)[1] + myImage.at<Vec3b>(i, k + 1)[1] + myImage.at<Vec3b>(i, k - 1)[1] + myImage.at<Vec3b>(i - 1, k)[1]) / 5;
        //      Result.at<Vec3b>(a, b)[2] = (myImage.at<Vec3b>(i, k)[2] + myImage.at<Vec3b>(i + 1, k)[2] + myImage.at<Vec3b>(i, k + 1)[2] + myImage.at<Vec3b>(i, k - 1)[2] + myImage.at<Vec3b>(i - 1, k)[2]) / 5;
        //      Result.at<Vec3b>(a, b)[0] = (myImage.at<Vec3b>(i, k)[0] + myImage.at<Vec3b>(i + 1, k)[0] + myImage.at<Vec3b>(i, k + 1)[0] + myImage.at<Vec3b>(i, k - 1)[0] + myImage.at<Vec3b>(i - 1, k)[0]) / 5;
        b++;
    }
    a++;
}

显然,可以将这两个选项设置为所调用的不同功能,但我只是想知道是否有更有效的方法来实现这一目标,这将使得面具要改变。

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我假设您想要在没有内置函数的情况下执行此操作(例如resizemeanfilter2d)并且只想直接寻址图像使用at。可以进行进一步的优化,但这是对原始代码的合理且可理解的改进。

此外,应该注意的是,当图像大小不能被比例因子完全整除时,我会忽略任何额外的行/列。如果你想要不同的东西,你需要指定预期的行为。

我要做的第一件事就是改变你认为的目标像素。假设您有一个像这样的3x3社区:

1 2 3
4 5 6
7 8 9

我们无论如何都会采用所有这些像素的平均值,因此无论我们将像素5称为目标还是像素1,对结果图像都没有影响。我将调用目标像素1,因为它可以使数学更清晰。

1像素将始终位于可由缩放系数整除的坐标上。如果缩放因子为2,则1的坐标将始终为偶数。

其次,不是环绕原始图像尺寸,而是实际导致在Result多次重新计算同一个像素,我将绕过Result的维度并计算原始图像中的哪些像素对结果中的每个像素有贡献。

因此,要在原始图像中找到与结果图像中的像素(x, y)对应的邻域,我们只需要查找该邻域的像素1。由于它是缩放因子的倍数,因此它只是

(x * scaleFactor, y * scaleFactor)

最后,我们需要添加两个嵌套循环来遍历scaleFactor x scaleFactor窗口。这是避免必须输出那些长计算的部分。

例如,在上面的3x3示例中,9附近的像素(x, y)将为:

(x * scaleFactor + 2, y * scaleFactor + 2)

我也直接在矢量中进行均值计算,而不是单独执行每个通道。这意味着我们的结果将溢出uchar,因此我使用Vec3i并在分割后将其强制转换为Vec3b。在这个地方你应该考虑使用内置函数mean计算窗口的平均值,因为它将消除对这些新循环的需求。

因此,如果我们的原始图片是myImage,我们就有:

int scaleFactor = 3;

Mat Result(myImage.rows/scaleFactor, myImage.rows/scaleFactor, 
           myImage.type(), Scalar::all(0));

for (int i = 0; i < Result.rows; i++)
{
    for (int k = 0; k < Result.cols; k++)
    {
        // make sum an int vector so it can hold
        //  value = scaleFactor x scaleFactor x 255
        Vec3i areaSum = Vec3i(0,0,0);
        for (int m = 0; m < scaleFactor; m++)
        {
            for (int n = 0; n < scaleFactor; n++)
            {
                areaSum += myImage.at<Vec3b>(i*scaleFactor+m, k*scaleFactor+n);
            }
        }

        Result.at<Vec3b>(i,k) = Vec3b(areaSum/(scaleFactor*scaleFactor));
    }
}

以下是几个样本......

原件:

enter image description here

scaleFactor = 2:

enter image description here

scaleFactor = 3:

enter image description here

scaleFactor = 5:

enter image description here

相关问题