ImageView坐标和Bitmap像素之间的对应关系 - Android

12
在我的应用程序中,我希望用户能够选择包含在ImageView中的图像的某些内容。为了选择内容,我创建了ImageView类的子类使其实现OnTouchListener接口,在其上绘制一个由用户决定边框的矩形。
下面是绘图结果的示例(你可以将其想象成在桌面上单击并拖动鼠标的方式): enter image description here 现在我需要确定Bitmap图像的哪些像素对应于所选部分。很容易确定属于矩形的ImageView的点,但我不知道如何获取相应的像素,因为ImageView具有与原始图像不同的纵横比。
我遵循了描述的方法,尤其是这里这里,但我并不完全满意,因为我认为所做的对应是在ImageView上像素和点之间的一对一映射,没有提供所有对应于所选区域的原始图像的相应像素。
hoveredRectImageView上的矩形,其中包含的点为:
class Point {
    float x, y;
    @Override
    public String toString() {
        return x + ", " + y;
    }
}

Vector<Point> pointsInRect = new Vector<Point>();

for( int x = hoveredRect.left; x <= hoveredRect.right; x++ ){
    for( int y = hoveredRect.top; y <= hoveredRect.bottom; y++ ){

        Point pointInRect = new Point();
        pointInRect.x = x;
        pointInRect.y = y;
        pointsInRect.add(pointInRect);
    }   
}
我该如何获取包含位图图像对应像素的`Vector<Pixels> pixelsInImage`?

补充说明

我稍微解释一下我的问题背景:

我需要在所选的部分进行一些图像处理,并确保矩形中的所有像素都被处理。

图像处理将在服务器上完成,但它需要确切地知道要处理哪些像素。服务器使用具有真实尺寸的图像,Android 应用程序只通过传递包含像素坐标的向量来告诉服务器要处理哪些像素。

为什么我不喜欢上面链接中提出的解决方案:

给出的答案会以1对1的方式转换坐标。这种方法显然对于我的任务是无效的,因为某个大小的 ImageView 上的50个点的区域可能与真实图像中相同数量的像素的区域不对应,而应该考虑不同的长宽比。

例如,如果图像比应用程序中显示的 ImageView 小,则应选择以下区域:

enter image description here


我不确定我明白为什么你不使用第一个链接中的方法。即使假设像素比点要多,它也会给出最近的像素。我猜你可以把它们改成浮点数来得到亚像素值,但我不知道它怎么能更接近了。也许我没有理解问题的关键点? - Geobits
我需要对所选部分进行一些图像处理,并确保矩形中的所有像素都得到处理。图像处理将在服务器上进行,但它需要确切地知道要处理哪些像素。服务器使用真实尺寸的图像进行处理,Android应用程序只告诉服务器要处理哪些像素。 - Matteo
我认为,如果您想获取所有像素,可以像Geobits所说的那样仅计算点并使用floor或ceil进行四舍五入。然后只向服务器发送这4个点。服务器可以循环并生成像素数组,这是您不应该在客户端上执行的操作,除非您必须在客户端上使用它。 - User
这只涉及到您确保大图像矩形内的所有像素也包含在小图像内的要求。因此对于左侧或顶部的坐标,使用 floor 进行舍入,对于右侧或底部的坐标,使用 ceil 进行舍入。floor 向最低整数舍入,ceil 向最高整数舍入。这样,由于四舍五入,您不会丢失矩形区域内的任何像素。您总是获得一个面积相等或更大的区域。 - User
但是刚开始不用考虑 floor 和 ceil,只需要将其四舍五入到最低的整数即可(转换为 int 类型已经实现了此功能)。 - User
显示剩余3条评论
3个回答

15

Matteo,

这似乎更多是一个关于您可以(主观上)容忍哪些像素发送到服务器的错误数量的问题。事实仍然是,对于任何不得出好整数的纵横比,您必须决定向哪个方向“推”选框。

您链接的解决方案是完全可行的解决方案。您必须问自己:如果我处理的图像与屏幕上显示的选择框相差一个像素,用户是否会注意到?我的猜测可能不会。我无法想象用户在用手指在触摸屏上选择矩形时会有那种像素精度:D

既然如此,我只需让发生在整数转换时的 floor() 处理来处理您最终传递给服务器的像素。

我们来看一个例子。

让我们定义我们 ImageView 和 Bitmap 的宽度和高度为:

ImageViewWidth = 400, ImageViewHeight = 150
BitmapWidth = 176, BitmapHeight = 65

然后,您将用于在它们之间转换选择框的纵横比将是:

WidthRatio = BitmapWidth / ImageViewWidth = 175 / 400 = 0.44
HeightRatio = BitmapHeight / ImageViewHeight = 65 / 150 = 0.44

一些好看的丑陋数字。不管我在ImageView上的像素位置如何,它都会对应到Bitmap中的一个像素,如下所示:

BitmapPixelX = ImageViewPixelX * WidthRatio
BitmapPixelY = ImageViewPixelY * HeightRatio

现在,我将这个位图放在ImageView上供用户选择一个矩形,用户在ImageView中使用左上角和右下角坐标选择一个矩形如下:

RectTopLeftX = 271, RectTopLeftY = 19
RectBottomRightX = 313, RectBottomRightY = 42

如何确定这些像素在位图中对应哪些像素?很简单,我们之前确定的比例。现在让我们先来看看左上角的坐标。

RectTopLeftX * WidthRatio = 271 * .44 = 119.24
RectTopLeftY * HeightRatio = 19 * .44 = 8.36
对于RectTopLeftX,我们发现自己位于BitmapPixelX值为119的位置,并且大约在像素的四分之一处。如果我们对该值进行floor(),并且对应的BitmapPixelY值为8.36,则将像素(119,8)发送到服务器进行处理。如果我们对这些值进行ceil(),则将像素(120,9)发送到服务器进行处理。这是完全由您决定的部分。 您几乎总是会降落在某个像素的小数部分上。您是否发送您降落的像素或其旁边的像素取决于您自己。我会说,这对用户来说完全无法察觉,所以再次强调,将在转换为整数时发生的floor()处理好就可以了。
希望有所帮助!

[编辑]

仔细阅读问题后,我认为更好地理解了您的疑问/困惑。我将使用上面的示例加以说明。
您说Bitmap中有176个像素,ImageView中有400个像素。因此,从一个映射到另一个的映射不是1:1的,这将在确定要提取哪些像素进行处理时导致问题。
但实际上不会!当您将ImageView中的矩形边界的坐标转换为Bitmap中的坐标时,您只是在给出 Bitmap中要迭代的像素的范围。这不是每个ImageView像素如何映射到相应的Bitmap像素的描述。
希望这能澄清您对问题的困惑。

我真的很喜欢你的回答!它非常有用,特别是关于我们困惑的最后一句话... ;) 非常感谢你花时间理解我的观点!你应该得到奖励 ;D - Matteo

2
这可以通过简单的数学计算来解决,你的矩形有4个点p1、p2、p3、p4,它们与ImageView的坐标系相关联,表示为x、y。
同时你也知道原始图像的尺寸。
现在如果p1.x的值为50,在一个宽度为200像素的ImageView中,你可以得到50 / 200 = 0.25的比例关系。
如果原始图像的宽度为100像素,则点p1.x将位于100 * 0.25 = 25像素处。
这可以表示为:

50 -> 200

? -> 100

计算式为:

? = 100 * 50 / 200

对于每个维度x、y,您需要为每个点执行此操作,然后您将得到原始图像中矩形的4个点。
现在如果使用这些计算出的点迭代,您应该能够获得原始图像中矩形的像素。

谢谢回答,但这与我之前提到的问题基本相同。我担心这些解决方案存在两个问题:1)当像素除法不产生整数结果时(例如,像素100 * 0.33 = 像素??如果宽高比为1/3),2)在较小图像中选择一个部分以包含较大图像中的相同部分时,必须在大图像中有超过50个像素,所以一对一的对应是不正确的。如果需要更好地解释,请告诉我。 - Matteo
第一个问题:使用float和double计算所有内容,并将原始图像中的坐标四舍五入到最近的整数(这可能会有点不准确,但看起来您的应用可以容忍这种情况?)。第二个问题:像素之间不是1:1的,如果是这样的话,您将在原始图像和ImageView中获得相同大小的矩形,这是没有意义的。 - User
请检查我的问题更新!我会考虑你说的...谢谢;D - Matteo

0

我在这里假设您已经将某些内容放置在图像视图中。请在onCreate方法内完成以下操作:

ImageView image=(ImageView) findViewById(R.id.imageView1);
TextView coordinates=(TextView) findViewById(R.id.textView1);
bitmap = ((BitmapDrawable)Map.getDrawable()).getBitmap();

而这个在onTouch内部:

coordinates.setText("Touch coordinates:"+String.valueOf(event.getX())+"x"+String.valueOf(event.getY()));
int pixel = bitmap.getPixel((int)event.getX(), (int)event.getY());

现在你已经拥有了你想要的特定像素。


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