我试图玩一些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++;
}
显然,可以将这两个选项设置为所调用的不同功能,但我只是想知道是否有更有效的方法来实现这一目标,这将使得面具要改变。
感谢您的帮助!
答案 0 :(得分:0)
我假设您想要在没有内置函数的情况下执行此操作(例如resize
,mean
或filter2d
)并且只想直接寻址图像使用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));
}
}
以下是几个样本......
原件:
scaleFactor = 2:
scaleFactor = 3:
scaleFactor = 5: