《图像处理实例》 之 目标旋转矫正(基于区域提取、DFT变换)
目标:1.把矩形旋转正。
2.把文字旋转校正。
目标一(旋转正方形)
思路:A.利用寻找边界进行旋转,然后进行ROI提取。
B.利用霍夫变换等寻找直线,主要找到拐点再进行图像变化。
本文利用第一点思路进行。。。
程序很简单,直接上代码:
#include<iostream>
#include <opencv2/opencv.hpp>
#include <math.h>
using namespace cv;
using namespace std; int Threshold_Value = ;
const int Threshold_Max_value = ;
const int Threshold_type_value = ;
double MaxWidth = , MaxHeight = ;//找最大的矩形边 RNG rng(); Mat input_image, threshold_image, output_image, Middle_image; void Threshold_Image_Bar(int, void *); int main(int argc, char**argv)
{
input_image = imread("1.jpg");
if (input_image.data == NULL) {
return -; cout << "can't open image.../";
}
imshow("Sourse Image", input_image);
blur(input_image, Middle_image,Size(,),Point(-,-),);
imshow("Blur Image", Middle_image);
cvtColor(Middle_image, Middle_image,COLOR_RGB2GRAY);
imshow("Gray Image", Middle_image);
namedWindow("Threshold Image",);
createTrackbar("阈值调整", "Threshold Image",&Threshold_Value,,Threshold_Image_Bar);
Threshold_Image_Bar(,);
waitKey();
return ;
} void Threshold_Image_Bar(int, void *)
{
/*--------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/
/*-----------------------------------------------------图像旋转校正---------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/ threshold(Middle_image, threshold_image, , , );
Canny(threshold_image,threshold_image, Threshold_Value, Threshold_Value*);
imshow("Threshold Image", threshold_image); vector<vector<Point>> contours;
vector<Vec4i> hireachy;
findContours(threshold_image,contours,hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(-,-));
char flag_count = ;
Mat Show_threImage = Mat::zeros(threshold_image.size(),CV_8UC3);
RotatedRect MinRect;
for (size_t i = ; i < contours.size(); i++)
{
const Scalar color = Scalar(rng.uniform(,),rng.uniform(,),rng.uniform(,));
drawContours(Show_threImage,contours,static_cast<int>(i),color,,,hireachy,,Point());
MinRect = minAreaRect(contours[i]);
//---------------------找最大的长宽、边界-----------------------------//
MaxWidth = MaxWidth > MinRect.size.width ? MaxWidth : MinRect.size.width;
MaxHeight = MaxHeight > MinRect.size.height ? MaxHeight : MinRect.size.height;
flag_count = ((MaxWidth == MinRect.size.width) || (MaxHeight == MinRect.size.height)) ? static_cast<int>(i) : flag_count;
}
imshow("Draw_Image_Contours", Show_threImage);
//-----------------为了求矩形边角点坐标---------------------//
Point2f pt[];
MinRect = minAreaRect(contours[flag_count]);
MinRect.points(pt);
Mat MaxRectImage = Mat::zeros(input_image.size(),CV_8UC3);
for (size_t i = ; i < ; i++)
{
const Scalar color = Scalar(,,);
line(MaxRectImage,Point(pt[i]),Point(pt[(i+)%]),color);
}
imshow("MaxRectImage", MaxRectImage);
//------水漫操作,为了就倾斜矩形座位ROI,在这里没作用,就是为了看看而已----//
Mat gray;
gray.create(input_image.size(), input_image.type());
Rect s = boundingRect(contours[flag_count]);
floodFill(MaxRectImage, Point(s.x + s.width / , s.y + s.height / ), Scalar(, , ));
bitwise_and(input_image, MaxRectImage, gray);
imshow("wjy", gray);
//--------图像旋转-----------//
Mat RotateImage = getRotationMatrix2D(Point2f(input_image.cols / , input_image.rows / ), +MinRect.angle, 1.0);
warpAffine(input_image, input_image,RotateImage, input_image.size(),,,Scalar(,,));
imshow("RotateImage", input_image); /*--------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/
/*------------------------------------------------ROI区域进行充满图像操作---------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------------------------------------------*/ Mat test;
blur(input_image, test, Size(, ), Point(-, -), );
cvtColor(test, test, COLOR_RGB2GRAY);
MaxWidth = ; MaxHeight = ;
threshold(test, test, , , ); Canny(test, test, Threshold_Value, Threshold_Value * );
findContours(test, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-, -));
for (size_t i = ; i < contours.size(); i++)
{
const Scalar color = Scalar(rng.uniform(, ), rng.uniform(, ), rng.uniform(, ));
drawContours(test, contours, static_cast<int>(i), color, , , hireachy, , Point());
MinRect = minAreaRect(contours[i]); MaxWidth = MaxWidth > MinRect.size.width ? MaxWidth : MinRect.size.width;
MaxHeight = MaxHeight > MinRect.size.height ? MaxHeight : MinRect.size.height;
flag_count = ((MaxWidth == MinRect.size.width) || (MaxHeight == MinRect.size.height)) ? static_cast<int>(i) : flag_count;
}
MinRect = minAreaRect(contours[flag_count]); /*通过水漫算法找ROI
Mat gray;
gray.create(input_image.size(), input_image.type());
Rect s = boundingRect(contours[flag_count]);
floodFill(MaxRectImage, Point(s.x + s.width / 2, s.y + s.height / 2), Scalar(255, 255, 255));
bitwise_and(input_image, MaxRectImage, gray);
imshow("wjy", gray);
*/
/*通过矩形找ROI
Rect bbox = MinRect.boundingRect();
Mat wjy_image = input_image(bbox);
imshow("123", wjy_image);
*/ /*通过四个点找ROI*/
MinRect.points(pt);
//Mat Result_Image = input_image(Rect(Point2i(100,100), Point2i(250, 250)));
Mat Result_Image = input_image(Rect(Point2i(pt[]),Point2i(pt[])));
Mat Result_ROI = Result_Image.clone();
imshow("",Result_ROI); }
目标二(旋转文字)
首先介绍一下DFT:
1.我们的图像是代表:空间域+值域。
(1)其中空间域就是像素直接的距离,之前的高斯滤波考虑的就是空间域,距离中心像素越近占的权重就越大(这个不懂去看我的高斯那篇博文)。
(2)其中值域就是像素值,之前的中值滤波考虑的就是像素值。
--->>>我现在想看看这个图像的边缘和纹理特征,也就是想看到图像哪里变化的快,图像的走向等,怎么办?如果是一条曲线,我们能看到线的走向,但是看得不明显,这个大家都知道,求f(x)的微分就可以了,看得不够细再求二阶微分。。。那么图像呢?边缘不就是canny算子求得梯度?sift算法不也是求得细节,其中也用到梯度了。这个时候我们的傅里叶变换就上场了。。。
2.我们的频率域代表:图像的变化率(简单理解梯度)
(1)傅里叶变换就是将图像从空间域--->>>频率域(值域就不说了,后者也包含值域),高频部分代表图像的细节、纹理等,低频部分代表图像的轮廓信息。比如中值滤波在空间域就是平衡那些较高的信号,在频率域就是把高频部分给过滤掉。两者是相互变化而来的,对某个的操作另一个操作也适合。
(2)变换的结果包括实数+复数(x+yi),以后用到的大部分都是将两者结合等于幅值图像显示。
(3)变换的大致意思就是任何一个函数都可以用sinx和cosx来表示,具体去看公式~~我也没太理解。。。
程序还有其它知识点,部分有注释,有部分看不懂的都在其它博客中有讲解!
上代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <windows.h> using namespace cv;
using namespace std;
void DFT(Mat& src, Mat& dst);
int main(int argc, char**argv)
{
Mat input_image,output_image;
input_image = imread("2.jpg");
if (input_image.data == NULL) {
return -; cout << "can't open image.../";
}
DFT(input_image, output_image); imshow("input_image2", input_image);
imshow("input_image2", output_image);
waitKey();
return ;
}
void DFT(Mat& src, Mat& dst123)
{
Mat dst,wjy = src.clone();
cvtColor(src, src, CV_BGR2GRAY);
//----获得有利于DFT变换的尺寸--->>就是扩大成奇数尺寸
const int height = getOptimalDFTSize(src.rows);
const int width = getOptimalDFTSize(src.cols);
//----为扩大的尺寸赋值为0
Mat middle_image;
copyMakeBorder(src, middle_image, , height - src.rows, , width - src.cols, BORDER_CONSTANT, Scalar::all());
//----DFT变换的结果为:实数+虚数,需要弄一个二通道Mat来存储
//Mat channels[] = { Mat_<float>(dst),Mat::zeros(src.size(),CV_32F) };
vector<Mat> channels();
channels[] = Mat_<float>(middle_image);
Mat temp = Mat::zeros(middle_image.size(), CV_32F);
channels[] = temp;
Mat complexI;
merge(channels,complexI);
dft(complexI, complexI);//DFT变换
split(complexI, channels);//分离变换的结果:实数+虚数,两个通道
magnitude(channels[], channels[], channels[]);//幅值计算-->>结果在channels[3]
dst = channels[];
//---由于幅值太大无法显示,需要进行尺度(详细看sift算法)变换
//---这里使用 M1 = log( M + 1 )进行尺寸缩小
dst += Scalar::all();
log(dst, dst);
dst = dst(Rect(, , src.cols, src.rows));
normalize(dst, dst, , , NORM_MINMAX);//浮点数直接显示不出来(如果是0-1可以显示)
dst.convertTo(dst, CV_8UC1);
//------频域移动
int cx = dst.cols;
int cy = dst.rows;
Mat top_lf = dst(Rect(Point(, ), Point(cx / , cy / )));
Mat top_rt = dst(Rect(Point(cx / , ), Point(cx, cy / )));
Mat bot_lf = dst(Rect(Point(, cy/ ), Point(cx/ , cy)));
Mat bot_rt = dst(Rect(Point(cx/ , cy/ ), Point(cx, cy)));
Mat mid;
//top_left<<--->>bottom_right
top_lf.copyTo(mid);
bot_rt.copyTo(top_lf);
mid.copyTo(bot_rt);
//top_right<<--->>bottom_left
top_rt.copyTo(mid);
bot_lf.copyTo(top_rt);
mid.copyTo(bot_lf);
//直线检测
//cvtColor(dst, dst, CV_BGR2GRAY);
threshold(dst, dst, , , THRESH_BINARY_INV);
//---此处不适合用houlinesP(),因为斜率在计算很麻烦
/*vector<Vec4i> lines;
HoughLinesP(dst, lines, 1, CV_PI / 360, 15, 3, 5);
Mat LineImage = Mat::zeros(dst.size(), dst.type());
for (size_t i = 0; i < lines.size(); i++)
{
line(LineImage, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(200, 55, 205), 1, 8, 0);
}*/
// 霍夫变换
vector<Vec2f> lines;
HoughLines(dst, lines, , CV_PI / , , , );
// 检测线个数
std::cout << "lines.size:" << lines.size() << std::endl;
Mat houghMat(dst.size(), CV_8UC3);
houghMat.setTo();
//for (size_t i = 0; i < lines.size(); i++)
// // 绘制检测线
//{
// float rho = lines[i][0], theta = lines[i][1];
// Point pt1, pt2;
// double a = cos(theta), b = sin(theta);
// double x0 = a*rho, y0 = b*rho;
// pt1.x = cvRound(x0 + 1000 * (-b));
// pt1.y = cvRound(y0 + 1000 * (a));
// pt2.x = cvRound(x0 - 1000 * (-b));
// pt2.y = cvRound(y0 - 1000 * (a));
// line(houghMat, pt1, pt2, Scalar(0, 255, 0), 1, CV_AA);
//}
//cv::imshow("houghMat", houghMat);
float theta = ;
// 检测线角度判断
for (size_t i = ; i < lines.size(); i++)
{
float thetaTemp = lines[i][] * / CV_PI;
if (thetaTemp > && thetaTemp < )
{
theta = thetaTemp;
break;
}
}
// 角度转换--具体见另一篇博客霍夫变换
float angelT = src.rows* tan(theta / * CV_PI) / src.cols;
theta = atan(angelT) * / CV_PI;
std::cout << "theta:" << theta << std::endl; // 取图像中心
cv::Point2f centerPoint = cv::Point2f(wjy.cols / , wjy.rows / );
double scale = ;
// 计算旋转矩阵
cv::Mat warpMat = getRotationMatrix2D(centerPoint, theta, scale);
// 仿射变换
cv::Mat resultImage(wjy.size(), wjy.type());
cv::warpAffine(wjy, resultImage,
warpMat, resultImage.size());
resultImage.copyTo(dst123);
}
参考: 贾老师opencv系列
《opencv图像处理编程实例》
http://open.163.com/movie/2013/3/K/8/M8PTB0GHI_M8RJ8VMK8.html讲解傅里叶变换的公开课
《图像处理实例》 之 目标旋转矫正(基于区域提取、DFT变换)的更多相关文章
- Qt Quick 图像处理实例之美图秀秀(附源代码下载)
在<Qt Quick 之 QML 与 C++ 混合编程具体解释>一文中我们解说了 QML 与 C++ 混合编程的方方面面的内容,这次我们通过一个图像处理应用.再来看一下 QML 与 C++ ...
- OpenCV文本图像的旋转矫正
用户在使用Android手机拍摄过程中难免会出现文本图像存在旋转角度.这里采用霍夫变换.边缘检测等数字图像处理算法检测图像的旋转角度,并根据计算结果对输入图像进行旋转矫正. 首先定义一个结构元素,再通 ...
- 基于区域的全卷积神经网络(R-FCN)简介
在 Faster R-CNN 中,检测器使用了多个全连接层进行预测.如果有 2000 个 ROI,那么成本非常高. feature_maps = process(image)ROIs = region ...
- C#数字图像处理时注意图像的未用区域
原文:C#数字图像处理时注意图像的未用区域 图1. 被锁定图像像素数组基本布局 如图1所示,数组的宽度并不一定等于图像像素数组的宽度,还有一部分未用区域.这是为了提高效率,系统要确定每 ...
- vue实现PC端调用摄像头拍照人脸录入、移动端调用手机前置摄像头人脸录入、及图片旋转矫正、压缩上传base64格式/文件格式
进入正题 1. PC端调用摄像头拍照上传base64格式到后台,这个没什么花里胡哨的骚操作,直接看代码 (canvas + video) <template> <div> &l ...
- 基于区域的CNN(R-CNN)
基于区域的CNN(R-CNN) Region-based CNNs (R-CNNs) 基于区域的卷积神经网络或具有CNN特征的区域(R-CNN)是一种将深度模型应用于目标检测的开创性方法.在本节中,将 ...
- Android图像处理实例教程
Android图像处理实例教程 原始出处 http://vaero.blog.51cto.com/4350852/856750
- Arcgis for JS之Cluster聚类分析的实现(基于区域范围的)
原文:Arcgis for JS之Cluster聚类分析的实现(基于区域范围的) 咱们书接上文,在上文,实现了基于距离的空间聚类的算法实现,在本文,将继续介绍空间聚类之基于区域范围的实现方式,好了,闲 ...
- 神州数码OSPF基于区域认证(简单、MD5认证)
实验要求:掌握基于区域的简单认证及MD5认证 拓扑如下 简单认证 R1 enable 进入特权模式 config 进入全局模式 hostname R1 修改名称 interface l0 进入端口 i ...
随机推荐
- FreeOpcUa compile
/********************************************************************************* * FreeOpcUa compi ...
- 《DSP using MATLAB》Problem 3.9
利用的频移性质为: 本习题代码: %% ------------------------------------------------------------------------ %% Outp ...
- TP3.2整合uplodify文件上传
HTML中:<style>#img_upload-queue{width:120px;float:left;} /*uploadify的上传进度条样式,前面的img_upload是根据上传 ...
- 看懂Class文件的装载流程
Class文件的加载过程 ClassLoader的工作模式 类的热加载 1 Class文件的装载流程 只有被java虚拟机装载的Class类型才能在程序中使用(注意装载和加载的区别) 1.1 类装载的 ...
- h5 的 audio 标签知识点
因为音频格式有版权,各浏览器使用不同的音频格式. 音频格式兼容性 音频格式 Chrome Firefox IE9 Opera Safari MP3 支持 不支持 支持 不支持 支持 OGG 支持 支持 ...
- 阻塞队列 BlockingQueue
在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利.本文 ...
- linq to sql 项目移植后,数据库实体类需要重新创建?
项目中,使用LINQ to SQL 访问数据库,代码移植到其他机器上,每次需要重新生成dbml文件,有无方法只要更改app.config呢? 经过试验是可行的: 1.引用system.configur ...
- bzoj1072排列
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1072 好像是这方面的裸题. 整除k 要想转移需要记录下 达到模k所有余数 的方案数. 为了生 ...
- bzoj2467生成树
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2467 大水题. #include<iostream> #include<cs ...
- C语言扩展动态内存报错:realloc(): invalid next size: 0x0000000002365010 ***
晚上被这个内存扩展崩溃的问题折腾的有点崩溃,当答案揭晓的那一刻,恍然大悟,原来如此简单. 练习题目:输入一个字符串,根据字母进行排序,说白了就是一个简单的冒泡 #include <stdio.h ...