简单理解霍夫变换
来源:互联网 发布:手机淘宝怎样更新版本 编辑:程序博客网 时间:2024/05/23 12:01
这些天,敲代码的时候遇到了一个需要自动纠正图片角度的问题,想了很久不知道怎么办。正好老师在上课的时候提到了霍夫变换这个名词,我犹如发现了金矿,回来就猛地一通百度。无奈自己智商实在不太够,网上大神说的我看了很久依然不太理解,于是浪费了很多时间,宛如回到了高中(或者初中)画直线方程图像的年代……
于是,在我终于弄明白之后,就有了这篇文章。写给和我一样的新人看看,也欢迎大神批评指正。
首先,霍夫变换的作用是找出一张图片中的直线(也可以是圆等更复杂的图形,这里只讨论直线),那么怎么找呢?这里需要回顾一下中学数学的极坐标相关知识(至于为什么用极坐标而不是直角坐标,后面再说):
假设图片中有一条直线BC,那么对于BC上任意一点A(x,y),连接AO,将
因此,我们可以使用公式1表示每一个图片上任何一个点到原点的长度。那么,在这条直线上的其他点呢?霍夫变换的思想就是,在同一条直线上的所有点,当
由上面的式子可知,当
首先我们需要一张二值化的图片(如果不了解灰度化、二值化等概念,建议先百度或者参考我的其他文章),设有一张二值化的图片,前景为白色,背景为黑色,我们的程序遍历每个像素,如果是前景色,那么就针对该像素的坐标,使用每一个可能的
如果需要将直线转换为直角坐标的话,那么:
好了,讲了这么多,下面放代码吧。代码用OpenCV读取图片文件,其他自己写:
Line HoughLines(Mat &mat){ if (mat.channels() > 1) { throw (std::exception("ERROR")); //这里偷个懒,假设传入的图片已经过二值化处理 } uchar foreColor = 255; int xCenter = mat.cols / 2; int yCenter = mat.rows / 2; int *angelMap[60]; //theta角的取值范围是-90度到90度,每3度为一个区间,如需提高精确度,可以细分区间 int maxP = static_cast<int>(sqrt(xCenter * xCenter + yCenter * yCenter)); //计算rho的取值范围,每2个单位为一个区间,包括正负 for (size_t idx = 0; idx < 60; idx++) { angelMap[idx] = new int[maxP + 1]{ 0 }; //分配储存空间,记录每个(rho, theta)值出现的次数 } for (size_t idx = 0; idx < mat.rows; idx++) { uchar* ptr = mat.ptr<uchar>(idx); for (size_t subIdx = 0; subIdx < mat.cols; subIdx++) { if (ptr[subIdx] == foreColor) { int x = subIdx - xCenter; int y = yCenter - idx; //将坐标原点转换为图片中间,方便计算 for (int theta = -90; theta < 90; theta+=3) { int p = (static_cast<int>(x * cosf(PIRate * theta) + y * sinf(PIRate * theta)) + maxP) / 2; angelMap[theta / 3 + 30][p]++; //分别将rho和theta的值映射到数组的索引下标,然后修改计数 } } } } int maxRho = 0, maxTheta = 0, maxVal = 0; for (size_t idx = 0; idx < 60; idx++) { //这里偷个懒,只计算最大的那条直线 for (size_t subIdx = 0; subIdx <= maxP; subIdx++) { if (angelMap[idx][subIdx] > maxVal) { maxVal = angelMap[idx][subIdx]; maxRho = subIdx; maxTheta = idx; } } } int angle = (maxTheta - 30) * 3; Line line(-cosf(angle * PIRate) / sinf(angle * PIRate), (maxRho * 2 - maxP) / sinf(angle * PIRate)); for (size_t idx = 0; idx < 60; idx++) { delete[] angelMap[idx]; } return line;}
另有Line的数据结构,简单的写一下:
struct Line{ float k; float b; Line(float k, float b) { this->k = k; this->b = b; } int getY(int x) { return static_cast<int>(k * x + b + 0.5f); } int getX(int y) { return static_cast<int>((y - b) / k + 0.5f); }};
然后简单测试一下,测试代码就懒得放了,直接看效果图吧(寝室随手拍的,请忽略那一堆衣服)~
效果还不错~完结撒花!
- 简单理解霍夫变换
- 霍夫变换理解
- 离散傅立叶变换 - 简单理解
- CPS变换的简单理解
- 霍夫变换的一些理解
- 霍夫变换直线检测理解
- 霍夫变换直线检测及原理理解
- 图像变换-霍夫变换
- 理解变换
- OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑
- OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑
- OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑
- Opencv学习笔记-----霍夫变换直线检测及原理理解
- 霍夫变换
- 霍夫变换【转】
- 霍夫变换原理
- 霍夫变换
- 霍夫变换原理
- OSPF简介
- 171. Excel Sheet Column Number
- 14.[个人]C++线程入门到进阶(14)----双线程读写队列数据
- Android图片压缩(质量压缩和尺寸压缩)&Bitmap转成字符串上传
- List接口实现类(3):Vector
- 简单理解霍夫变换
- 杭电oj1213——How Many Tables(并查集)
- 线程同步Lock
- List接口实现类(4):Vector
- Java多线程中join方法的理解
- 15.[个人]C++线程入门到进阶(15)----线程函数:WaitForSingleObject
- MarkdownPad 2 Pro 注册码
- 【C语言】getchar单个字符依次输入(可对单个字符进行处理)
- Java线程安全的计数器