【问题标题】:How to straighten the images given any orientation of the images如何在给定图像的任何方向的情况下拉直图像
【发布时间】:2021-11-30 01:39:14
【问题描述】:

我尝试了一种算法来旋转图像,但由于图像可以具有 0 到 180/360 度之间的任何方向,因此它无助于拉直我的图像。

示例图片: 抱歉,我无法在此处直接附加图片,因为我是 StackOverflow 的新手,它还不允许我直接发布图片。

img_1_75

img_2_90

img_3_45

我将不胜感激任何有关代码的帮助。 谢谢!

我的代码:

# import the necessary packages
import matplotlib.pyplot as plt
import numpy as np
import argparse
import cv2
import copy

def plt_imshow(title, image):
   # display the image
   plt.imshow(image)
   plt.title(title)
   plt.grid(False)
   plt.show()

args = {
"image": "C:/Users/tskta/Desktop/Images rotation/all 
rotations/75.png"}

# load the image from disk
image = cv2.imread(args["image"])

#convert the image to greyscale and flip the foreground and 
background
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.bitwise_not(gray)

thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | 
              cv2.THRESH_OTSU)[1]

#grab the x,y coordinates of all pixel values that are greater than 
zero
#and form a rotating bounding box
coords = np.column_stack(np.where(thresh > 0))
angle = cv2.minAreaRect(coords)[-1]
print(angle)

if -45 > angle >-89:
    angle = -(90 + angle)
elif angle == -90.0:
    angle = -angle
else:
    angle = -angle 

print(angle) 

# rotate the image to deskew it
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(image, M, (w, h),
          flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

# draw the correction angle on the image so we can validate it
cv2.putText(rotated, "Angle: {:.2f} degrees".format(angle),
    (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)

# show the output image
print("[INFO] angle: {:.3f}".format(angle))
plt_imshow("Input", image)
plt_imshow("Rotated", rotated)
cv2.imwrite("Downloads/rotated.png", rotated)

【问题讨论】:

  • 问题的上一次迭代:stackoverflow.com/questions/69404020/…
  • 你得到了什么结果?您是否能够将账单与背景清晰地分开?
  • 您的图片显示了一些简单代码可能无法处理的问题。 1)您有两个背景 - 白色和纹理 2)您有纹理背景 3)您在图像中有不止一张钞票。 4)您的代码似乎没有考虑到您的结果需要是纵向模式。 5) 这些技术可以简单地旋转,使文本正面朝上或正面朝下。所以你需要 OCR 来判断文本是否在正确的位置而不是倒置。
  • 我假设您不是要完全隔离白钞票,而是要提取钞票的矩形或纹理背景。所以我认为这应该在你的代码被表达时起作用。但主要的是您需要考虑到您想要输出的纵向模式方向。您拥有的代码,我可以简单地旋转到最近的 90 度方向。今天晚些时候,如果有机会,我将不得不运行您的代码。

标签: image opencv image-processing opencv-python image-rotation


【解决方案1】:

您的 Python/OpenCV 代码看起来不错,只是角度测试没有考虑对象需要旋转到纵向模式。

以下是通过计算具有最大长度的边的方向,然后将其与垂直 (Y) 轴对齐(不旋转)来实现的。

下面的代码采用一个白色矩形并将其在 -90 到 90 之间以 10 度为增量旋转。假设是单据的扫描会使得票据的顶部在图像的上半部分,从而满足假设。

输入:

import cv2
import numpy as np
import math

# unrotation to portrait mode

# read input
img = cv2.imread('vertical_rect.png')
hh, ww = img.shape[:2]

# compute center
cx = ww // 2
cy = hh // 2

# rotated input in range -90 to 90 inclusive
# assume scanned so always in that range
# do unrotation by using the longest edge of rectangle to find the direction 
# and unrotate so that it aligns with the vertical (Y) axis upwards
for rotation in range(-90,100,10):

    # rotate image
    matrix = cv2.getRotationMatrix2D((cx,cy), rotation, 1.0)
    img_rotated = cv2.warpAffine(img, matrix, (ww, hh), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

    # convert to gray
    gray = cv2.cvtColor(img_rotated, cv2.COLOR_BGR2GRAY)

    # threshold (must be convex, so use morphology to close up if needed)
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]

    # find all non-zero pixel coordinates
    # swap x and y for conversion from numpy y,x to opencv x,y ordering via transpose
    coords = np.column_stack(np.where(thresh.transpose() > 0))

    # get minAreaRect and its vertices
    rotrect = cv2.minAreaRect(coords)
    pts = cv2.boxPoints(rotrect)
    #print(pts)
    
    list = []
    # compute edge lengths and directions (in range -90 to 90) and put into list
    polygon = img_rotated.copy()
    for i in range(0,4):
        i1 = i
        i2 = i+1 if i!=3 else 0
        pt1 = pts[i1]
        pt2 = pts[i2]
        pt1x = pt1[0]
        pt2x = pt2[0]
        pt1y = pt1[1]
        pt2y = pt2[1]
        length = math.sqrt( (pt2x-pt1x)*(pt2x-pt1x) + (pt2y-pt1y)*(pt2y-pt1y) )
        direction = (180/math.pi)*math.atan2( (pt2y-pt1y), (pt2x-pt1x) )
        list.append([length, direction, pt1])
        
        # optional: draw lines around box points on input (rotated)
        # points start at left most point (and top most to break ties)
        # and go clockwise around rectangle
        # first point is blue and second point is green to show direction
        x1 = int(pt1x)
        y1 = int(pt1y)
        x2 = int(pt2x)
        y2 = int(pt2y)
        if i == 0:
            cv2.circle(polygon,(x1,y1),7,(255,0,0),-1)
            cv2.circle(polygon,(x2,y2),5,(0,255,0),-1)
            cv2.line(polygon, (x1,y1), (x2,y2), (0,0,255), 2)
        else:
            cv2.line(polygon, (x1,y1), (x2,y2), (0,0,255), 2)
                
    # sort list on length with largest first
    def takeFirst(elem):
        return elem[0]
    list.sort(key=takeFirst, reverse=True)
    
    # get direction of largest length and correct to -90 to 90 range
    item = list[0]
    dir = item[1]
    if dir < -90:
        dir = dir + 180
    if dir > 90:
        dir = dir - 180
                
    # correct to portrait mode
    # if dir is negative or zero, then add 90; otherwise subtract 90
    # dir = 0 occurs for both 0 and 90, so both cannot be determined -- pick one
    if dir <= 0:
        unrotate = dir + 90
    else:
        unrotate = dir - 90
        
    print("initial rotation=", rotation, "edge direction=", dir, "unrotation angle=", unrotate)
    
    # unrotate image
    M = cv2.getRotationMatrix2D((cx, cy), unrotate, 1.0)
    img_unrotated = cv2.warpAffine(img_rotated, M, (ww, hh), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

    #cv2.imshow('img_rotated', img_rotated)
    cv2.imshow('polygon', polygon)
    cv2.imshow('img_unrotated', img_unrotated)
    cv2.waitKey()

    # optional save output
    #cv2.imwrite("vertical_rect_{0}.png".format(rotation), img_unrotated)

Results:

initial rotation= -90 edge direction= 0.0 unrotation angle= 90.0
initial rotation= -80 edge direction= -10.024489033132815 unrotation angle= 79.97551096686719
initial rotation= -70 edge direction= -19.922231300954746 unrotation angle= 70.07776869904525
initial rotation= -60 edge direction= -30.000354626107335 unrotation angle= 59.99964537389266
initial rotation= -50 edge direction= -39.98688344835102 unrotation angle= 50.01311655164898
initial rotation= -40 edge direction= -50.00064126059898 unrotation angle= 39.99935873940102
initial rotation= -30 edge direction= -60.00891266459192 unrotation angle= 29.991087335408082
initial rotation= -20 edge direction= -70.07776869904525 unrotation angle= 19.92223130095475
initial rotation= -10 edge direction= -79.97551600323786 unrotation angle= 10.024483996762143
initial rotation= 0 edge direction= 90.0 unrotation angle= 0.0
initial rotation= 10 edge direction= 79.97550927006476 unrotation angle= -10.024490729935238
initial rotation= 20 edge direction= 70.07776869904525 unrotation angle= -19.92223130095475
initial rotation= 30 edge direction= 59.99507091100694 unrotation angle= -30.00492908899306
initial rotation= 40 edge direction= 50.013119348261796 unrotation angle= -39.986880651738204
initial rotation= 50 edge direction= 39.99936207636015 unrotation angle= -50.00063792363985
initial rotation= 60 edge direction= 29.991085160541278 unrotation angle= -60.008914839458726
initial rotation= 70 edge direction= 19.922231300954746 unrotation angle= -70.07776869904525
initial rotation= 80 edge direction= 10.02448431018454 unrotation angle= -79.97551568981547
initial rotation= 90 edge direction= 0.0 unrotation angle= 90.0

【讨论】:

  • 您好@fmw42,感谢您宝贵的时间和代码!我不知道如何保存处于纵向模式的图像
  • 为什么需要从我的脚本中保存生成的纵向模式,因为它与输入相同?如果真的需要,我可以添加该代码,但我不明白动机
  • 是的,您的输入是纵向模式,但我想尝试使用同样不是纵向的不同图像。所以,我需要那部分代码来保存图像。希望你能理解。
  • 我在脚本末尾的 cv2.waitKey() 之后添加了代码。它已被注释掉,但您可以根据需要取消注释。
  • 感谢您的帮助@fmw42
猜你喜欢
  • 1970-01-01
  • 2015-05-04
  • 1970-01-01
  • 1970-01-01
  • 2018-09-14
  • 1970-01-01
  • 2010-12-16
  • 2012-01-25
  • 1970-01-01
相关资源
最近更新 更多