How to convert coordinates of the image view to the coordinates of the bitmap?
Asked Answered
D

7

21

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?

Dilative answered 8/2, 2011 at 13:29 Comment(1)
A
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.

Ahimsa answered 8/2, 2011 at 15:17 Comment(5)
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! ;)Telegenic
@Telegenic Heh, would seem to be. You're the first person to catch that, it seems, haha. Fixed. :)Ahimsa
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! ;DTelegenic
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.Ahimsa
I understand, thks a lot!Anyway I've posted a question 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! ;)Telegenic
L
35

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;
}

Lucrative answered 30/3, 2012 at 15:22 Comment(3)
This solution is elegant and compact.Preindicate
How do I remap the coordinates?Longdrawn
What exactly does this code? can you add some explanation?Weiman
A
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.

Ahimsa answered 8/2, 2011 at 15:17 Comment(5)
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! ;)Telegenic
@Telegenic Heh, would seem to be. You're the first person to catch that, it seems, haha. Fixed. :)Ahimsa
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! ;DTelegenic
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.Ahimsa
I understand, thks a lot!Anyway I've posted a question 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! ;)Telegenic
U
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.

Unfurl answered 8/2, 2011 at 13:49 Comment(2)
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();Dilative
@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...Unfurl
E
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.

Exterritorial answered 8/2, 2011 at 13:32 Comment(2)
The main issue is that the bitmap may be scaledDilative
@alekz do you know the scaled value? If so, you can factor that in as well.Exterritorial
S
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.

Shrubbery answered 4/11, 2011 at 19:28 Comment(0)
I
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;
Indic answered 24/4, 2017 at 10:33 Comment(2)
What is floor width and height? can you please explain a bit more?Weiman
A real dimensions of a floor : ex:- 2000 x 1500 sqftIndic
U
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)
    }

    ...

}
Underlie answered 7/12, 2022 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.