opencv中cv2.minAreaRect函数输出角度问题详解
作者:alex1801 发布时间:2023-09-08 06:38:47
前言
网上很多例子都说cv2.minAreaRect函数的输出的角度范围在[-90,0],但是实测输出范围在[0,90]。再进行调研,确定为opencv4.5版本升级改动引起。
cv2.minAreaRect输入:四边形的四个点(不要求顺序)。
输出:最小外接矩形的中心点坐标x,y,宽高w,h,角度anlge,输出形式为元组((x,y),(w,h),anlge),顺序格式不变。
1、4.5版本
4.5版本定义为,x轴顺时针旋转最先重合的边为w,angle为x轴顺时针旋转的角度,angle取值为(0,90]。
cnts, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(cnts[0])
box = np.int0(cv2.boxPoints(rect))
print(rect)
输出结果:((201.25, 92.10), (20.93, 101.94), 67.47)
中心点坐标:(201, 92),宽高: (20, 101),角度: 67。
配合旋转函数,可实现框的拉平。旋转函数(逆时针旋转):
import cv2
import numpy as np
def rotate(img, angle, center=None, scale=1.0, fill=0, interpolation=cv2.INTER_LINEAR, expand=True):
if center is not None and expand:
raise ValueError('`auto_bound` conflicts with `center`')
h, w = img.shape[:2]
if center is None:
center = ((w - 1) * 0.5, (h - 1) * 0.5)
assert isinstance(center, tuple)
matrix = cv2.getRotationMatrix2D(center, angle, scale)
if expand:
cos = np.abs(matrix[0, 0])
sin = np.abs(matrix[0, 1])
new_w = h * sin + w * cos
new_h = h * cos + w * sin
matrix[0, 2] += (new_w - w) * 0.5
matrix[1, 2] += (new_h - h) * 0.5
w = int(np.round(new_w))
h = int(np.round(new_h))
rotated = cv2.warpAffine(
img,
matrix, (w, h),
flags=interpolation,
borderValue=fill)
return rotated
执行旋转:
rotate(img, -23, center=(201, 92), expand=False)
结果:
角度说明:
角度为x轴顺时针旋转,第一次接触到矩形边界时的值,范围:0~90°,第一次接触的边界为宽,区分方向可以使用宽、高的值来确定。
角度按逆时针旋转方式调整为:
if rect[1][0] > rect[1][1]: # w > h
angle = int(rect[2])
else:
angle = -(90 - int(rect[2]))
2、4.5之前版本
有网友测试4.1.*,4.2.*,4.3.*,4.4.*下minAreaRect函数的都一样,就是网上常见的角度输出为[-90~0]情况。但是实测python版本4系列的都为上述4.5版情况,可能是c++版本的不同吧。这里补充[-90~0]情况。
rect = cv2.minAreaRect(cnts[0])
rect[0]返回最小外接矩形的中心点,rect[1]为最小外接矩形的宽、高。rect[2]为旋转角度。
宽、高和角度定义如下:角度为x轴沿逆时针旋转遇到的第一个边时的旋转角度,因为是逆时针旋转所以角度为0~-90度。约定:遇到的第一个边为宽、另一个边为高。
补充:opencv ---minAreaRect()计算偏转角度并纠正
此次试验的目的是计算目标图像偏转的角度,在不改变图像尺寸下纠正
这里主要用到minAreaRect()函数和getRotationMatrix2D()函数
先简单的介绍下minAreaRect()函数,本人在这里踩了一些坑,在这里说明一下,如有不妥的地方,大家尽管指正。
函数为minAreaRect(InputArray points) ,InputArray points是所要求最小外接矩形的点集,这个点集不定个数。
这个矩形是可以有偏转角度的,可以与图像的边界不平行。
调用形式:RotatedRect minAreaRect(InputArray points)
角度计算规则:以左上角为原点,X轴逆时针旋转,所得到的第一个角度即为旋转角度,第一条边为最小外接矩形的宽。角度范围[-90,0],当最小外接矩形平行(或垂直)X轴时角度为-90。(跟目标图像的长宽没关系)
顺时针为正,逆时针为负
函数getRotationMatrix2D(Point2f center, double angle, double scale)
参数详解:
Point2f center:表示旋转的中心点
double angle:表示旋转的角度 //这里的角度顺时针为负,逆时针为正
double scale:图像缩放因子
踩坑的地方主要在角度分不清,我总结了一下:
minAreaRect():以X轴正方向为起点,顺时针为正,逆时针为负
getRotationMatrix2D():以X轴正方向为起点,顺时针为负,逆时针为正
下面是一个例子:
#include <iostream>
#include <string>
#include <math.h>
#include <vector>
#include <io.h>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat image = imread("a11.jpg");
Mat gaussianimage,grayimage, cannyimage, thresholdimage;
//---------------------旋转-----------------
//计算偏转角度
GaussianBlur(image, gaussianimage, Size(5, 5), 3, 3);//尽可能多的去除杂质
Canny(gaussianimage, cannyimage, 50, 150, 3);
cvtColor(cannyimage, grayimage, CV_GRAY2BGR);
vector<vector<Point>>vec_point;
vector<Vec4i>hireachy;
findContours(cannyimage, vec_point, hireachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
double degree = 0;
for (size_t i = 0; i < vec_point.size(); i++)
{
RotatedRect minrect = minAreaRect(vec_point[i]);//minAreaRect():以X轴正方形为起点,顺时针为正,逆时针为负
degree = minrect.angle;
//此处目的是为了让目标图像旋转到水平位置
if (degree > -90 && degree <= -45)
{
degree += 90;
}
else if (degree >-45 && degree < 0)
{
degree;
}
else
{
degree = 0;
}
cout <<"degree:" << degree << endl;
}
//旋转纠正
Point center = Point(image.cols / 2, image.rows / 2);
double angle = degree;
double scale = 1;
Mat rot(2, 3, CV_32FC1);
rot = getRotationMatrix2D(center, angle, scale);//getRotationMatrix2D():以X轴正方形为起点,顺时针为负,逆时针为正
Mat rotimage;
warpAffine(image, rotimage, rot, image.size());
namedWindow("rotation", 0);
resizeWindow("rotation", 800, 600);
imshow("rotation", rotimage);
}
效果图:
来源:https://blog.csdn.net/weixin_34910922/article/details/120239687


猜你喜欢
- 如果你是一位ASP爱好者,你一定想过ASP的执行效率如何?大家都知道ASP效率和CGI的比,在访问量少的时候,它们是不相上下的,有时可能CG
- 本文实例讲述了php获取客户端IP及URL的方法。分享给大家供大家参考,具体如下:function getonlineip(){//获取用户
- 前言说起这个事情吧也相对来说比较尴尬,对于一个技术来说忘记密码然后找回密码都是相当简单的一个事情,但是在生产环境中没有保存记录只能是自己的失
- 本文实例为大家分享了python实现计算器功能的具体代码,供大家参考,具体内容如下1. 案例介绍本例利用 Python 开发一个可以进行简单
- 卡口转换率将数据导入hive,通过SparkSql编写sql,实现不同业务的数据计算实现,主要讲述车辆卡口转换率,卡口转化率:主要计算不同卡
- 近几天闲着没事开始学了些python,看了看之前写过的代码,把写过的基础的几个程序写进博客,今天是第一个。一个可以说简单的不能再简单的小程序
- 参考文章ubuntu源码安装python3linux 编译安装python3.6的教程详解准备工作安装工具sudo apt updatesu
- 如下所示:file->settings->Editor->General->Console里面的console co
- Go流程控制1、条件语句IF1、简单格式(不支持三目运算符)if 布尔表达式 { // 执行语句}2、if里面包含多个表达式的时
- 使用python实现双向链表,供大家参考,具体内容如下双向链表: 指的是讲数据链接在一起,每个数据是一个节点,每一个节点都有一个数据区,两个
- 学习目的: 掌握ADO.NET打开SQL SERVER数据库的方法。 今天做个非常普通的例子,做一个用户登录框。主要是通过这个练习认识一下S
- 将源目录的图片用MD5命名并设定目标目录尝试了一下用 python 实现了一个生成密码的程序。感觉还是比较好上手的。但是那个程序还是非常简单
- MySQL数据库管理软件有两种版本,一种是企业版,一种是社区版,其中,前者是收费的,如果是个人使用的,社区版足矣。下载mysql-5.7.1
- 前言Python的异常处理能力非常强大,但是用不好也会带来负面的影响。我平时写程序的过程中也喜欢使用异常,虽然采取防御性的方式编码会更好,但
- 在python 开发web程序时,需要调用第三方的相关接口,在调用时,需要对请求进行签名。需要用到unix时间戳。 在python里,在网上
- Python有自己内置的标准GUI库--Tkinter,只要安装好Python就可以调用。今天学习到了图形界面设计的问题,刚开始就卡住了。为
- element-ui form或table lable换行问题今天在写项目,突然遇到个需求,需要将form里面的lable换行,百度了下,发
- pprint的英文全称Data pretty printer,顾名思义就是让显示结果更漂亮。print()和pprint()都是python
- 一、正则1.正则表达式定义正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一
- Doug Bowman,Google的Visual Design Lead离职了,一封带有感 * 彩的离职信惹发了大家不少的讨论。甚至还有人用