Cannot find position of a point when scaling down image in android

Viewed 165

I was trying to make an application that can find rectangles in an image and highlight the corners of the same. I was able to find the corners of the image using OpenCV and no issues with it. But when i try to place some object like a small imageview to show the corners, it does not get itself at the correct position. After some brainstorming i realised that the image would be scaled and so i have to position the markers accordingly.
I took a look at this answer and used that method and tried to calculate the constraints to locate the marker but still it is not accurate (not even a bit). Just as an example i am providing an image i captured.
Captured image showing incorrect corners Here, the red circles are drawn by OpenCV (the required position) whereas the white-grey circles are those that were calculated.

Here is the code: (imgViewHeight and imgViewWidth are known constants)

protected void showCorners(List<Point> corners, Bitmap image){
        List<ImageView> markers = new ArrayList<>();
        markers.add((ImageView) findViewById(R.id.c1));
        markers.add((ImageView) findViewById(R.id.c2));
        markers.add((ImageView) findViewById(R.id.c3));
        markers.add((ImageView) findViewById(R.id.c4));

        ConstraintLayout cornerLayer = findViewById(R.id.cornerLayer);

        for (int i=0;i<4;i++){
            Point point = corners.get(i);

            //TODO: Correct the algorithm to find constraints.

            double constraintTop = ((imgViewHeight*point.y)/image.getHeight());   //Here are the problems
            double constraintLeft = ((imgViewWidth*point.x)/image.getWidth());    //Here are the problems

            Log.d("Constraints", constraintLeft+" x "+constraintTop);

            ImageView marker = markers.get(i);
            ConstraintSet set = new ConstraintSet();
            set.clone(cornerLayer);
            set.connect(marker.getId(), ConstraintSet.LEFT, cornerLayer.getId(), ConstraintSet.LEFT, (int)constraintLeft);
            set.connect(marker.getId(), ConstraintSet.TOP, cornerLayer.getId(), ConstraintSet.TOP, (int)constraintTop);
            set.applyTo(cornerLayer);
            cornerLayer.bringChildToFront(marker);

            marker.setVisibility(View.VISIBLE);
        }

    }

Here is the part of my layout containig the cornerLayer (it is a constraintLayout):

<androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cornerLayer"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/preview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:contentDescription="@string/title_activity_view_edit"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:srcCompat="@tools:sample/backgrounds/scenic" />

        <ImageView
            android:id="@+id/c1"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginStart="50dp"
            android:layout_marginTop="50dp"
            android:contentDescription="@string/a_corner"
            android:src="@android:drawable/radiobutton_off_background"
            app:layout_constraintStart_toStartOf="@id/cornerLayer"
            app:layout_constraintTop_toTopOf="@id/cornerLayer" />

        <ImageView
            android:id="@+id/c2"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginTop="50dp"
            android:layout_marginStart="200dp"
            android:contentDescription="@string/a_corner"
            android:src="@android:drawable/radiobutton_off_background"
            app:layout_constraintStart_toStartOf="@id/cornerLayer"
            app:layout_constraintTop_toTopOf="@id/cornerLayer" />

        <ImageView
            android:id="@+id/c3"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginStart="50dp"
            android:layout_marginTop="400dp"
            android:contentDescription="@string/a_corner"
            android:src="@android:drawable/radiobutton_off_background"
            app:layout_constraintStart_toStartOf="@id/cornerLayer"
            app:layout_constraintTop_toTopOf="@id/cornerLayer" />

        <ImageView
            android:id="@+id/c4"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginStart="200dp"
            android:layout_marginTop="400dp"
            android:contentDescription="@string/a_corner"
            android:src="@android:drawable/radiobutton_off_background"
            app:layout_constraintStart_toStartOf="@+id/cornerLayer"
            app:layout_constraintTop_toTopOf="@id/cornerLayer" />

    </androidx.constraintlayout.widget.ConstraintLayout>

I have given some default constraint values just for showing neatness in android studio but it gets changed so it doesn't matter (i suppose).

Any help would be appreciated.
Thanks.

1 Answers

Hi I would suggest you to use something like a scale function a) your image b) for your corner points

I wrote some python code which demonstrates how to scales both:

import cv2
filename = 'rectangle.jpg'
#read and show image 
imageOriginal = cv2.imread(filename,1) 
cv2.imshow('origin',imageOriginal)

#def corners
corners = list()
corners.append((0,0))
corners.append((430,0))
corners.append((0,280))
corners.append((430,280))

#def intial scale factors
scaleFactorX = 2
scaleFactorY = 2
#create a img copy for scaling
imageScale = imageOriginal.copy() 

#def colours
blue = (255,0,0)
red = (0,0,255)

#b) function to scale points
def scalePoints(corners,sx,sy):
    cornersScaled = list()
    for c in corners:
         scaledPoint = ((int(c[0]*sx),int(c[1]*sy)))
         cornersScaled.append(scaledPoint)
    return cornersScaled

# we scale the image and draw scaled points
def scaleImageAndPoints(x,y):
    global imageOriginal 
    global corners
    global scaleFactorX
    global scaleFactorY 
    scaleFactorX = x 
    scaleFactorY = y  
    #scale image
    #a)
    imageScale = cv2.resize(imageOriginal, (0,0), fx=scaleFactorX, fy=scaleFactorY) 
    #we print corners origin in blue
    for c in corners:
         cv2.circle(imageScale, c, 10,blue,5)
    #we scale the points
    scaledCorner=scalePoints(corners,scaleFactorX,scaleFactorY)
    #we print corners scaled in red
    for c in scaledCorner:
         cv2.circle(imageScale, c, 10, red,5)
    cv2.imshow('scaled',imageScale)
    
# callback function for trackbars
def UpdateX(value):
    if value == 0: #we avoid trackbars zero 
    value = 1
    scaleImageAndPoints(value,scaleFactorY)
def UpdateY(value):
    if value == 0: #we avoid trackbars zero 
    value = 1
    scaleImageAndPoints(scaleFactorX,value)

#create window ,add trackbars, and show it
cv2.namedWindow('scaled',cv2.WINDOW_GUI_NORMAL)
cv2.createTrackbar("x scale factor", "scaled", 2, 10, UpdateX)
cv2.createTrackbar("y scale factor", "scaled", 2, 10, UpdateY)
#inital open
scaleImageAndPoints(scaleFactorX,scaleFactorY)
cv2.waitKey(0)
cv2.destroyAllWindows()

Here a pic how the output looks like, the blue dots are the non-scaled points and the red dots are scaling with the picture:

example

and the example picture for reproducing: enter image description here

The cpp functions will work analog. I hope to find time to code this example in cpp too..

Related