在Android上(使用Java)对图像中的区域进行模糊处理

7
在我的Android应用中,我正在从后台服务程序中以编程方式捕获屏幕截图。 我将其作为 Bitmap 获取。
接下来,我使用以下Android框架API获取感兴趣区域(ROI)的坐标:
Rect ROI = new Rect();
viewNode.getBoundsInScreen(ROI);

这里,getBoundsInScreen() 是 Android 版本的 Javascript 函数 getBoundingClientRect()
在 Android 中,Rect 具有以下属性:
rect.top
rect.left
rect.right
rect.bottom
rect.height()
rect.width()
rect.centerX()    /* rounded off to integer */
rect.centerY()
rect.exactCenterX()    /* exact value in float */
rect.exactCenterY()

Android中的Rect对象中top、left、right和bottom是什么意思?

而在OpenCV中,Rect具有以下属性

rect.width
rect.height
rect.x    /* x coordinate of the top-left corner  */
rect.y    /* y coordinate of the top-left corner  */

现在,在执行任何与OpenCV相关的操作之前,我们需要将Android Rect转换为OpenCV Rect

了解Android中实际drawRect或绘图坐标的工作原理

有两种方法可以将Android Rect转换为OpenCV Rect(如Karl Phillip在他的答案中建议的)。两种方法生成相同的值并产生相同的结果。
/* Compute the top-left corner using the center point of the rectangle. */
int x = androidRect.centerX() - (androidRect.width() / 2);
int y = androidRect.centerY() - (androidRect.height() / 2);

// OR simply use the already available member variables:
x = androidRect.left;
y = androidRect.top;

int w = androidRect.width();
int h = androidRect.height();

org.opencv.core.Rect roi = new org.opencv.core.Rect(x, y, w, h);

现在我正在执行的OpenCV操作之一是模糊截屏中的ROI
Mat originalMat = new Mat();
Bitmap configuredBitmap32 = originalBitmap.copy(Bitmap.Config.ARGB_8888, true);
Utils.bitmapToMat(configuredBitmap32, originalMat);
Mat ROIMat = originalMat.submat(roi).clone();
Imgproc.GaussianBlur(ROIMat, ROIMat, new org.opencv.core.Size(0, 0), 5, 5);
ROIMat.copyTo(originalMat.submat(roi));

Bitmap blurredBitmap = Bitmap.createBitmap(originalMat.cols(), originalMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(originalMat, blurredBitmap);

这使我们非常接近所需的结果。接近了,但还不够。目标区域下方的区域模糊。例如,如果感兴趣的目标区域是密码字段,则上述代码会产生以下结果:左侧为Microsoft Live ROI,右侧为Pinterest ROI。

可以看到,ROI正下方的区域被模糊了。
所以我的问题是,为什么感兴趣的确切区域没有被模糊?
通过Android API getBoundsInScreen()获取的坐标似乎是正确的。
将Android Rect转换为OpenCV Rect也似乎是正确的。还是有问题吗?
模糊感兴趣区域的代码似乎也是正确的。还有其他方法可以做同样的事情吗?
请注意:我提供了实际大小的屏幕截图,因为它们正在获取。它们已经缩小了50%以适应此帖子,但除此之外,它们与我在Android设备上获取的完全相同。

我建议使用标记来调整图片大小,这样问题会更加舒适易读。 - karlphillip
@karlphillip:好主意。然而,我提供的是实际大小的屏幕截图。它们已经缩小了50%以适应此帖子,但除此之外,它们与我在Android设备上获取的完全相同。我不想错过可能导致此问题的任何上下文。 - Yash Sampat
我已经编辑了问题以改善格式。您仍然可以右键单击图像并在新标签页中打开它们,以查看完整分辨率的图像。编辑问题并观察用于调整图像大小的标记代码。它极大地提高了问题的可读性。 - karlphillip
2个回答

4

如果我没记错,OpenCV的Rect假定xy指定矩形的左上角:

/* Compute the top-left corner using the center point of the rectangle
 * TODO: take care of float to int conversion
 */
int x = androidRect.centerX() - (androidRect.width() / 2);
int y = androidRect.centerY() - (androidRect.height() / 2);

// OR simply use the already available member variables:
x = androidRect.left;
y = androidRect.top;

int w = androidRect.width();
int h = androidRect.height();

org.opencv.core.Rect roi = new org.opencv.core.Rect(x, y, w, h);

谢谢Karl的时间和努力!我还有一个问题:坐标计算似乎接近但不完全是期望的结果。请查看更新问题中的屏幕截图。我正在尝试模糊/扭曲屏幕截图中的密码字段。使用上述坐标,始终会模糊实际ROI下方的区域。而您提供的两种计算x、y的方法产生相同的结果。您能否在这方面提供一些指导?谢谢并对造成的麻烦表示抱歉.... :) - Yash Sampat
如果你在这些坐标处画一个小圆,你就能够调试裁剪计算了。我不能再帮你更多了。祝你好运! - karlphillip
谢谢你的帮助,你的工作至今已经非常有用。我会按照你的建议进行调试。我已经修改了问题并添加了一份悬赏,如果你以后有时间的话... 再次感谢!! :) - Yash Sampat
@Y.S 可能是因为 Android 图像从左下角开始,而不是像 OpenCV 一样从左上角开始。尝试使用 (10,10, 10,1000) 绘制一个矩形,并查看它在哪里绘制。如果我的假设是正确的,那么类似 coord.y = height - coord.y 的代码可能会起作用。 - hagor
@hagor 哇,这确实是一个令人费解的概念!虽然我正在考虑你的观点,但我想提醒一下,根据 这个 ,我们最初的假设似乎是正确的... :) - Yash Sampat

1
根据截图,您获取的rect.x值与OpenCV rect不同。因为Android rect中心x值是从屏幕像素密度获取的,而OpenCV rect值反映了图像像素行和列。如果您发现图像高度和原始矩阵的总行数不同,但为了完美地放置矩形,它们应该相同,因此您需要将距离乘以某个常数值以获得矩形的精确距离。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接