Arashk
Arashk

Reputation: 23

how to write the script for image corner degree calculation?

I have over 100 images that each of them is in different angle. I have written an basic python code to rotate each of the image one by one from any angle to zero degree ( I mean making them flat). The python code that I have attached is a simple code and unfortunately it doesn't automatically find the angle and it does not make it exact zero. Any time for any of the image I should find the angle and run the code for many time to make it zero (sometimes I am not able to make it exactly flat or what we celled zero degree). According to the images that I have attached, image1 is one of the sample image as an input and image_2 is the rotated image that I want to have at the end as an output. I would like to ask anyone that can help me to modify the current code or provide me with the new python code (which I prefer the new code)that I can make my images rotate from any angle to zero degree. please feel free to ask me for more explanation if you want.

my opencv-python code is:

import cv2
import numpy as np
img = cv2.imread('image1.tif')
num_rows, num_cols = img.shape[:2]
rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows/2),69.4, 1)
img_rotation = cv2.warpAffine(img, rotation_matrix, (num_cols, num_rows))
cv2.imshow('Rotation', img_rotation)
cv2.imwrite('image_2.tif',img_rotation)
cv2.waitKey()

Note: input and output images deleted.

Upvotes: 2

Views: 926

Answers (2)

ewcz
ewcz

Reputation: 13087

it's definitely not the most robust method, but perhaps an option would be to:

  1. assume that the boundary is all black
  2. identify the top-most (x0,y0) / right-most (x1,y1) corners of the image
  3. calculate the angle of rotation as alpha = math.atan2(x1-x0,y1-y0)

I downloaded your figure (it was converted to png on imgur) and tested the procedure with:

#!/usr/bin/env python
import cv2
import math

import numpy as np

img = cv2.imread('test.png')
H, W = img.shape[:2]

x0,y0 = None,None
x1,y1 = None,None

#scan all rows starting with the first
for i in range(0, H):
    row = img[i].sum(axis=1)
    s = np.sum(row)
    if s:
        #if there is at least one non-black pixel, mark
        #its position
        x0 = np.max(np.where(row>0))
        y0 = i
        break

#scan all columns starting with the right-most one
for j in range(W-1,-1,-1):
    col = img[:,j,:].sum(axis=1)
    s = np.sum(col)
    if s:
        #mark the position of the first non-black pixel
        x1 = j
        y1 = np.min(np.where(col>0))
        break

dx = x1 - x0
dy = y1 - y0

alpha = math.atan2(dx, dy) / math.pi * 180

rotation_matrix = cv2.getRotationMatrix2D((W/2, H/2), -alpha, 1)
img_rotation = cv2.warpAffine(img, rotation_matrix, (W, H))
cv2.imwrite('image_2.tif',img_rotation)

EDIT:

The previous method can be inaccurate in case the "corner" pixel is black as well so that the calculated angle is then biased. Slightly more accurate approach could be as follows:

  1. determine the "upper" boundary of the rectangle (i.e., the coordinates of the pixels defining the edges)
  2. take the edge the projection of which on the x-axis is longer
  3. fit the coordinates in order to calculate the slope of the line defining the edge

The implementation:

#!/usr/bin/env python
import cv2
import math

import numpy as np

img = cv2.imread('test.png')
H, W = img.shape[:2]

data = []
for j in range(0, W):
    col = img[:,j,:].sum(axis=1)
    s = np.sum(col)
    if not s:
        continue

    for i in range(0, H):
        if col[i] > 0:
            data.append((j, i))
            break

y_min, min_pos = None, None
for idx, (x, y) in enumerate(data):
    if y_min is None or y < y_min:
        y_min = y
        min_pos = idx

N = len(data)
if min_pos > N - min_pos:
    data = data[:min_pos]
else:
    data = data[min_pos:]

data = np.asarray(data).T
coeffs = np.polyfit(data[0], data[1], 1)
alpha = math.atan(coeffs[0]) / math.pi * 180

print(alpha)

rotation_matrix = cv2.getRotationMatrix2D((W/2, H/2), alpha, 1)
img_rotation = cv2.warpAffine(img, rotation_matrix, (W, H))
cv2.imwrite('image_2.tif',img_rotation)

Upvotes: 1

Once
Once

Reputation: 1

another way to find the angle is (asuming that the image is over a black background):

  1. Convert the image to grayscale
  2. Segmentate the image using a threshold
  3. Find the contours of the image
  4. Find the parameters of the ellipse that fit the contour

    import cv2
    import numpy as np
    
    image = cv2.imread("DlYEa.png")
    
    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    
    num_rows, num_cols = image.shape[:2]
    _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
    
    img, contours, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
    cnt = contours[0]
    (x, y), (Ma, ma), angle = cv2.fitEllipse(cnt)
    
    angle = int(angle - 90)
    
    rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows/2), angle, 1)
    img_rotation = cv2.warpAffine(image, rotation_matrix, (num_cols, num_rows))
    
    cv2.imshow("rotation", img_rotation)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

Upvotes: 0

Related Questions