Qt开发记录11——功能开发——自动纠偏(倾斜校正)——由Java语言代码改成Qt的C语言代码

Java语言代码

1、toRotateAuto

/**
 * 自动纠偏
 * minimumDeskewThreshold表示完成纠偏的容忍偏移量
 * 越大越快完成纠偏,但有可能导致最终还是偏的
 * 越小越正,但是有可能导致一直在纠偏微调中
 * 0.015d-0.08d
 */
public void toRotateAuto() {
	long t1 = System.currentTimeMillis();
	try {
		final double minimumDeskewThreshold = 0.05d;
		ImageDeskew id = new ImageDeskew(base);
		double imageSkewAngle = id.getSkewAngle();
		if ((imageSkewAngle > minimumDeskewThreshold || imageSkewAngle < -(minimumDeskewThreshold))) {
			dst5 = ImageHelper.rotateImageByMat(base, -imageSkewAngle);
		}
		
	}catch (Exception e){
		log.error("自动纠偏抛出异常!", e);
		e.printStackTrace();
	}finally {
		long t2 = System.currentTimeMillis();
		log.info("自动纠偏耗时:"+(t2-t1)+"毫秒");
	}
}

2、ImageDeskew 类

package com.cxbdapp.cadre.util;

import org.opencv.core.Mat;
import java.awt.image.BufferedImage;

public class ImageDeskew {
    /**
     * Representation of a line in the image.
     */
    public class HoughLine {
        // count of points in the line
        public 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) = d
        public double alpha;
        public double d;
    }

    // the source image
    private BufferedImage cImage;
    // the range of angles to search for lines
    private double cAlphaStart = -20;
    private double cAlphaStep = 0.2;
    private int cSteps = 40 * 5;
    // pre-calculation of sin and cos
    private double[] cSinA;
    private double[] cCosA;
    // range of d
    private double cDMin;
    private double cDStep = 1.0;
    private int cDCount;
    // count of points that fit in a line
    private int[] cHMatrix;

    /**
     * Constructor.
     * @param image 图片
     */
    public ImageDeskew(BufferedImage image) {
        this.cImage = image;
    }
    public ImageDeskew(Mat image) {
        this.cImage = mat2BufferedImage(image);
    }

    /**
     * Calculates the skew angle of the image cImage.
     * @return BufferedImage
     */
    public static BufferedImage mat2BufferedImage(Mat mat) {
        int cols = mat.cols();
        int rows = mat.rows();
        int elemSize = (int) mat.elemSize();
        byte[] data = new byte[cols * rows * elemSize];
        int type;
        mat.get(0, 0, data);
        switch (mat.channels()) {
            case 1:
                type = BufferedImage.TYPE_BYTE_GRAY;
                break;
            case 3:
                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:
                return null;
        }
        BufferedImage image = new BufferedImage(cols, rows, type);
        image.getRaster().setDataElements(0, 0, cols, rows, data);
        return image;
    }
    public void  FlyChange() {

    }

    public double getSkewAngle() {
        ImageDeskew.HoughLine[] hl;
        double sum = 0.0;
        int count = 0;

        // perform Hough Transformation
        calc();
        // top 20 of the detected lines in the image
        hl = getTop(20);

        if (hl.length >= 20) {
            // average angle of the lines
            for (int i = 0; i < 19; i++) {
                sum += hl[i].alpha;
                count++;
            }
            return (sum / count);
        } else {
            return 0.0d;
        }
    }

    // calculate the count lines in the image with most points
    private ImageDeskew.HoughLine[] getTop(int count) {
        ImageDeskew.HoughLine[] hl = new ImageDeskew.HoughLine[count];
        for (int i = 0; i < count; i++) {
            hl[i] = new ImageDeskew.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 Transformation
    private void calc() {
        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 considered
                if (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)
    private void 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);
            }
        }
    }

    private void init() {
        double angle;
        // pre-calculation of sin and cos
        this.cSinA = new double[this.cSteps - 1];
        this.cCosA = new double[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 d
        this.cDMin = -this.cImage.getWidth();
        this.cDCount = (int) (2.0 * ((this.cImage.getWidth() + this.cImage.getHeight())) / this.cDStep);
        this.cHMatrix = new int[this.cDCount * this.cSteps];
    }

    public double getAlpha(int index) {
        return this.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;
import static org.opencv.core.Core.BORDER_CONSTANT;

public class ImageHelper {
    /**
     * angel:左偏是1到180度,向右偏是-1到-180度
     * @param srcMat
     * @param angel
     * @return
     */
    public static Mat RotateNew(Mat srcMat, double angel) {
        Mat dst = srcMat.clone();
        Point center = new Point(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,new Scalar(255,255,255));
        mat1 = null;
        center = null;
        return dst;
    }

    /**
     * 自动纠偏
     * @param src
     * @param angle
     * @return
     */
    public static Mat rotateImageByMat(Mat src, double angle){
        angle = -1 * angle;
        return RotateNew(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 = new ImageDeskew(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 lines
        for (int i = 0; i < 19; i++) {
            sum += hl[i].alpha;
            count++;
        }
        return (sum / count);
    } else {
        return 0.0;
    }
}

/**
 * @brief 图像中检测到的前20行
 * @param count 前多少行
 * @return 霍夫变换类HoughLine
 */
HoughLine* ImageDeskew::getTop(int count)
{
    HoughLine* hl = new HoughLine[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 cos
    this->cSinA = new double[this->cSteps - 1];
    this->cCosA = new double[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 d
    this->cDMin = -this->disImage.width();
    this->cDCount = (int) (2.0 * ((this->disImage.width() + this->disImage.height())) / this->cDStep);
    this->cHMatrix = new int[this->cDCount * this->cSteps];
}

/**
 * @brief 线条角度范围在index点的值
 * @param index 点
 * @return 值
 */
double ImageDeskew::getAlpha(int index) {
    return this->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 considered
            if (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);               // 灰度级数256
        for (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;
    }

    else if (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,2
        return image.rgbSwapped();              // rgbSwapped是为了显示效果色彩好一些。
    }
    else if (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,3
        return image.copy();
    }
    else
    {
        return QImage();
    }
}

2、effect.h中

class Effect
{
public:
    Effect(void);
    ~Effect(void);

public:
    //自动纠偏-倾斜校正
    void RotateAudo(cv::Mat &dst);

private:
    cv::Mat m_src;
};


class HoughLine
{
public:
    int count = 0;
    int index = 0;
    double alpha;
    double d;
private:

};

class ImageDeskew
{
public:
    ImageDeskew(const cv::Mat &src);
    ~ImageDeskew();

    double getSkewAngle();
    void init();
    void calc();
    void calc(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;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小言W

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值