OpenCV实现倾斜图片转正示例
本文通过OpenCV实例详解如何校正倾斜图片。重点解析cv2.minAreaRect()函数的返回值结构,包括中心点、边长顺序及角度含义,并提供完整的Python代码实现从灰度二值化、轮廓筛选到旋转抠图的全过程。
本文通过一个具体案例来学习OpenCV中的一个关键函数。网络上关于该函数的教程质量参差不齐,许多内容缺乏深入理解,直接复制粘贴的现象较为普遍。这种“拿来主义”往往导致知其然不知其所以然。在实际搜索解决方案时,常发现大量重复甚至错误的信息,因此深入理解原理至关重要。
本文将通过一个实际案例,详细讲解cv2.minAreaRect()函数,该函数在网络上常被误解或未能正确应用。
使用的示例图片如下。

我们的目标是将倾斜的钉子图像校正为水平放置,并提取出钉子的区域,效果如后续所示。

实现步骤如下。
第一步:进行灰度化和二值化处理,处理后的效果如图所示。

第二步:使用cv2.findContours()查找轮廓。由于二值化后会产生多个轮廓,我们需要通过面积筛选出目标轮廓。这里选择面积最大的轮廓,即钉子的轮廓。效果如图所示,红线表示找到的轮廓点连接后的形状。

第三步:查找该轮廓的最小外接矩形(包含角度信息),这是最关键的一步,也是本文开头提到的cv2.minAreaRect()函数。我们需要完全掌握其返回值。效果如图所示,蓝线表示找到的最小外接矩形。

有人可能会觉得这很简单,但我们需要仔细分析cv2.minAreaRect()的具体返回值结构。
box= cv2.minAreaRect(counter)
print(box)该函数的返回值是一个元组。
((257.3854675292969, 292.03851318359375), (454.5963439941406, 140.96072387695312), 48.21548080444336)
元组的第一个元素是最小外接矩形的中心点坐标。
元组的第二个元素也是一个元组,包含最小外接矩形的两条边长(注意是边长,而非简单的宽高)。这里存在一个容易混淆的点:返回顺序并不固定代表“宽x高”,有时长边在前,有时短边在前。例如,另一个返回值的示例如下。
((290.1945495605469, 256.9798889160156), (140.9435577392578, 460.39862060546875), 49.1729850769043)
元组的第三个元素是一个浮点数,表示外接矩形的角度。这个角度是指第二元组中第一个元素所对应的边与X轴的夹角。需要注意的是,该角度始终大于0。网上有些资料声称角度范围是-90到90度,但经过实际测试,负角度的情况极少见,可能是测试方法有误。



上述内容已阐明核心原理。
第四步:根据获取的中心点、角度以及第二元组中的边长大小,开始进行旋转操作。
需要注意的是,OpenCV中的旋转是逆时针方向,因此在计算旋转角度时必须注意方向。处理效果如图所示。

第五步:利用中心点和两条边长的信息,进行图像裁剪。处理效果如图所示。

至此,任务完成。
以下是完整代码。
# -*- coding: utf-8 -*-
# @Time : 2022/7/25 15:51
# @Author : guligedong
import cv2
import os
import numpy as np
base_folder = r'F:\mvtec_anomaly_detection\screw\test\good'
for i in os.listdir(base_folder):
img_path = os.path.join(base_folder, i)
if img_path.split('.')[-1] == 'db':
continue
print(img_path)
img = cv2.imread(img_path)
img = cv2.resize(img,(512,512))
img_h,img_w = img.shape[:2]
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_,bi_img = cv2.threshold(gray_img,127,255,cv2.THRESH_BINARY_INV)
counters,_ = cv2.findContours(bi_img,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for index,counter in enumerate(counters):
if cv2.contourArea(counter) > 10000 :
cv2.drawContours(img,counters,index,(0,0,255),1)
box= cv2.minAreaRect(counter)
print(box)
boxs= cv2.boxPoints(box)
boxs = np.int0([boxs])
print(boxs)
cv2.polylines(img, boxs, isClosed=True, color=(255, 125, 125), thickness=1)
cv2.imshow('img', img)
# cv2.waitKey()
center_x,center_y = int(box[0][0]),int(box[0][1])
lenth1, lenth2 = int(box[1][0]), int(box[1][1])
print(lenth1, lenth2)
angle = box[2]
print(angle)
if lenth1 > lenth2:
angle = angle
else:
angle = -(90 - angle)
rotate_matrix = cv2.getRotationMatrix2D(center=(center_x,center_y),angle=angle,scale=1)
rotated_image = cv2.warpAffine(src=img, M=rotate_matrix, dsize=(img_w, img_h))
cv2.imshow('Rotated image', rotated_image)
y_start = center_y - min(lenth1, lenth2) // 2 if center_y - min(lenth1, lenth2) // 2 > 0 else 0
y_end = center_y + min(lenth1, lenth2) // 2 if center_y + min(lenth1, lenth2) // 2 < img_h else img_h
x_start = center_x - max(lenth1, lenth2) // 2 if center_x - max(lenth1, lenth2) // 2 >0 else 0
x_end = center_x + max(lenth1, lenth2) // 2 if center_x + max(lenth1, lenth2) // 2 <img_w else img_w
crop_image = rotated_image[y_start:y_end,x_start:x_end]
cv2.imshow('crop_image', crop_image)
cv2.waitKey()该数据来源于mvtec_anomaly_detection数据集中的钉子数据集,读者可自行搜索资源进行尝试。关键在于理解上述逻辑而非仅运行代码。
本文关于OpenCV实现倾斜图片转正的示例介绍完毕。更多相关内容可搜索本站历史文章或浏览后续文章,感谢支持。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









