package com.cxbdapp.cadre.util;import org.opencv.core.Mat;import java.awt.image.BufferedImage;publicclassImageDeskew{/**
* Representation of a line in the image.
*/publicclassHoughLine{// count of points in the linepublic int count =0;// index in matrix.public int index =0;// the line is represented as all x, y that solve y * cos(alpha) - x *// sin(alpha) = dpublic double alpha;public double d;}// the source imageprivate BufferedImage cImage;// the range of angles to search for linesprivate double cAlphaStart =-20;private double cAlphaStep =0.2;private int cSteps =40*5;// pre-calculation of sin and cosprivate double[] cSinA;private double[] cCosA;// range of dprivate double cDMin;private double cDStep =1.0;private int cDCount;// count of points that fit in a lineprivate int[] cHMatrix;/**
* Constructor.
* @param image 图片
*/publicImageDeskew(BufferedImage image){this.cImage = image;}publicImageDeskew(Mat image){this.cImage =mat2BufferedImage(image);}/**
* Calculates the skew angle of the image cImage.
* @return BufferedImage
*/publicstatic BufferedImage mat2BufferedImage(Mat mat){
int cols = mat.cols();
int rows = mat.rows();
int elemSize =(int) mat.elemSize();
byte[] data =newbyte[cols * rows * elemSize];
int type;
mat.get(0,0, data);switch(mat.channels()){case1:
type = BufferedImage.TYPE_BYTE_GRAY;break;case3:
type = BufferedImage.TYPE_3BYTE_BGR;// bgr to rgb
byte b;for(int i =0; i < data.length; i = i +3){
b = data[i];
data[i]= data[i +2];
data[i +2]= b;}break;default:returnnull;}
BufferedImage image =newBufferedImage(cols, rows, type);
image.getRaster().setDataElements(0,0, cols, rows, data);return image;}publicvoidFlyChange(){}public double getSkewAngle(){
ImageDeskew.HoughLine[] hl;
double sum =0.0;
int count =0;// perform Hough Transformationcalc();// top 20 of the detected lines in the image
hl =getTop(20);if(hl.length >=20){// average angle of the linesfor(int i =0; i <19; i++){
sum += hl[i].alpha;
count++;}return(sum / count);}else{return0.0d;}}// calculate the count lines in the image with most pointsprivate ImageDeskew.HoughLine[]getTop(int count){
ImageDeskew.HoughLine[] hl =newImageDeskew.HoughLine[count];for(int i =0; i < count; i++){
hl[i]=newImageDeskew.HoughLine();}
ImageDeskew.HoughLine tmp;for(int i =0; i <(this.cHMatrix.length -1); i++){if(this.cHMatrix[i]> hl[count -1].count){
hl[count -1].count =this.cHMatrix[i];
hl[count -1].index = i;
int j = count -1;while((j >0)&&(hl[j].count > hl[j -1].count)){
tmp = hl[j];
hl[j]= hl[j -1];
hl[j -1]= tmp;
j--;}}}
int alphaIndex;
int dIndex;for(int i =0; i < count; i++){
dIndex = hl[i].index / cSteps;// integer division, no// remainder
alphaIndex = hl[i].index - dIndex * cSteps;
hl[i].alpha =getAlpha(alphaIndex);
hl[i].d = dIndex + cDMin;}return hl;}// Hough Transformationprivatevoidcalc(){
int hMin =(int)((this.cImage.getHeight())/4.0);
int hMax =(int)((this.cImage.getHeight())*3.0/4.0);init();for(int y = hMin; y < hMax; y++){for(int x =1; x <(this.cImage.getWidth()-2); x++){// only lower edges are consideredif(ImageUtil.isBlack(this.cImage, x, y)){if(!ImageUtil.isBlack(this.cImage, x, y +1)){calc(x, y);}}}}}// calculate all lines through the point (x,y)privatevoidcalc(int x, int y){
double d;
int dIndex;
int index;for(int alpha =0; alpha <(this.cSteps -1); alpha++){
d = y *this.cCosA[alpha]- x *this.cSinA[alpha];
dIndex =(int)(d -this.cDMin);
index = dIndex *this.cSteps + alpha;try{this.cHMatrix[index]+=1;}catch(Exception e){//logger.warn("", e);}}}privatevoidinit(){
double angle;// pre-calculation of sin and costhis.cSinA =newdouble[this.cSteps -1];this.cCosA =newdouble[this.cSteps -1];for(int i =0; i <(this.cSteps -1); i++){
angle =getAlpha(i)* Math.PI/180.0;this.cSinA[i]= Math.sin(angle);this.cCosA[i]= Math.cos(angle);}// range of dthis.cDMin =-this.cImage.getWidth();this.cDCount =(int)(2.0*((this.cImage.getWidth()+this.cImage.getHeight()))/this.cDStep);this.cHMatrix =newint[this.cDCount *this.cSteps];}public double getAlpha(int index){returnthis.cAlphaStart +(index *this.cAlphaStep);}}
3、rotateImageByMat
package com.cxbdapp.cadre.util;import org.opencv.core.Mat;import org.opencv.core.Point;import org.opencv.core.Scalar;import org.opencv.imgproc.Imgproc;importstatic org.opencv.core.Core.BORDER_CONSTANT;publicclassImageHelper{/**
* angel:左偏是1到180度,向右偏是-1到-180度
* @param srcMat
* @param angel
* @return
*/publicstatic Mat RotateNew(Mat srcMat, double angel){
Mat dst = srcMat.clone();
Point center =newPoint(srcMat.width()/2.0,srcMat.height()/2.0);
Mat mat1 = Imgproc.getRotationMatrix2D(center, angel,1.0);
Imgproc.warpAffine(srcMat,dst,mat1,dst.size(),Imgproc.INTER_NEAREST,BORDER_CONSTANT,newScalar(255,255,255));
mat1 =null;
center =null;return dst;}/**
* 自动纠偏
* @param src
* @param angle
* @return
*/publicstatic Mat rotateImageByMat(Mat src, double angle){
angle =-1* angle;returnRotateNew(src, angle);}}
C语言代码
1、effect.cpp中
/**
* @brief 自动纠偏-倾斜校正
*/void Effect::RotateAudo(cv::Mat &dst){if(!m_src.data)return;
dst = m_src;
double minimumDeskewThreshold =0.05;
ImageDeskew* id =newImageDeskew(m_src);
double imageSkewAngle = id->getSkewAngle();printLog("imageSkewAngle = "+ QString::number(imageSkewAngle));if((imageSkewAngle > minimumDeskewThreshold || imageSkewAngle <-(minimumDeskewThreshold))){
_RotateZoom* m_rotate =new_RotateZoom(imageSkewAngle,0,0,0,0,0,1.0,false);RotateZoom(m_rotate, m_src);}}
ImageDeskew::ImageDeskew(const Mat &src){// this->cImage = src;this->disImage =cvMat2QImage(src);}/**
* @brief 获得图像偏斜角
* @return double 偏斜角
*/
double ImageDeskew::getSkewAngle(){
HoughLine* hl;
double sum =0.0;
int count =0;// perform Hough Transformation// 执行Hough变换[霍夫变换](Hough变换的基本原理是将影像空间中的曲线(包括直线)变换到参数空间中,通过检测参数空间中的极值点,确定出该曲线的描述参数,从而提取影像中的规则曲线。)calc();// top 20 of the detected lines in the image// 图像中检测到的前20行
hl =getTop(20);
int hl_length =_msize(hl)/sizeof(HoughLine);if(hl_length >=20){// average angle of the linesfor(int i =0; i <19; i++){
sum += hl[i].alpha;
count++;}return(sum / count);}else{return0.0;}}/**
* @brief 图像中检测到的前20行
* @param count 前多少行
* @return 霍夫变换类HoughLine
*/
HoughLine* ImageDeskew::getTop(int count){
HoughLine* hl =newHoughLine[count];
HoughLine tmp;
int cHMatrix_length =_msize(cHMatrix)/sizeof(int);for(int i =0; i <(cHMatrix_length -1); i++){if(this->cHMatrix[i]> hl[count -1].count){
hl[count -1].count =this->cHMatrix[i];
hl[count -1].index = i;
int j = count -1;while((j >0)&&(hl[j].count > hl[j -1].count)){
tmp = hl[j];
hl[j]= hl[j -1];
hl[j -1]= tmp;
j--;}}}
int alphaIndex;
int dIndex;for(int i =0; i < count; i++){// integer division, no
dIndex = hl[i].index / cSteps;// remainder
alphaIndex = hl[i].index - dIndex * cSteps;
hl[i].alpha =getAlpha(alphaIndex);
hl[i].d = dIndex + cDMin;}return hl;}/**
* @brief 初始化参数
*/void ImageDeskew::init(){
double angle;// 正弦和余弦的预计算// pre-calculation of sin and costhis->cSinA =newdouble[this->cSteps -1];this->cCosA =newdouble[this->cSteps -1];for(int i =0; i <(this->cSteps -1); i++){
angle =getAlpha(i)*PI/180.0;this->cSinA[i]=sin(angle);this->cCosA[i]=cos(angle);}// range of dthis->cDMin =-this->disImage.width();this->cDCount =(int)(2.0*((this->disImage.width()+this->disImage.height()))/this->cDStep);this->cHMatrix =newint[this->cDCount *this->cSteps];}/**
* @brief 线条角度范围在index点的值
* @param index 点
* @return 值
*/
double ImageDeskew::getAlpha(int index){returnthis->cAlphaStart +(index *this->cAlphaStep);}/**
* @brief 霍夫变换
*/void ImageDeskew::calc(){
int hMin =(int)((disImage.height())/4.0);
int hMax =(int)((disImage.height())*3.0/4.0);
int w = disImage.width()-2;init();for(int y = hMin; y < hMax; y++){for(int x =1; x < w; x++){// only lower edges are consideredif(isBlack(disImage, x, y)){if(!isBlack(disImage, x, y +1)){calc(x, y);}}}}}/**
* @brief 计算通过点(x,y)的所有直线
* @param x 点横坐标
* @param y 点纵坐标
*/void ImageDeskew::calc(int x, int y){
double d;
int dIndex;
int index;for(int alpha =0; alpha <(this->cSteps -1); alpha++){
d = y *this->cCosA[alpha]- x *this->cSinA[alpha];
dIndex =(int)(d -this->cDMin);
index = dIndex *this->cSteps + alpha;try{this->cHMatrix[index]+=1;}catch(Exception e){//logger.warn("", e);}}}/**
* @brief 像素是否为黑色
* @param disImage 图片
* @param x 横坐标
* @param y 纵坐标
* @return 是否为黑色
*/
bool ImageDeskew::isBlack(QImage disImage, int x, int y){
QColor c;
double luminance =0;
c = disImage.pixel(x, y);
luminance =(c.red()*0.299)+(c.green()*0.587)+(c.blue()*0.114);return luminance <140;}/**
* @brief Mat 改成 QImage
* @param mat 图片Mat
* @return 图片QImage
*/
QImage ImageDeskew::cvMat2QImage(const Mat& mat){if(mat.type()==CV_8UC1)// 单通道{
QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
image.setColorCount(256);// 灰度级数256for(int i =0; i <256; i++){
image.setColor(i,qRgb(i, i, i));}
uchar *pSrc = mat.data;// 复制mat数据for(int row =0; row < mat.rows; row++){
uchar *pDest = image.scanLine(row);memcpy(pDest, pSrc, mat.cols);
pSrc += mat.step;}return image;}elseif(mat.type()==CV_8UC3)// 3通道{const uchar *pSrc =(const uchar*)mat.data;// 复制像素
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);// R, G, B 对应 0,1,2return image.rgbSwapped();// rgbSwapped是为了显示效果色彩好一些。}elseif(mat.type()==CV_8UC4){const uchar *pSrc =(const uchar*)mat.data;// 复制像素// Create QImage with same dimensions as input Mat
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);// B,G,R,A 对应 0,1,2,3return image.copy();}else{returnQImage();}}
2、effect.h中
classEffect{public:Effect(void);~Effect(void);public://自动纠偏-倾斜校正voidRotateAudo(cv::Mat &dst);private:
cv::Mat m_src;};classHoughLine{public:
int count =0;
int index =0;
double alpha;
double d;private:};classImageDeskew{public:ImageDeskew(const cv::Mat &src);~ImageDeskew();
double getSkewAngle();voidinit();voidcalc();voidcalc(int x, int y);
double getAlpha(int index);
bool isBlack(QImage disImage, int x, int y);
HoughLine*getTop(int count);
QImage cvMat2QImage(const cv::Mat& mat);private:// the source image// 源图// cv::Mat cImage;
QImage disImage;// the range of angles to search for lines// 搜索线条的角度范围
double cAlphaStart =-20;
double cAlphaStep =0.2;
int cSteps =40*5;// pre-calculation of sin and cos// 正弦和余弦的预计算
double* cSinA;
double* cCosA;// range of d// d的范围
double cDMin;
double cDStep =1.0;
int cDCount;// count of points that fit in a line// 一条直线上的点数
int* cHMatrix;};