Q:

Rotate (alignment) an image by line along the y-axis

I am trying to rotate (alignment) an image, which contain line (with two points P1 and P2) along the y-axis
Original image:

Note: the green area represents the original image
Result should be:

Note: the red area represents the original image after rotation
So I need to calculate the angle between line defined by P1(x1,y1) and P2(x2,y2) and by y-axis,

Note: the green line represents the y-axis
My code is:
import cv2
import numpy as np
from math import *
import math
import imutils

height = 500
width = 500

original_image = np.zeros((height,width,3), np.uint8)
original_image[:] = (0,255,0)

x1 = 400
y1 = 50

P1 = (x1, y1)

x2 = 100
y2 = 300

P2 = (x2, y2)

cv2.line(original_image, P1, P2, (0, 0, 0), 3)

deltaY = y1 - y2
deltaX = x1 - x2

angleInDegrees = atan2(deltaY, deltaX) * 180 / math.pi

print(angleInDegrees)

rotated_image = imutils.rotate_bound(original_image, angleInDegrees)

cv2.imshow("Original", original_image)
cv2.imshow("Rotated", rotated_image)
cv2.waitKey(0)

But my rotated_image is not properly aligned
Result looks like:

How should I fix it?

A:

First off you are calculating the wrong angle. The angle you are calcuating is between a vector originating at the origin and ending at P1 & a vector orignating at the origin and ending at P2.
The angle you need is between the vector starting at P1 and ending at P2 [P2-P1] & a vector that represents the direction of the y-axis which would be [0, 1].
Secondly you have to take into account that your origin is in the top left corner so you need to reflect the angle once calculated.
import cv2
import numpy as np
from math import *
import math
import imutils

height = 500
width  = 500

original_image = np.zeros((height,width,3), np.uint8)
original_image[:] = (0,255,0)

x1 = 400 
y1 = 50

P1 = np.array([x1, y1])

x2 = 100
y2 = 300

P2 = np.array([x2, y2])

# checks orientation of p vector & selects appropriate y_axis_vector
if (P2[1] - P1[1]) < 0:
    y_axis_vector = np.array([0, -1])
else:
    y_axis_vector = np.array([0, 1])

if (P2[0] - P1[0]) < 0 and (P2[1] - P1[1]) :
    y_axis_vector = np.array([0, 1])

p_unit_vector = (P2 - P1) / np.linalg.norm(P2-P1)
angle_p_y     = np.arccos(np.dot(p_unit_vector, y_axis_vector)) * 180 /math.pi

cv2.line(original_image, tuple(P1), tuple(P2), (0, 0, 0), 3)

print(angle_p_y)
print (P2-P1)

rotated_image = imutils.rotate_bound(original_image, -angle_p_y)

cv2.imshow("Original", original_image)
cv2.imshow("Rotated", rotated_image)
cv2.waitKey(0)

