图像角度回正的几种方案

文章介绍了两种方法来旋转矫正验证码图像中的东倒西歪的点选内容。方案一是利用OpenCV的minAreaRect()找到最小外接矩形并计算旋转角度,然后进行图像的顺时针或逆时针旋转。方案二是通过霍夫变换检测图像中的直线,计算平均倾斜角度后进行旋转。每种方法都可能产生两种旋转结果,以适应不同情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

方案一:通过最小外接矩形解决

方案二:通过霍夫变换解决

源码下载:


在识别某些验证码(如点选验证码)的时候,我们会看到验证码图片上要求点选的内容是东倒西歪的,如下图所示。

为了更好的识别点选内容,我们最好先将该内容回正,也就是要调整图片的角度。现在我们一起来看下有什么好的方案。

方案一:通过最小外接矩形解决

我们可以使用opencv-python的minAreaRect()方法找到点选内容的最小外接矩形,关键是该方法会返回矩形当前的旋转角度。我们现在试着将下面的文字内容回正。

编写代码如下:

import cv2 as cv
import numpy as np


def get_angle():
    img = cv.imread("pic.jpg")

    # 灰度化
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    # 高斯模糊
    img_gaussian = cv.GaussianBlur(img_gray, (9, 9), 0)

    # 二值化
    ret, img_thresh = cv.threshold(img_gaussian, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)

    # 腐蚀处理
    kernel = np.ones((3, 3), np.float32)
    img_erode = cv.erode(img_thresh, kernel)

    # 通过计算图像非零值找到文本轮廓
    # 文本所在轮廓非零值应该是最大的
    def get_non_zero(_cnt, _img):
        _x, _y, _w, _h = cv.boundingRect(_cnt)
        return cv.countNonZero(_img[_y:_y + _h, _x:_x + _w])
    cnts, hiers = cv.findContours(img_erode, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    max_cnt = max(cnts, key=lambda cnt: get_non_zero(cnt, img_erode))

    # 获取文本轮廓的最小外接矩形,从而得到角度
    (x, y), (w, h), angle = cv.minAreaRect(max_cnt)

    # # 画出最小外接矩形
    # box = np.int0(cv.boxPoints(cv.minAreaRect(max_cnt)))
    # cv.drawContours(img, [box], 0, (0, 255, 0), 2)
    # cv.imshow("img", img)
    # cv.waitKey(0)

    # 如果角度为90,则不需要旋转
    if angle == 90:
        cv.imwrite("result.jpg", img)
        return

    # 两种方向回正
    angleToRotate1 = -90 + angle
    angleToRotate2 = angle

    # 顺时针回正
    rows, cols = img.shape[:2]
    M1 = cv.getRotationMatrix2D((cols / 2, rows / 2), angleToRotate1, 1)
    result_img1 = cv.warpAffine(img, M1, (cols, rows))
    cv.imwrite("result1.jpg", result_img1)

    # 逆时针回正
    M2 = cv.getRotationMatrix2D((cols / 2, rows / 2), angleToRotate2, 1)
    result_img2 = cv.warpAffine(img, M2, (cols, rows))
    cv.imwrite("result2.jpg", result_img2)

    # 显示
    cv.imshow("result1", result_img1)
    cv.imshow("result2", result_img2)
    cv.waitKey(0)


if __name__ == "__main__":
    get_angle()

我们首先对图像进行处理并获得一个二值化图像,然后用findContours()方法找到图像中的各个轮廓。轮廓中拥有最大非零值的就断定为文本轮廓,然后我们调用minAreaRect()方法获得该轮廓的最小外接矩形。

minAreaRect()方法会最小外接矩形的中心点坐标、高宽以及矩形当前旋转的角度值,该角度是x轴顺时针旋转时与碰到的矩形第一条边的夹角,范围在(0, 90]。请见下方示意图。

注:minAreaRect()方法返回的角度是x轴顺时针旋转时与碰到的矩形第一条边的夹角,而碰到的第一条边也会被当做矩形的宽。

如果角度为90°,则说明最小外接矩形是正的,不需要旋转。如果不是90°,则通过判断最小外接矩形的宽和高来决定回正时要旋转的角度。如果宽大于高,则顺时针旋转-90+angle角度;如果宽小于等于高,则逆时针旋转angle角度。

注:角度值为正时,getRotationMatrix2D()方法会逆时针旋转图片。

但是由于文字种类很多,而且文字还被旋转,所以我们不能确定被点选文字在最小外接矩形中所呈现的宽高大小、笔者这里就回正了两次,得到两张图片。运行结果如下:

方案二:通过霍夫变换解决

霍夫变换是一种特征提取方法,我们可以通过该方法获取到图像中的各个直线,然后通过直线的倾斜角度来调整图像。具体步骤如下:

1. 用Canny()方法检测出图像中的边缘轮廓线。

2. 用HoughLines()方法检测出图像中的所有直线。

3. 求出多条特定倾斜范围内直线的平均倾斜角度,然后进行回正。

编写代码如下:

import math
import cv2 as cv
import numpy as np


def rotate():
    img = cv.imread("test1.jpg")

    # 灰度化
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    # 高斯模糊
    img_gaussian = cv.GaussianBlur(img_gray, (9, 9), 0)

    # 二值化
    ret, img_thresh = cv.threshold(img_gaussian, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)

    # 腐蚀处理
    kernel = np.ones((3, 3), np.float32)
    img_erode = cv.erode(img_thresh, kernel)

    # 边缘检测
    img_edge = cv.Canny(img_erode, 350, 400, apertureSize=3)

    # 提取直线
    # 如果lines为None,则需要调整该函数的最后一个值
    lines = cv.HoughLines(img_edge, 1, np.pi / 180, 25)

    # 循环直线获取角度
    line_angle_list = []
    for line in lines:
        for rho, theta in line:
            a = np.cos(theta)
            b = np.sin(theta)
            x0 = a * rho
            y0 = b * rho
            x1 = int(x0 + 1000 * (-b))
            y1 = int(y0 + 1000 * (a))
            x2 = int(x0 - 1000 * (-b))
            y2 = int(y0 - 1000 * (a))

            # 如果是完全水平或者垂直的线就直接跳过
            if x1 == x2 or y1 == y2:
                cv.imwrite("result.jpg", img)
                return

            # 寻找特定范围的倾斜直线,跳过倾斜角度过大的
            t = float(y2 - y1) / (x2 - x1)
            if t < - math.pi / 3 or t > math.pi / 3:
                continue

            # 将符合条件的线条的倾斜角度保存到列表中
            angle = abs(math.degrees(math.atan(t)))
            line_angle_list.append(angle)

    # 计算平均角度
    average_angle = sum(line_angle_list) / len(line_angle_list)

    # 两种回正方案
    angleToRotate1 = -90 + average_angle
    angleToRotate2 = average_angle

    # 顺时针回正
    rows, cols = img.shape[:2]
    M1 = cv.getRotationMatrix2D((cols / 2, rows / 2), angleToRotate1, 1)
    result_img1 = cv.warpAffine(img, M1, (cols, rows))
    cv.imwrite("result1.jpg", result_img1)

    # 逆时针回正
    M2 = cv.getRotationMatrix2D((cols / 2, rows / 2), angleToRotate2, 1)
    result_img2 = cv.warpAffine(img, M2, (cols, rows))
    cv.imwrite("result2.jpg", result_img2)

    # 显示
    cv.imshow("result1", result_img1)
    cv.imshow("result2", result_img2)
    cv.waitKey(0)


if __name__ == "__main__":
    rotate()

我们首先对图像进行处理并获得一个二值化图像,然后用Canny()方法检测图像中的边缘线条,接着再通过HoughLines()方法检测出图像中的所有直线。通过t <= - math.pi / 3 or t >= math.pi / 3条件判断可以得到在特定倾斜范围内的一些直线(倾斜弧度小于math.pi/3的直线),我们将这些直线的倾斜角度的绝对值保存到列表中。

注:之所以要保存角度的绝对值,是因为直线可能朝着不同的方向,而得出的角度就会有正有负,如下图所示。当然我们也可以选择只保存角度为正的或者角度为负的值。

由于文字种类很多,而且文字还被旋转,所以我们不能确定文字要朝着哪一个方向旋转。笔者这里就回正了两次,得到两张图片。运行结果如下:

 

源码下载:

笔者还放入了其他两张测试图片。

链接:https://pan.baidu.com/s/1eWMw4s6RsmYsuIrZnOLwCA  

密码:fkdd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

la_vie_est_belle

谢谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值