20

In my app I need to let users to check the eyes at some photo. In OnTouchListener.onTouch(...) I get the coordinates of the ImageView.

How can I convert this coordinates to the point at the bitmap that was touched?

Alex
  • 1,319
  • 3
  • 15
  • 33

7 Answers7

34

this works for me at least with API 10+:

final float[] getPointerCoords(ImageView view, MotionEvent e)
{
    final int index = e.getActionIndex();
    final float[] coords = new float[] { e.getX(index), e.getY(index) };
    Matrix matrix = new Matrix();
    view.getImageMatrix().invert(matrix);
    matrix.postTranslate(view.getScrollX(), view.getScrollY());
    matrix.mapPoints(coords);
    return coords;
}

akonsu
  • 28,824
  • 33
  • 119
  • 194
31

Okay, so I've not tried this, but giving it a bit of thought, here's what I've got as a suggestion:

ImageView imageView = (ImageView)findViewById(R.id.imageview);
Drawable drawable = imageView.getDrawable();
Rect imageBounds = drawable.getBounds();

//original height and width of the bitmap
int intrinsicHeight = drawable.getIntrinsicHeight();
int intrinsicWidth = drawable.getIntrinsicWidth();

//height and width of the visible (scaled) image
int scaledHeight = imageBounds.height();
int scaledWidth = imageBounds.width();

//Find the ratio of the original image to the scaled image
//Should normally be equal unless a disproportionate scaling
//(e.g. fitXY) is used.
float heightRatio = intrinsicHeight / scaledHeight;
float widthRatio = intrinsicWidth / scaledWidth;

//do whatever magic to get your touch point
//MotionEvent event;

//get the distance from the left and top of the image bounds
int scaledImageOffsetX = event.getX() - imageBounds.left;
int scaledImageOffsetY = event.getY() - imageBounds.top;

//scale these distances according to the ratio of your scaling
//For example, if the original image is 1.5x the size of the scaled
//image, and your offset is (10, 20), your original image offset
//values should be (15, 30). 
int originalImageOffsetX = scaledImageOffsetX * widthRatio;
int originalImageOffsetY = scaledImageOffsetY * heightRatio;

Give this idea a try and see if it works for you.

Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • `xImageOffsetX` and `yImageOffset` are not defined, by using them you mean `scaledImageOffsetX` and `scaledImageOffsetY`?thks in advance, and sorry for bothering you on an old question! ;) – Matteo Jul 04 '12 at 17:16
  • @Matteo Heh, would seem to be. You're the first person to catch that, it seems, haha. Fixed. :) – Kevin Coppock Jul 04 '12 at 18:39
  • Great, happy to help! ;D and great answer btw. I'd have just another question if you don't mind: It seems to me that with this approach a point (x,y) on the `ImageView` is put in correspondence with a pixel of the `Bitmap`. But how does this solution take into consideration the fact that there may be more pixels than points of the `ImageView`?Shouldn't the correspondence be one to many being there more pixels than points?I hope I made the question understandable, if not tell me (if you have time ;D) and I can try to explain myself better! And thks anyway! ;D – Matteo Jul 04 '12 at 19:29
  • I'm not sure that it does take that into consideration, haha. In this solution, it's handling a specific touch event on the ImageView, so the spot touched is guaranteed to be a point on that ImageView's boundaries. So even if the image is larger than the ImageView, there should be no instance in which that's a problem in this particular scenario. – Kevin Coppock Jul 04 '12 at 19:44
  • I understand, thks a lot!Anyway I've posted a [question](http://stackoverflow.com/questions/11334984/correspondence-between-imageview-coordinates-and-bitmap-pixels) on my particular issue, and I'd be very glad if you found the time to give a look at it, when you have the chance. Thks again! ;) – Matteo Jul 04 '12 at 20:15
1

besides considering the offset due to padding (margin is part of the layout, it's space outside the view and doesn't have to be considered), if the image is scaled you can get the image matrix (ImageView.getImageMatrix()) to scale coordinates.

EDIT: You can get x/y scaling factor and translation amount getting the values array and using respective index constants:

float[] values;
matrix.getValues(values);

float xScale = values[Matrix.MSCALE_X];

note that translation doesn't include padding, you still would have to consider that separately. translation is used for instance in FIT_CENTER scaling when there's some "blank" space.

bigstones
  • 15,087
  • 7
  • 65
  • 82
  • Thanks for the answer. But how can I use the matrix in my scenario? `matrix.mapPoints()` converts the bitmap point to the ImageView's point. Currently I've set ImageViews scale type to "FitXY" and use something like this: `xCoord = (xCoord / imagView.getWidth())*bitmap.getWidth();` – Alex Feb 08 '11 at 14:46
  • @alekz edited answer. I'm not good at matrix algebra, so actually I don't know if translation value is in scaled units or not... – bigstones Feb 08 '11 at 15:32
0

Get floor Width and height

float floorWidth = floorImage.getWidth();
float floorHeight = floorImage.getHeight();

Calculate protionate value

float proportionateWidth = bitmapWidth / floorWidth;
float proportionateHeight = bitmapHeight / floorHeight;

Your X & Y

float x = 315;
float y = 119;

Multiple with PropotionateValue

x = x * proportionateWidth;
y = y * proportionateHeight;
Mohammad nabil
  • 1,010
  • 2
  • 12
  • 23
0

I'd say you probably need to offset the coordinates from the ImageView with any padding or margins in the layout to get the correct coordinates of the BitMap.

dbryson
  • 6,047
  • 2
  • 20
  • 20
0

As I came accross this question and tried it out myself, here is my solution. It seems to work with stretched and centered images.

class MyEditableImageView(context: Context, attrs: AttributeSet) :
    androidx.appcompat.widget.AppCompatImageView(context, attrs) {

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val image = drawable.toBitmap().copy(Bitmap.Config.ARGB_8888, true)
        val xp = (event.x - imageMatrix.values()[Matrix.MTRANS_X]) / imageMatrix.values()[Matrix.MSCALE_X]
        val yp = (event.y - imageMatrix.values()[Matrix.MTRANS_Y]) / imageMatrix.values()[Matrix.MSCALE_Y]
        if (xp >= 0 && xp < image.width && yp >= 0 && yp < image.height) {
            doSomethingOnImage(image, xp, yp)
            setImageBitmap(image)
        }
        return super.onTouchEvent(event)
    }

    ...

}
Oliver Hoffmann
  • 146
  • 2
  • 11
0

To add to kcoppock's answer, I just want to add that:

//original height and width of the bitmap
int intrinsicHeight = drawable.getIntrinsicHeight();
int intrinsicWidth = drawable.getIntrinsicWidth();

may return an answer you're not expecting. These values depend on the dpi of the drawable folder you load the image from. For instance, you might get a different value if you load the image from /drawable vs /drawable-hdpi vs /drawable-ldpi.

AndyB
  • 1,896
  • 2
  • 22
  • 32