目标: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变换)的更多相关文章

  1. Qt Quick 图像处理实例之美图秀秀(附源代码下载)

    在<Qt Quick 之 QML 与 C++ 混合编程具体解释>一文中我们解说了 QML 与 C++ 混合编程的方方面面的内容,这次我们通过一个图像处理应用.再来看一下 QML 与 C++ ...

  2. OpenCV文本图像的旋转矫正

    用户在使用Android手机拍摄过程中难免会出现文本图像存在旋转角度.这里采用霍夫变换.边缘检测等数字图像处理算法检测图像的旋转角度,并根据计算结果对输入图像进行旋转矫正. 首先定义一个结构元素,再通 ...

  3. 基于区域的全卷积神经网络(R-FCN)简介

    在 Faster R-CNN 中,检测器使用了多个全连接层进行预测.如果有 2000 个 ROI,那么成本非常高. feature_maps = process(image)ROIs = region ...

  4. C#数字图像处理时注意图像的未用区域

    原文:C#数字图像处理时注意图像的未用区域 图1. 被锁定图像像素数组基本布局         如图1所示,数组的宽度并不一定等于图像像素数组的宽度,还有一部分未用区域.这是为了提高效率,系统要确定每 ...

  5. vue实现PC端调用摄像头拍照人脸录入、移动端调用手机前置摄像头人脸录入、及图片旋转矫正、压缩上传base64格式/文件格式

    进入正题 1. PC端调用摄像头拍照上传base64格式到后台,这个没什么花里胡哨的骚操作,直接看代码 (canvas + video) <template> <div> &l ...

  6. 基于区域的CNN(R-CNN)

    基于区域的CNN(R-CNN) Region-based CNNs (R-CNNs) 基于区域的卷积神经网络或具有CNN特征的区域(R-CNN)是一种将深度模型应用于目标检测的开创性方法.在本节中,将 ...

  7. Android图像处理实例教程

    Android图像处理实例教程 原始出处 http://vaero.blog.51cto.com/4350852/856750

  8. Arcgis for JS之Cluster聚类分析的实现(基于区域范围的)

    原文:Arcgis for JS之Cluster聚类分析的实现(基于区域范围的) 咱们书接上文,在上文,实现了基于距离的空间聚类的算法实现,在本文,将继续介绍空间聚类之基于区域范围的实现方式,好了,闲 ...

  9. 神州数码OSPF基于区域认证(简单、MD5认证)

    实验要求:掌握基于区域的简单认证及MD5认证 拓扑如下 简单认证 R1 enable 进入特权模式 config 进入全局模式 hostname R1 修改名称 interface l0 进入端口 i ...

随机推荐

  1. Linux C single linked for any data type

    /************************************************************************** * Linux C single linked ...

  2. led灯的驱动电流和电阻

     通常led灯条所采用的LED驱动电流都是20mA, 这网站里有led电阻的详细计算过程:http://www.bao1314.net/792.html

  3. 粘包、拆包发生原因滑动窗口、MSS/MTU限制、Nagle算法

    [TCP协议](3)---TCP粘包黏包 [TCP协议](3)---TCP粘包黏包 有关TCP协议之前写过两篇博客: 1.[TCP协议](1)---TCP协议详解 2.[TCP协议](2)---TCP ...

  4. 10款PHP开源网店系统

    在当今经济危机的大环境下,网上购物越来越来吃香,网上开店成本低,快捷方便,出名的电子商务网站有淘宝,拍拍,Ebay或是最新的百度有啊,这些网站都提供开店的机会,如果是想自己搭建购物平台,可以从下面选择 ...

  5. docker 方式运行drill

    drill 1.14 版本已经官方支持使用docker 直接运行可,还是比较方便的,尽管镜像 有点大,但是实际测试使用还是比较方便的,实际上自己做一个也比较简单. 下载镜像 docker pull d ...

  6. Oracle 多行合并一行 方法

    假如有如下表,其中各个i值对应的行数是不定的 SQL> select * from t; I A          D ---------- ---------- --------------- ...

  7. Oracle C#处理时间类型的Insert

    首先如果直接   parm.Value=DateTime.Now;   insert into table (TheTime)Value(@parm);   执行sql就会报错 ----------- ...

  8. 【转】每天一个linux命令(25):linux文件属性详解

    原文网址:http://www.cnblogs.com/peida/archive/2012/11/23/2783762.html Linux 文件或目录的属性主要包括:文件或目录的节点.种类.权限模 ...

  9. Mac 上 java 究竟在哪里,本文彻底让你搞清楚!

    Mac下当你在[终端]输入java -version时,是执行的哪里的java呢,which java命令可以看到,就是[/usr/bin/java] [/usr/bin/java]只是个替身,实际指 ...

  10. 启动ECLIPSE时,提示failed to create the java virtual machine

    修改eclipse.ini中的-XX:MaxPermSize=256M 这一项的原始值是512M.