(四)条码定位三种方案的相关代码

Morphology.h

#pragma once

#ifndef __MORPHOLOGY__
#define __MORPHOLOGY__

#include "opencv2\opencv.hpp"
#include 
#include 
#include 

class Morphology 
{

public:
    Morphology();
    ~Morphology();

    // 中值滤波
    void medianFilter(cv::Mat &src, cv::Mat &dst, int n);

    // *************************针对二值图****************************
    // 腐蚀
    void  binary_erode(cv::Mat &src, cv::Mat& dst, int n);
    // 膨胀
    void binary_dilate(cv::Mat &src, cv::Mat &dst, int n);
    // 开运算: 先腐蚀,后膨胀
    void binary_openOperation(cv::Mat &src, cv::Mat &dst, int n);
    // 闭运算: 先膨胀,后腐蚀
    void binary_closeOperation(cv::Mat &src, cv::Mat &dst, int n);

    // 形态学梯度 g = (f+b)-(f-b)
    void binary_MorphologicalGradiend(cv::Mat &src, cv::Mat &dst, int n);

    // *************************针对灰度图****************************
    // 腐蚀
    void gray_erode(cv::Mat &src, cv::Mat &dst, int n);
    // 膨胀
    void gray_dilate(cv::Mat &src, cv::Mat &dst, int n);
    // 开运算
    void gray_openOperation(cv::Mat &src, cv::Mat &dst, int n);
    // 闭运算
    void gray_closeOperation(cv::Mat &src, cv::Mat &dst, int n);
    // 顶帽作用
    void gray_topHat(cv::Mat &src, cv::Mat &dst, int n);
    // 底帽作用
    void gray_bottomCap(cv::Mat &src, cv::Mat &dst, int n);
};


#endif
View Code

imageProcessing.h

#pragma once

#ifndef __IMAGEPROCESSING__
#define __IMAGEPROCESSING__

#include "Morphology.h"


struct SecondMaxNumber;


class ImageProcessing 
{
public:
    ImageProcessing();
    ~ImageProcessing();

    // Otsu方法寻找分割最佳阈值
    int OtsuAlgThreshold(const cv::Mat image);
    // 5*5 二值图滤波,消除零星噪声点的干扰
    void binary_filter(cv::Mat &src, cv::Mat &dst,int n);

    // 目标区域定位 
    // 注: src_bin 是底帽变换之后的二值图
    void objDetect(cv::Mat &src_bin, cv::Mat &dst);

    // 形态学处理操作
    void morphology(cv::Mat &src, cv::Mat &dst);
    
    // *************************1. 边缘一致性***********************************
    // 四方向边缘检测(0/45/90/135)
    int edge_Scharr_4(cv::Mat &src_gray, cv::Mat &dst_grad, cv::Mat &dst_angle);
    int Scharr4_gradConsis(cv::Mat &grad_bin, cv::Mat &grad_angle);

    // 8方向边缘检测
    // 1. 分为八个方向统计边缘的个数
    // 2. 边缘个数最多的方向作为该图像块的边缘方向主方向;
    // 3. 主方向的边缘点数占边缘总数的比例大于某个阈值(0.6-0.8),认为是候选区域,否者抛弃;
    void getSecondMaxNumber(std::vector<float> &arr, SecondMaxNumber &sm);
    int edge_Scharr_8(cv::Mat &src_gray, cv::Mat &dst_grad, cv::Mat &dst_angle);
    int Scharr8_gradConsis(cv::Mat &grad_bin, cv::Mat &grad_angle);

    // ************************************************************************

    // ***********************2. 梯度方向一致性***********************************
    // 1. 计算图像块内:梯度方向一致性的强弱coh
    // 2. coh:方向一致性在0-1之间,取值越大方向一致性越强
    float cal_gradientDirection(cv::Mat &grad_x, cv::Mat &grad_y);
    // 利用Scharr滤波器,计算在X和Y方向的边缘梯度
    void Scharr2_x_and_y_grad(cv::Mat &src_gray, cv::Mat &x_grad, cv::Mat &y_grad);

    //****************************************************************************

    //*************************3.对比度和边缘强度对比******************************
    // 统计对比度特征:前、后背景的平均灰度差
    float cal_contrast(cv::Mat &src_gray);
    // 统计4个方向边缘强度特征:0/45/90/135
    // 边缘强度的个数占比
    int cal_edge(cv::Mat &src_gray);

    //****************************************************************************

    // 图像增强方法
    int imge_enhancement(cv::Mat &src_gray, cv::Mat &dst);
    // 找轮廓定位条码区域
    void codeBar_detect(cv::Mat &merge_bin, cv::Mat &MergeImage, cv::Mat &dst);

    // 分块统计特征:对比度、方向边缘强度、线性尺度
    void cut_and_merge(cv::Mat &src, cv::Mat &dst);

    // **********************************提取面单*********************************
    void removeBack(cv::Mat &src_gray, cv::Mat &dst);

    // 第二次局部自适应阈值分割
    void obj_Segment(cv::Mat &src_gray, cv::Mat &src_bin, int &otsu_Th, int &ostu_second);
    // 定位面单区域
    void find_ROI(cv::Mat &src_gray, cv::Mat &src_bin, cv::Mat &dst);

    void detect_medu(cv::Mat &src, cv::Mat &dst, int &otsu_val, int &ostsu_second);


    // 图像处理主函数
    void imgProcessMain(cv::Mat &src, cv::Mat &dst, int &otsu_Th, int &ostsu_second);

};


#endif
View Code

Morphology.cpp

#include "Morphology.h"

Morphology::Morphology() 
{

}

Morphology::~Morphology() 
{

}

// 中值滤波
void  Morphology::medianFilter(cv::Mat &src, cv::Mat &dst, int n)
{
    std::vector<int> windows_pix;
    src.copyTo(dst);

    int win_sum = n*n;
    int edge = n / 2;

    for (int i = edge; i < src.rows - edge; i++)
    {
        uchar* dstptr = dst.ptr(i);

        for (int j = edge; j < src.cols - edge; j++)
        {
             windows_pix.clear();
            for (int x = -edge; x <= edge; x++)
            {
                for (int y = -edge; y <= edge; y++)
                {
                    windows_pix.push_back(src.at(i + x, j + y));
                }
            }

            std::sort(windows_pix.begin(), windows_pix.end());
            dstptr[j] = int(windows_pix[win_sum / 2]);
        }
    }
}

// **************************针对二值图像****************************************
// 腐蚀作用:腐蚀是一种收缩或细化操作,
//   针对灰度图像:将小于结构元的图像细节从图像中滤除(去除)了;
// 膨胀作用:膨胀则会“增长”或“粗化”二值图像中的物体,最简应用:桥接裂缝;

// 开运算:先腐蚀,后膨胀;
// 开运算作用:
// 闭运算:先膨胀,后腐蚀;

// 二值图腐蚀
// 图像f,背景0,目标前景A(255),结构元素B(255)

// 腐蚀定义:在图像f中,移动结构元素B的区域全部属于前景A;
// 在图像f中的某一像素点,若该处像素值为255,以结构元素B的中心处来对应该像素点,若结构元素B大小覆盖的邻域范围内(图像f上)存在非255的值,那么该像素点就会被腐蚀掉,即变成0;
// 腐蚀编程的逻辑:
// 1)首先判断该处像素是否为255,若不是255,则结束,判断下一个像素;若是255,跳转步骤2;
// 2) 若是255,在判断其邻域(结构元素大小)是否全是255; 若全是255,结束,判读下一个像素;若不全是255,跳转步骤3;
// 3) 若存在不是255的值,则该出像素值被腐蚀,即该处像素值改为0。
void   Morphology::binary_erode(cv::Mat &src, cv::Mat& dst, int n)
{
    std::vector<float> winTotal;
    src.copyTo(dst);

    int windows_size = n*n;
    int edge = n / 2;

    for (int i = edge; i < src.rows - edge; i++)
    {
        uchar * dstptr = dst.ptr(i);

        for (int j = edge; j < src.cols - edge; j++)
        {

            float center = src.at(i, j);
            if (255.0 != center)
                continue;

            winTotal.clear();
            for (int x = -edge; x <= edge; x++)
            {
                for (int y = -edge; y <= edge; y++)
                {
                    winTotal.push_back(src.at(i + x, j + y));
                }
            }

            float sum = accumulate(winTotal.begin(), winTotal.end(), 0);

            float mean = sum / winTotal.size();

            if (mean != 255.0)
                dstptr[j] = 0;
        }
    }
}

// 二值图膨胀
// 作用:可消除二值图中前景(255)中的零星黑点(0);
// 膨胀定义:在图像f上,移动结构元素b,与前景A有重叠的所有区域;
// 膨胀编程逻辑:
// 1) 针对图像f上的当前像素,对应结构元素B的中心位置,
//    若在结构元素B覆盖范围(图像f上)邻域内有非0的元素,那么该像素点就被膨胀处理,即变为255; 
//    否则,不做任何处理,运行下一像素. 
void Morphology::binary_dilate(cv::Mat &src, cv::Mat &dst, int n)
{
    std::vector<float> winTotal;
    src.copyTo(dst);

    int windows_size = n*n;
    int edge = n / 2;

    for (int i = edge; i < src.rows - edge; i++)
    {
        uchar * dstptr = dst.ptr(i);

        for (int j = edge; j < src.cols - edge; j++)
        {
            winTotal.clear();
            for (int x = -edge; x <= edge; x++)
            {
                for (int y = -edge; y <= edge; y++)
                {
                    winTotal.push_back(src.at(i + x, j + y));
                }
            }

            float sum = accumulate(winTotal.begin(), winTotal.end(), 0);
            if (sum > 0)
                dstptr[j] = 255;
        }
    }

}

// 开运算: 先腐蚀,后膨胀;
// 作用:
void Morphology::binary_openOperation(cv::Mat &src, cv::Mat &dst, int n)
{
    cv::Mat src_temp;
    binary_erode(src, src_temp, n);
    binary_dilate(src_temp, dst, n);
}

// 闭运算: 先膨胀,后腐蚀;
void Morphology::binary_closeOperation(cv::Mat &src, cv::Mat &dst, int n)
{
    cv::Mat src_temp;
    binary_dilate(src, src_temp, n);
    binary_erode(src_temp, dst, n);
}

void Morphology::binary_MorphologicalGradiend(cv::Mat &src, cv::Mat &dst, int n) 
{
    cv::Mat src_dilate, src_erode;

    binary_dilate(src, src_dilate, n);
    binary_erode(src, src_erode, n);

    if (src_dilate.rows == src_erode.rows && src_dilate.cols == src_erode.cols
        && src_dilate.type() == src_erode.type())
    {
        subtract(src_dilate, src_erode, dst);
        return;
    }

    src.copyTo(dst);
 
}

// ***********************************针对灰度图*********************************
// 腐蚀
void Morphology::gray_erode(cv::Mat &src, cv::Mat &dst, int n)
{
    std::vector<float> winTotal;
    src.copyTo(dst);

    int windows_size = n*n;
    int edge = n / 2;

    for (int i = edge; i < src.rows - edge; i++)
    {
        uchar * dstptr = dst.ptr(i);

        for (int j = edge; j < src.cols - edge; j++)
        {

            winTotal.clear();
            for (int x = -edge; x <= edge; x++)
            {
                for (int y = -edge; y <= edge; y++)
                {
                    winTotal.push_back(src.at(i + x, j + y));
                }
            }

            std::sort(winTotal.begin(), winTotal.end());
            dstptr[j] = winTotal[0];
        }
    }
}

// 膨胀
void Morphology::gray_dilate(cv::Mat &src, cv::Mat &dst, int n)
{
    std::vector<float> winTotal;
    src.copyTo(dst);

    int windows_size = n*n;
    int edge = n / 2;

    for (int i = edge; i < src.rows - edge; i++)
    {
        uchar * dstptr = dst.ptr(i);

        for (int j = edge; j < src.cols - edge; j++)
        {
            winTotal.clear();
            for (int x = -edge; x <= edge; x++)
            {
                for (int y = -edge; y <= edge; y++)
                {
                    winTotal.push_back(src.at(i + x, j + y));
                }
            }

            std::sort(winTotal.begin(), winTotal.end());
            dstptr[j] = winTotal[winTotal.size() - 1];
        }

    }
}

void Morphology::gray_openOperation(cv::Mat &src, cv::Mat &dst, int n)
{
    cv::Mat src_temp;
    gray_erode(src, src_temp, n);
    gray_dilate(src_temp, dst, n);
}

void Morphology::gray_closeOperation(cv::Mat &src, cv::Mat &dst, int n)
{
    cv::Mat src_temp;
    gray_dilate(src, src_temp, n);
    gray_erode(src_temp, dst, n);
}

void Morphology::gray_topHat(cv::Mat &src, cv::Mat &dst, int n)
{
    cv::Mat src_temp;
    gray_openOperation(src, src_temp, n);

    if (src.rows == src_temp.rows && src.cols == src_temp.cols
        && src.type() == src_temp.type())
    {
        subtract(src, src_temp, dst);
    }
}

void Morphology::gray_bottomCap(cv::Mat &src, cv::Mat &dst, int n)
{
    cv::Mat src_temp;
    gray_closeOperation(src, src_temp, n);

    if (src.rows == src_temp.rows && src.cols == src_temp.cols
        && src.type() == src_temp.type())
    {
        subtract(src_temp, src, dst);
    }

}
View Code

imageProcessing.cpp

#include "imageProcessing.h"
#include 
#include 

#define pi 3.1415926

ImageProcessing::ImageProcessing() 
{

}

ImageProcessing::~ImageProcessing() 
{

}

int ImageProcessing::OtsuAlgThreshold(const cv::Mat image)
{
    if (image.channels() != 1)
    {
        std::cout << "Please input Gray-image!" << std::endl;
        return 0;
    }
    int T = 0;            //Otsu算法阈值
    double varValue = 0; //类间方差中间值保存
    double w0 = 0;        //前景像素点数所占比例
    double w1 = 0;        //背景像素点数所占比例
    double u0 = 0;        //前景平均灰度
    double u1 = 0;        //背景平均灰度
    double Histogram[256] = { 0 }; //灰度直方图,下标是灰度值,保存内容是灰度值对应的像素点总数
    uchar *data = image.data;
    double totalNum = image.rows*image.cols; //像素总数
                                             //计算灰度直方图分布,Histogram数组下标是灰度值,保存内容是灰度值对应像素点数
    for (int i = 0; i//为表述清晰,并没有把rows和cols单独提出来
    {
        for (int j = 0; j)
        {
            Histogram[data[i*image.step + j]]++;
        }
    }

    for (int i = 0; i<255; i++)
    {
        //每次遍历之前初始化各变量
        w1 = 0;        
        u1 = 0;        
        w0 = 0;        
        u0 = 0;
        //***********背景各分量值计算**************************
        for (int j = 0; j <= i; j++) //背景部分各值计算
        {
            w1 += Histogram[j];  //背景部分像素点总数
            u1 += j*Histogram[j]; //背景部分像素总灰度和
        }

        if (w1 == 0) //背景部分像素点数为0时退出
        {
            continue;
        }

        u1 = u1 / w1;        //背景像素平均灰度
        w1 = w1 / totalNum; // 背景部分像素点数所占比例
    
        //***********前景各分量值计算**************************
        for (int k = i + 1; k<255; k++)
        {
            w0 += Histogram[k];  //前景部分像素点总数
            u0 += k*Histogram[k]; //前景部分像素总灰度和
        }

        if (w0 == 0)        //前景部分像素点数为0时退出
        {
            break;
        }
        u0 = u0 / w0;        //前景像素平均灰度
        w0 = w0 / totalNum; // 前景部分像素点数所占比例
                            

        //***********类间方差计算******************************
        double varValueI = w0*w1*(u1 - u0)*(u1 - u0); //当前类间方差计算
        if (varValue<varValueI)
        {
            varValue = varValueI;
            T = i;
        }
    }
    return T;
}

void ImageProcessing::binary_filter(cv::Mat &src, cv::Mat &dst,int n)
{

    src.copyTo(dst);
    int edge = n / 2;

    float threshold_val = 255.0 * n;

    for (int i = edge; i < src.rows - edge; i++)
    {
        for (int j = edge; j < src.cols - edge; j++)
        {
            float sumOfWhitePix = 0;

            for (int x = -edge; x <= edge; x++)
            {
                for (int y = -edge; y <= edge; y++)
                {
                    
                    sumOfWhitePix += (float)(src.at(i + x, j + y));
                }
            }

            // 邻域内允许存在的前景像素个数阈值threshold_val
            if (sumOfWhitePix >= threshold_val)
            {
                dst.at(i, j) = 255;
            }

        }
    }
}

void ImageProcessing::objDetect(cv::Mat &src_bin, cv::Mat &dst)
{
    // 5*5滤波二值图,消除零星噪点的干扰 
    // 这里设置最小阈值个数 21
    cv::Mat bin_boxFilter, bin_findContours;
    binary_filter(src_bin, bin_boxFilter, 21);

    std::vector > contours;
    //注意第5个参数为CV_RETR_EXTERNAL,只检索外框  
    findContours(bin_boxFilter, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓

    if (contours.size() <= 0 || contours.size() > 99999)
    {
        src_bin.copyTo(dst);
        return;
    }

    std::vector boundRect(contours.size());
    //std::cout << contours.size() << std::endl;

    double area_max = 0;
    int i_max = 0;

    // 找出面积区域最大的轮廓及对应的contours下标
    for (int i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);

    //    std::cout << i << ", max_area = " << area << std::endl;

        if (area < 400)
            continue;

        if (area >= area_max)
        {
            area_max = area;
            i_max = i;
        }
    }

    //std::cout << "最大面积: " << area_max << ",对应下标 " << i_max << std::endl;

    CvPoint2D32f rectpoint[4];
    CvBox2D rect = cv::minAreaRect(cv::Mat(contours[i_max]));
    cvBoxPoints(rect, rectpoint); //获取4个顶点坐标  

    //与水平线的角度 
    float angle = rect.angle;
    //std::cout << angle << std::endl;

    //新建一个感兴趣的区域图,大小跟原图一样大  
    cv::Mat bin_roi(src_bin.rows, src_bin.cols, CV_8UC3);
    bin_roi.setTo(0); //颜色都设置为黑色

    //对得到的轮廓填充一下 
    drawContours(bin_roi, contours, i_max, cv::Scalar(255), CV_FILLED);
    //cv::imshow("ROI", bin_roi);

    cv::Mat mask;
    cvtColor(bin_roi, mask, CV_BGR2GRAY);

    cv::Mat src_bin_obj;
    src_bin.copyTo(src_bin_obj, mask);
    src_bin_obj.copyTo(dst);
    //imshow("目标区域", src_bin_obj);

    //创建一个旋转后的图像  
    //cv::Mat RotationedImg(src_bin.rows, src_bin.cols, CV_8UC1);
    //RotationedImg.setTo(0);

    //对RoiSrcImg进行旋转校正 
    //cv::Point2f center = rect.center;  //中心点  
    //cv::Mat M2 = getRotationMatrix2D(center, angle, 1);//计算旋转加缩放的变换矩阵 
    //warpAffine(src_bin, RotationedImg, M2, src_bin.size(), 1, 0, cv::Scalar(0));//仿射变换 
    //RotationedImg.copyTo(dst);


    //imshow("旋转之后", dst);
    //cv::waitKey(3000);
    //system("pause");
}

void ImageProcessing::morphology(cv::Mat &src, cv::Mat &dst) 
{
    Morphology morphology;
    cv::Mat src_resize, src_gray, src_median;

    resize(src, src_resize, cv::Size(int(src.cols*0.25), int(src.rows*0.25)));  // 尺度缩放
    cvtColor(src_resize, src_gray, CV_BGR2GRAY);        // 灰度化
 // morphology.medianFilter(src_gray, src_median, 5);   //中值滤波

    // *********************针对灰度图像(形态学操作)************************
    cv::Mat gray_erode, gray_dilate, gray_open,
        gray_close, gray_TopHat, gray_BottomCap;

    // morphology.gray_erode(src_gray, gray_erode, 3);              // 腐蚀
    // morphology.gray_dilate(src_gray, gray_dilate, 3);          // 膨胀
    // morphology.gray_openOperation(src_gray, gray_open, 5);    // 开运算
    // morphology.gray_closeOperation(src_gray, gray_close, 3);  // 闭运算
    // morphology.gray_topHat(src_gray, gray_TopHat, 13);         // 顶帽变换
    //morphology.gray_bottomCap(src_gray, gray_BottomCap, 13);   // 底帽变换

    // 利用opencv的morphologyEx函数实现黑帽变换
    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(13, 13));
    morphologyEx(src_gray, gray_BottomCap, cv::MORPH_BLACKHAT, element);

    cv::Mat bin;
    int otsu_val = OtsuAlgThreshold(gray_BottomCap);        // Otsu寻找全局最佳阈值
    threshold(gray_BottomCap, bin, otsu_val, 255, CV_THRESH_BINARY);  // 二值化

    //cv::Mat src_binary;                                                              //int otsu_val = OtsuAlgThreshold(src_gray);        // Otsu寻找全局最佳阈值
    //threshold(src_gray, src_binary, otsu_val, 255, CV_THRESH_BINARY);  // 二值化                                                                  // *********************针对二值图像(形态学操作)************************
    cv::Mat bin_erode, bin_dilate, bin_open, bin_close, bin_morphologicalGradiend;

    // morphology.binary_erode(src_binary, bin_erode, 7);          // 腐蚀
    // morphology.binary_dilate(src_binary, bin_dilate, 3);        // 膨胀  
    // morphology.binary_openOperation(src_binary, bin_open, 5);   // 开运算
    // morphology.binary_closeOperation(src_binary, bin_close, 7); // 闭运算

    //morphology.binary_MorphologicalGradiend(bin, bin_morphologicalGradiend, 7);

    cv::Mat obj_bin;
    objDetect(bin, obj_bin);

    cut_and_merge(obj_bin, dst);


    // 5*5滤波二值图,消除零星噪点的干扰 
    // 这里设置最小阈值个数 5
    //binary_filter(bin, dst, 21); 

    //imshow("dst_elnp", src_binary);
    //imwrite("D:\\image\\binary.bmp", src_gray);
    //imwrite("D:/image/样本数据_test/被处理后的的样本/武汉现场/alg/", gray_BottomCap);

    //bin_morphologicalGradiend.copyTo(dst);
    //gray_BottomCap.copyTo(dst);

    //waitKey(3000);
}

// 
float ImageProcessing::cal_contrast(cv::Mat &src_gray)
{
    if (!src_gray.data)
    {
        printf("计算对比度特征时,无法读取灰度图");
        return 0;
    }

    int image_count = 1;//要计算直方图的图像的个数
    int channels[1] = { 0 };//图像的通道

    cv::Mat out;//计算所得直方图
    int dims = 1;//得到直方图的维数
    int histsize[1] = { 256 };//直方图横坐标的子区间数
    float hrange[2] = { 0, 255 };//区间的总范围
    const float *ranges[1] = { hrange };//指针数组
    calcHist(&src_gray, image_count, channels, cv::Mat(), out, dims, histsize, ranges);

    int total = src_gray.rows*src_gray.cols;  // 像素点总数
    int half = total / 5;

    float numOfPix = 0, sumOfval = 0;
    float numOfBackPix = 0;        // 背景的像素个数统计
    float numOfFrontPix = 0;    // 前景的像素个数统计
    float sumOfBackVal = 0;        // 背景的像素灰度值之和统计
    float sumOfFrontVal = 0;    // 前景的像素灰度值之和统计
    float segmentPixLevel = 0;    // 背景和前景分割的像素灰度阈值

    for (int h = 0; h < out.rows; h++)
    {
        float binVal = out.at<float>(h);
        //std::cout << h << ": " << binVal << std::endl;

        numOfPix += binVal;
        sumOfval += (h*binVal);

        if (numOfPix < half)
        {
            sumOfBackVal = sumOfval;
            numOfBackPix = numOfPix;
            segmentPixLevel = h;
        }
    }

    sumOfFrontVal = sumOfval - sumOfBackVal;
    numOfFrontPix = numOfPix - numOfBackPix;

    float dark_mean = sumOfBackVal / numOfBackPix;
    float bright_mean = sumOfFrontVal / numOfFrontPix;
    
    //std::cout << dark_mean << ", " << bright_mean << std::endl;

    float contrast = bright_mean - dark_mean;
    
    return contrast;
}

int ImageProcessing::cal_edge(cv::Mat &src_gray)
{
    std::vector<float> temp1 = { -1,0,1,
                                -2,0,2,
                                 -1,0,1 };
    std::vector<float> temp2 = { -1,-2,-1,
                                 0,0,0,
                                 1,2,1 };
    std::vector<float> temp3 = { -2,-1,0,
                                 -1,0,1,
                                 0,1,2};
    std::vector<float> temp4 = { 0,1,2,
                                 -1,0,1,
                                 -2,-1,0 };

    int n = 3;
    int win_sum = n*n;
    int edge = n / 2;

    int t;
    float result1, result2, result3, result4;

    float segmentPixLevel = 180;
    int code1_count = 0, code2_count = 0, 
        code3_count = 0, code4_count = 0;

    std::vector<int> maxNum;
    maxNum.clear();

    for (int i = edge; i < src_gray.rows - edge; i++)
    {
        for (int j = edge; j < src_gray.cols - edge; j++)
        {
            t = 0;
            result1 = 0; result2 = 0; result3 = 0; result4 = 0;

            for (int y = -edge; y <= edge; y++)
            {
                for (int x = -edge; x <= edge; x++)
                {
                    float pix_val1 = temp1[t] * src_gray.at(i + x, j + y);
                    float pix_val2 = temp2[t] * src_gray.at(i + x, j + y);
                    float pix_val3 = temp3[t] * src_gray.at(i + x, j + y);
                    float pix_val4 = temp4[t] * src_gray.at(i + x, j + y);

                    result1 += pix_val1;
                    result2 += pix_val2;
                    result3 += pix_val3;
                    result4 += pix_val4;
                }
            }

            if (abs(result1/4) > segmentPixLevel)
                code1_count++;
            if (abs(result2/4) > segmentPixLevel)
                code2_count++;
            if (abs(result3/4) > segmentPixLevel)
                code3_count++;
            if (abs(result4/4) > segmentPixLevel)
                code4_count++;
        }
    }

    maxNum.push_back(code1_count);
    maxNum.push_back(code2_count);
    maxNum.push_back(code3_count);
    maxNum.push_back(code4_count);

    std::sort(maxNum.begin(), maxNum.end());
    int maxEdge = maxNum[maxNum.size() - 1];

    int width = src_gray.rows - 2 * edge;
    int height = src_gray.cols - 2 * edge;

    int totalOfPix = width* height;
    float ratio = maxEdge*1.0 / totalOfPix;

    /*std::cout << "像素总数 = " << totalOfPix << "最强边缘像素个数:" << maxEdge
        << ",占比" << ratio << std::endl;*/

    return ratio;
}

float ImageProcessing::cal_gradientDirection(cv::Mat &grad_x, cv::Mat &grad_y)
{
    if (!((grad_x.rows == grad_y.rows) && (grad_x.cols == grad_y.cols) && (grad_x.type() == grad_y.type())))
    {
        return 0;
    }

    float gx, gy;
    float Gxx = 0, Gyy = 0, Gxy = 0;
    float sum_down = 0, sum_up = 0;

    for (int i = 0; i < grad_x.rows; i++)
    {
        for (int j = 0; j < grad_x.cols; j++)
        {
            gx = (float)(grad_x.at(i, j));
            gy = (float)(grad_y.at(i, j));

            Gxx += gx*gx;
            Gyy += gy*gy;
            Gxy += abs(gx*gy);

        }
    }

    float num1 = pow((Gxx - Gyy), 2);
    float num2 = 4 * pow(Gxy, 2);
    float num = num1 + num2;

    sum_up += pow(num, 0.5);
    sum_down += (Gxx + Gyy);

    /*cout << "gx = " << gx << ", gy = " << gy << ", Gxx = " <<
        Gxx << ", Gyy = " << Gyy << ", Gxy = " << Gxy << endl;
    cout << "num1 = " << num1 << ", num2 = " << num2 << ", sum_up =" << sum_up << ", sum_down = " << sum_down << endl;
    */
    float coh = 0;
    if (sum_down != 0) 
        coh = sum_up / sum_down;
    //std::cout << coh << std::endl;

    return coh;
}

void ImageProcessing::Scharr2_x_and_y_grad(cv::Mat &src_gray, cv::Mat &x_grad, cv::Mat &y_grad)
{
    if (!src_gray.data)
    {
        printf("Scharr2_x_and_y_grad计算梯度时,无法读取灰度图");
        return;
    }
    //  Y 方向梯度
    std::vector<float> temp1 = { -3,0,3,
                                -10,0,10,
                                -3,0,3 };
    // X 方向梯度
    std::vector<float> temp2 = { -3,-10,-3,
                                0,0,0,
                                3,10,3 };

    x_grad = src_gray.clone();
    y_grad = src_gray.clone();
    x_grad.setTo(0);
    y_grad.setTo(0);

    int n = 3, t;
    int win_sum = n*n;
    int edge = n / 2;
    float result1, result2;

    for (int i = edge; i < src_gray.rows - edge; i++)
    {
        for (int j = edge; j < src_gray.cols - edge; j++)
        {
            t = 0;
            result1 = 0; 
            result2 = 0;

            for (int y = -edge; y <= edge; y++)
            {
                for (int x = -edge; x <= edge; x++)
                {
                    float pix_y = temp1[t] * src_gray.at(i + x, j + y);
                    float pix_x = temp2[t] * src_gray.at(i + x, j + y);

                    result1 += pix_y;
                    result2 += pix_x;

                    t++;
                }
            }

            float gradY = abs(result1 / 16);
            float gradX = abs(result2 / 16);

            x_grad.at(i, j) = gradX;
            y_grad.at(i, j) = gradY;
        }
    }

}

int ImageProcessing::imge_enhancement(cv::Mat &src_gray, cv::Mat &dst)
{
    if (!src_gray.data)
    {
        printf("增强图像质量时,无法读取灰度图...\n");
        return 0;
    }

    dst = src_gray.clone();
    dst.setTo(0);

    int M = src_gray.rows;
    int N = src_gray.cols;

    float perc1 = 0.06;
    float perc2 = 0.06;

    float H_l = perc1 *M*N;
    float H_h = perc2 *M*N;

    int image_count = 1;//要计算直方图的图像的个数
    int channels[1] = { 0 };//图像的通道

    cv::Mat out;//计算所得直方图
    int dims = 1;//得到直方图的维数
    int histsize[1] = { 256 };//直方图横坐标的子区间数
    float hrange[2] = { 0, 255 };//区间的总范围
    const float *ranges[1] = { hrange };//指针数组
    calcHist(&src_gray, image_count, channels, cv::Mat(), out, dims, histsize, ranges);

    float H_l_threshold = 0, H_h_threshold = 0;

    int H = 191, L = 64;

    for (int h = 0; h <= L; h++)
    {
        float binVal = out.at<float>(h);

        H_l_threshold += binVal;

        if (H_l_threshold > H_l)
        {
            H_l = H_l_threshold;
            L = h;
            break;
        }
    }

    for (int h = out.rows - 1; h >= H; h--)
    {
        float binVal = out.at<float>(h);
        H_h_threshold += binVal;

        if (H_h_threshold > H_h)
        {
            H_h = H_h_threshold;
            H = h;
            break;
        }

    }

    //printf("低阈值L灰度级:%d,共计:%f像素\n", L, H_l);
    //printf("高阈值H灰度级:%d,共计:%f像素\n", H, H_h);

    for (int i = 0; i < src_gray.rows; i++)
    {
        for (int j = 0; j < src_gray.cols; j++)
        {
            float pix_val = src_gray.at(i, j);

            if (pix_val < L)     pix_val = 0;
            else if (pix_val >H) pix_val = 255;
            else
            {
                pix_val = (pix_val - L + 1) * 256 / (H - L + 1);
                pix_val = abs(pix_val) - 1;
            }

            dst.at(i, j) = pix_val;
        }
    }

}

int ImageProcessing::edge_Scharr_4(cv::Mat &src_gray, cv::Mat &dst_grad, cv::Mat &dst_angle)
{
    if (!src_gray.data)
    {
        printf("计算4边缘图像时,无法读取图像...\n");
        return 0;
    }

    dst_grad= src_gray.clone();
    dst_grad.setTo(0);

    dst_angle = src_gray.clone();
    dst_angle.setTo(0);

    std::vector<float> temp1 = { -3,0,3,-10,0,10,-3,0,3 };    // 0 : 0
    std::vector<float> temp2 = { 0,-3,-10,3,0,-3,10,3,0 };    // 1: 45
    std::vector<float> temp3 = { -3,-10,-3,0,0,0,3,10,3 };    // 2: 90
    std::vector<float> temp4 = { -10,-3,0,-3,0,3,0,3,10 };    // 3: 135

    float result1, result2, result3, result4;
    
    std::vector<float> arr;
    
    int n = 3;
    int win_sum = n*n;
    int edge = n / 2;
    int main_direction, t;
    main_direction = 0;        // 主方向下标 0:0 1:45 2:90 3:135

    for (int i = edge; i < src_gray.rows - edge; i++)
    {
        for (int j = edge; j < src_gray.cols - edge; j++)
        {
            t = 0;
            result1 = 0; result2 = 0; result3 = 0; result4 = 0;
            arr.clear();

            for (int y = -edge; y <= edge; y++)
            {
                for (int x = -edge; x <= edge; x++)
                {
                    //    printf(" (%d,%d) = %f   ", y, x, temp4[t]);
                    float pix_val1 = temp1[t] * src_gray.at(i + x, j + y);
                    float pix_val2 = temp2[t] * src_gray.at(i + x, j + y);
                    float pix_val3 = temp3[t] * src_gray.at(i + x, j + y);
                    float pix_val4 = temp4[t] * src_gray.at(i + x, j + y);

                    result1 += pix_val1;
                    result2 += pix_val2;
                    result3 += pix_val3;
                    result4 += pix_val4;

                    t++;
                }
                //    printf("\n");
            }

            arr.push_back(abs(result1 / 16));
            arr.push_back(abs(result2 / 16));
            arr.push_back(abs(result3 / 16));
            arr.push_back(abs(result4 / 16));

            // 寻找最大值
            float maxValue = *std::max_element(arr.begin(), arr.end());
            // 4个方向的梯度(绝对)最大值作为该点的梯度幅值
            dst_grad.at(i, j) = maxValue;


            // 寻找最大值对应的下标
            int maxPosition = std::max_element(arr.begin(), arr.end()) - arr.begin();
            main_direction = maxPosition;
            float maxEdgeVal = main_direction * 85;

            if (maxEdgeVal > 255)
                maxEdgeVal = 255;

            // 4个方向的梯度最大值对应的方向作为该点的梯度方向
            dst_angle.at(i, j) = maxEdgeVal;
        }
    }

}

int ImageProcessing::Scharr4_gradConsis(cv::Mat &grad_bin, cv::Mat &grad_angle)
{
    if (!((grad_bin.rows == grad_angle.rows) && (grad_bin.cols == grad_angle.cols)))
    {
        return 0;
    }

    int edge_num, angle_0, angle_45, angle_90, angle_135;
    edge_num = 0;
    angle_0 = 0;
    angle_45 = 0;
    angle_90 = 0;
    angle_135 = 0;

    std::vector<int> angle_vec;
    angle_vec.clear();

    for (int i = 0; i < grad_bin.rows; i++)
    {
        for (int j = 0; j < grad_bin.cols; j++)
        {
            float pix_val = grad_bin.at(i, j);

            if (pix_val < 122)
                continue;

            float  pix_angle = grad_angle.at(i, j);

            if (pix_angle == 0)  angle_0++;
            else if (pix_angle == 85) angle_45++;
            else if (pix_angle == 170) angle_90++;
            else if (pix_angle == 255) angle_135++;

            edge_num++;
        }
    }

    if (edge_num  < 30)
        return 0;

    int angle_sum = angle_0 + angle_45 + angle_90 + angle_135;
    /* printf("angle_sum=%d, angle_0=%d, angle_45=%d,angle_90=%d,angle_135=%d\n",
         angle_sum, angle_0, angle_45, angle_90, angle_135);
     printf("edge_sum=%d \n", edge_num);*/

    angle_vec.push_back(angle_0);
    angle_vec.push_back(angle_45);
    angle_vec.push_back(angle_90);
    angle_vec.push_back(angle_135);

    int maxPosition = max_element(angle_vec.begin(), angle_vec.end()) - angle_vec.begin();
    int main_direction_sum = angle_vec[maxPosition];

    float main_direction_ratio = main_direction_sum*1.0 / angle_sum;
    float T2 = 0.37;

    if (main_direction_ratio <T2)
        return 0;

    return 1;
}

struct SecondMaxNumber
{

    float maxValue = -1;
    float secondValue = -1;
    int maxIndex = -1;
    int secondIndex = -1;
} maxAndSecond;

// 仅限4个数比较
void ImageProcessing::getSecondMaxNumber(std::vector<float> &arr, SecondMaxNumber &sm)
{

    float maxValue = *max_element(arr.begin(), arr.end());
    float minValue = *min_element(arr.begin(), arr.end());

    int maxPosition = max_element(arr.begin(), arr.end()) - arr.begin();
    int minPosition = min_element(arr.begin(), arr.end()) - arr.begin();


    float second_max = -1;
    int second_max_index = -1;
    for (int i = 0; i < arr.size(); i++)
    {
        //    printf("下标:%d, 值:%f\n", i, arr[i]);

        if (i == maxPosition || i == minPosition)
            continue;

        if (arr[i] > second_max)
        {
            second_max = arr[i];
            second_max_index = i;
        }

    }

    sm.maxValue = maxValue;
    sm.secondValue = second_max;
    sm.maxIndex = maxPosition;
    sm.secondIndex = second_max_index;

    //printf("最大值 = %f, 下标为:%d, 第二大值 = %f, 下标为:%d\n", maxValue, maxPosition, second_max, second_max_index);

}

int ImageProcessing::edge_Scharr_8(cv::Mat &src_gray, cv::Mat &dst_grad, cv::Mat &dst_angle)
{
    if (!src_gray.data)
    {
        printf("计算对比度特征时,无法读取灰度图");
        return 0;
    }

    dst_grad = src_gray.clone();
    dst_grad.setTo(0);

    dst_angle = src_gray.clone();
    dst_angle.setTo(0);

    std::vector<float> temp1 = { -3,0,3,-10,0,10,-3,0,3 }; // 0 : 0
    // 2: 45
    std::vector<float> temp3 = { 0,-3,-10,3,0,-3,10,3,0 };
    // 4: 90
    std::vector<float> temp2 = { -3,-10,-3,0,0,0,3,10,3 };
    // 6: 135
    std::vector<float> temp4 = { -10,-3,0,-3,0,3,0,3,10 };

    // 1:22.5   3:67.5  5:112.5  7:157.5
    float result1, result2, result3, result4;

    std::vector<float> arr;

    int n = 3;
    int win_sum = n*n;
    int edge = n / 2;
    int main_direction_angle, t;
    main_direction_angle = 0;    // 主方向梯度的角度值

    SecondMaxNumber sm;

    for (int i = edge; i < src_gray.rows - edge; i++)
    {
        for (int j = edge; j < src_gray.cols - edge; j++)
        {
            t = 0;
            result1 = 0; result2 = 0; result3 = 0; result4 = 0;
            arr.clear();

            for (int y = -edge; y <= edge; y++)
            {
                for (int x = -edge; x <= edge; x++)
                {
                    //    printf(" (%d,%d) = %f   ", y, x, temp4[t]);
                    float pix_val1 = temp1[t] * src_gray.at(i + x, j + y);
                    float pix_val2 = temp2[t] * src_gray.at(i + x, j + y);
                    float pix_val3 = temp3[t] * src_gray.at(i + x, j + y);
                    float pix_val4 = temp4[t] * src_gray.at(i + x, j + y);

                    result1 += pix_val1;
                    result2 += pix_val2;
                    result3 += pix_val3;
                    result4 += pix_val4;

                    t++;
                }
                //    printf("\n");
            }

            float val1 = abs(result1 / 16);
            float val2 = abs(result2 / 16);
            float val3 = abs(result3 / 16);
            float val4 = abs(result4 / 16);

            arr.push_back(val1);
            arr.push_back(val2);
            arr.push_back(val3);
            arr.push_back(val4);

            getSecondMaxNumber(arr, sm);

            //printf("最大值 = %f, 下标为:%d, 第二大值 = %f, 下标为:%d\n", sm.maxValue, sm.maxIndex, sm.secondValue, sm.secondIndex);

            int maxIndex = sm.maxIndex;
            int secondIndex = sm.secondIndex;

            float maxValue = sm.maxValue;
            float secondValue = sm.secondValue;

            // 8个方向,主边缘的判断
            if (maxValue == 0.0) 
            {
                main_direction_angle = 0;
            }
            else
            {
                int index_flag = abs(maxIndex - secondIndex);

                //    printf("index_flag = %d\n", index_flag);
                if (index_flag != 2)
                {
                    //printf("不是相邻角\n");
                    main_direction_angle = maxIndex;
                }

                if (index_flag == 1 || index_flag == 3)
                {
                    //    printf("是相邻角\n");

                    float grad_var = abs(maxValue - secondValue);
                    float grad_threshold = 3; // ????

                    if (grad_var > grad_threshold) 
                    {
                        main_direction_angle = maxIndex;
                    }
                    else
                    {
                        int angle_sum = maxIndex + secondIndex;
                        switch (angle_sum)
                        {
                        case 1: main_direction_angle = 4;
                            break;
                        case 3:
                            if (maxIndex == 0 || maxIndex == 3)
                                main_direction_angle = 7;
                            else
                                main_direction_angle = 5;
                            break;
                        case 5: main_direction_angle = 6;
                            break;
                        default:
                            main_direction_angle = 0;
                            break;
                        }
                    }
                }
            }

            float angle_val = 0;
            if (main_direction_angle < 4)
            {
                angle_val = main_direction_angle * 64;
            }
            else
            {
                angle_val = (main_direction_angle - 4) * 64 + 32;
            }

            if (angle_val > 255)
                angle_val = 254;
            dst_angle.at(i, j) = angle_val;

            dst_grad.at(i, j) = maxValue;

        }
    }

    //imwrite("D:\\image\\test\\dst_angle_4.bmp", dst);
}

int ImageProcessing::Scharr8_gradConsis(cv::Mat &grad_bin, cv::Mat &grad_angle)
{
    if (!((grad_bin.rows == grad_angle.rows) && (grad_bin.cols == grad_angle.cols)))
    {
        return 0;
    }

    int edge_num = 0;
    // 0 85 170 255 
    int angle_0 = 0, angle_45 = 0, angle_90 = 0, angle_135 = 0,
        angle_22 = 0, angle_67 = 0, angle_112 = 0, angle_157 = 0;
    std::vector<int> angle_vec;

    for (int i = 0; i < grad_bin.rows; i++)
    {
        for (int j = 0; j < grad_bin.cols; j++)
        {
            float pix_val = grad_bin.at(i, j);

            if (pix_val < 122)
                continue;

            float  pix_angle = grad_angle.at(i, j);
            //printf("pix_val = %f, pix_angle=%f \n", pix_val, pix_angle);

            if (pix_angle == 0)  angle_0++;
            else if (pix_angle == 64) angle_45++;
            else if (pix_angle == 128) angle_90++;
            else if (pix_angle == 192) angle_135++;
            else if (pix_angle == 32) angle_22++;
            else if (pix_angle == 96) angle_67++;
            else if (pix_angle == 160) angle_112++;
            else if (pix_angle == 254) angle_157++;

            edge_num++;
        }
    }

    int angle_sum = angle_0 + angle_45 + angle_90 + angle_135
        + angle_22 + angle_67 + angle_112 + angle_157;

    // printf("angle_sum=%d, angle_0=%d, angle_45=%d,angle_90=%d,angle_135=%d, angle_22=%d, angle_67=%d,angle_112=%d,angle_157=%d\n",
    //    angle_sum, angle_0, angle_45, angle_90, angle_135, angle_22 ,angle_67 , angle_112 , angle_157);
    // printf("edge_sum=%d \n", edge_num);

    if (angle_sum < 8)
    {
        return 0;
    }

    angle_vec.push_back(angle_0);
    angle_vec.push_back(angle_45);
    angle_vec.push_back(angle_90);
    angle_vec.push_back(angle_135);
    angle_vec.push_back(angle_22);
    angle_vec.push_back(angle_67);
    angle_vec.push_back(angle_112);
    angle_vec.push_back(angle_157);

    int maxPosition = max_element(angle_vec.begin(), angle_vec.end()) - angle_vec.begin();
    int main_direction_sum = angle_vec[maxPosition];

    float main_direction_ratio = main_direction_sum*1.0 / angle_sum;

    float T2 = 0.35;

    if (main_direction_ratio <T2)
        return 0;

    return 1;
}

void ImageProcessing::codeBar_detect(cv::Mat &merge_bin, cv::Mat &src, cv::Mat &dst)
{
    
    //新建一个感兴趣的区域图,大小跟原图一样大  
    cv::Mat bin_roi;
    bin_roi = merge_bin.clone();

    std::vector > contours;
    //注意第5个参数为CV_RETR_EXTERNAL,只检索外框  
    findContours(merge_bin, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓

    if (contours.size() <= 0 || contours.size() > 99999)
    {
        merge_bin.copyTo(dst);
        return;
    }

    double area_max, least_area;
    //  条码宽高比 7:2
    least_area = 16 * 16* 21;
    area_max = 0;

    //新建一个感兴趣的区域图,大小跟原图一样大  
    cv::Mat dst_roi(src.rows, src.cols, CV_8UC3);
    dst_roi = src.clone();
    

    // 绘制最小外接矩形集合
    std::vector boundRect(contours.size());
    // 定义最小外接矩形集合
    std::vector box(contours.size());
    cv::Point2f rect[4];

    // 找出面积区域最大的轮廓及对应的contours下标
    for (int i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);

        //    std::cout << i << ", max_area = " << area << std::endl;

        // 如果轮廓的面积小于设置的阈值,就抛弃
        if (area < least_area)
            continue;

        box[i] = cv::minAreaRect(cv::Mat(contours[i])); // 计算每个轮廓的最小外接矩形
        boundRect[i] = cv::boundingRect(cv::Mat(contours[i]));

        box[i].points(rect);  // 把最小外接矩形四个端点复制给rect数组

        double len_1, len_2, H_W_ratio = 0;
        len_1 = pow(rect[0].x - rect[1].x, 2) + pow(rect[0].y - rect[1].y, 2);
        len_1 = pow(len_1, 0.5);
        len_2 = pow(rect[1].x - rect[2].x, 2) + pow(rect[1].y - rect[2].y, 2);
        len_2 = pow(len_2, 0.5);

        if (len_1 > len_2)
            H_W_ratio = len_1 / len_2;
        else
            H_W_ratio = len_2 / len_1;

        if (H_W_ratio < 2.5)
            continue;

        cv::circle(bin_roi, cv::Point(box[i].center.x, box[i].center.y), 
            5, cv::Scalar(255), -1, 8);   // 绘制最小外接矩形的中心点

        cv::circle(dst_roi, cv::Point(box[i].center.x, box[i].center.y),
            5, cv::Scalar(0,255,0), -1, 8);   // 绘制最小外接矩形的中心点

        cv::rectangle(bin_roi, cv::Point(boundRect[i].x, boundRect[i].y), 
            cv::Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height), cv::Scalar(0, 255, 0), 2, 8);

        for (int j = 0; j<4; j++)
        {
            cv::line(bin_roi, rect[j], rect[(j + 1) % 4], cv::Scalar(255), 2, 8);  //绘制最小外接矩形每条边
            cv::line(dst_roi, rect[j], rect[(j + 1) % 4], cv::Scalar(0, 255, 0), 2, 8); //绘制最小外接矩形每条边
        }

    }

    dst = dst_roi.clone();
}

void ImageProcessing::cut_and_merge(cv::Mat &src, cv::Mat &dst)
{
    if (!src.data)
    {
        printf("读取src_bin错误");
        return;
    }

    cv::Mat src_gray, src_resize, src_gauss, src_enhance,
        src_Scharr4_grad, src_Scharr4_angle, src_Scharr4_grad_bin,
        src_Scharr8_grad, src_Scharr8_angle, src_Scharr8_grad_bin,
        src_Scharr2_x_grad, src_Scharr2_y_grad;

    cvtColor(src, src_gray, CV_BGR2GRAY);

    //resize(src_gray, src_resize, cv::Size(src_gray.cols, src_gray.rows),0,0, cv::INTER_LINEAR);  // 尺度缩放
    //src_gray.copyTo(src_resize);

    // 高斯滤波
    //cv::GaussianBlur(src_resize, src_gauss, cv::Size(5, 5), 3, 3);

    // 图像增强
    imge_enhancement(src_gray, src_enhance);

    // **************************************基于分块的边缘一致性检测定位算法**************************
    // Scharr_4方向边缘检测
    edge_Scharr_4(src_enhance, src_Scharr4_grad, src_Scharr4_angle);
    int ostu_val_4 = OtsuAlgThreshold(src_Scharr4_grad);
    threshold(src_Scharr4_grad, src_Scharr4_grad_bin, ostu_val_4, 255, CV_THRESH_BINARY);
    
    // Scharr_8方向边缘检测
    /*edge_Scharr_8(src_enhance, src_Scharr8_grad, src_Scharr8_angle);
    int ostu_val_8 = OtsuAlgThreshold(src_Scharr8_grad);
    threshold(src_Scharr8_grad, src_Scharr8_grad_bin, ostu_val_8, 255, CV_THRESH_BINARY);*/

    //***********************************************************************************


    // ********************************基于分块的(梯度)梯度方向一致性检测定位算法***********************
    
    // 利用 Scharr 来滤波检测边缘
    //cv::Mat grad_x, grad_y, abs_grad_x, abs_grad_y, grad_val, grad_angle;
    //Scharr(src_resize, grad_x, CV_16S, 1, 0, 1, 0, cv::BORDER_DEFAULT);
    //Scharr(src_resize, grad_y, CV_16S, 0, 1, 1, 0, cv::BORDER_DEFAULT);
    //convertScaleAbs(grad_x, abs_grad_x);
    //convertScaleAbs(grad_y, abs_grad_y);
    //// 梯度幅值 
    //cv::addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad_val);
    // 梯度的角度 正切值 tan(angle) = Gy/Gx 
    // Gy和Gx 本身带正负号,即方向值
    // 角度的取值范围是(-pi/2, pi/2]
    // cv::divide(grad_y, grad_x, grad_angle);

    // Scharr2_x_and_y_grad(src_enhance, src_Scharr2_x_grad, src_Scharr2_y_grad);
    
    //**********************************************************************************************

    int width = src_gray.cols;
    int height = src_gray.rows;

    int SUB_WIDTH = 16;
    int SUB_HEIGHT = 16;
    int M = width / SUB_WIDTH;
    int N = height / SUB_HEIGHT;

    // 图像分块
    std::vector ceil_img,
        ceil_grad_x, ceil_grad_y,        // 分块计算梯度方向一致性时,X和Y方向梯度
        ceil_grad_val, ceil_grad_angle, // 边缘方向一致性检测时,梯度的幅值和角度
        ceil_Scharr4_grad_bin, ceil_Scharr4_angle,
        ceil_Scharr8_grad_bin, ceil_Scharr8_angle;

    std::vector<int> ceil_flag;  // 0: 背景 1:前景
    std::vector<float> contrastFeature, edgeFeature, cohFeature;
    std::vector<int> edge_consistency, Scharr4_feature, Scharr8_feature;

    cv::Mat image_cut, roi_img;
    cv::Mat grad_val_cut, grad_val_roi,
        grad_angle_cut, grad_angle_roi,
        grad_x_cut, grad_x_roi,
        grad_y_cut, grad_y_roi,
        Scharr4_grad_bin_cut, Scharr4_grad_bin_roi,
        Scharr4_angle_cut, Scharr4_angle_roi,
        Scharr8_grad_bin_cut, Scharr8_grad_bin_roi,
        Scharr8_angle_cut, Scharr8_angle_roi;

    for (int j = 0; j < N; j++)
    {
        for (int i = 0; i < M; i++)
        {
            cv::Rect rect(i * SUB_WIDTH, j*SUB_HEIGHT, SUB_WIDTH, SUB_HEIGHT);

            // 原图的灰度图分块
            image_cut = cv::Mat(src_gray, rect);
            roi_img = image_cut.clone();
            ceil_img.push_back(roi_img);

            // ******************* 1. 边缘方向一致性 ****************************
            // Scharr4 滤波边缘检测后分块  
            // 梯度幅值
            /*Scharr4_grad_bin_cut = cv::Mat(src_Scharr4_grad_bin, rect);
            Scharr4_grad_bin_roi = Scharr4_grad_bin_cut;
            ceil_Scharr4_grad_bin.push_back(Scharr4_grad_bin_roi);*/

            // 梯度角度
            /*Scharr4_angle_cut = cv::Mat(src_Scharr4_angle, rect);
            Scharr4_angle_roi = Scharr4_angle_cut;
            ceil_Scharr4_angle.push_back(Scharr4_angle_roi);*/

            // Scharr8 滤波边缘检测后分块  
            // 梯度幅值
            /*Scharr8_grad_bin_cut = cv::Mat(src_Scharr8_grad_bin, rect);
            Scharr8_grad_bin_roi = Scharr8_grad_bin_cut;
            ceil_Scharr8_grad_bin.push_back(Scharr8_grad_bin_roi);*/

            // 梯度角度
            /*Scharr8_angle_cut = cv::Mat(src_Scharr8_angle, rect);
            Scharr8_angle_roi = Scharr8_angle_cut;
            ceil_Scharr8_angle.push_back(Scharr8_angle_roi);*/

            // ********************* 2. 梯度方向一致性 **************************
            // X方向梯度图分块
            //grad_x_cut = cv::Mat(src_Scharr2_x_grad, rect);
            //grad_x_roi = grad_x_cut.clone();
            //ceil_grad_x.push_back(grad_x_roi);

            //// Y方向梯度图分块
            //grad_y_cut = cv::Mat(src_Scharr2_y_grad, rect);
            //grad_y_roi = grad_y_cut.clone();
            //ceil_grad_y.push_back(grad_y_roi);

            // ******************************************************************

        }
    }

    //分块处理
    for (int t = 0; t < M*N; t++)
    {
        //imshow(to_string(name[t]), ceil_img[t]);
        
        // ******************************* 1.边缘方向一致性 **********************************
        // 4方向滤波
        /*int Scharr4_flag = Scharr4_gradConsis(ceil_Scharr4_grad_bin[t], ceil_Scharr4_angle[t]);
        Scharr4_feature.push_back(Scharr4_flag);*/

        // 8方向滤波
        /*int Scharr8_flag = Scharr8_gradConsis(ceil_Scharr8_grad_bin[t], ceil_Scharr8_angle[t]);
        Scharr8_feature.push_back(Scharr8_flag);*/

        // ******************************* 2.分块计算梯度方向一致性 ***************************
        /*float coh = cal_gradientDirection(ceil_grad_x[t], ceil_grad_y[t]);
        cohFeature.push_back(coh);*/

        // ******************************** 3. 对比度和边缘方向强度*********************************
        // 分块计算对比度特征
        float contrastVal = cal_contrast(ceil_img[t]);
        contrastFeature.push_back(contrastVal);
        
        // 分块计算边缘强度
    /*    float edgeVal_ratio = cal_edge(ceil_img[t]);
        edgeFeature.push_back(edgeVal_ratio);*/

    }

    //图像合并
    int t = 0;
    cv::Mat MergeImage(cv::Size(width, height), CV_LOAD_IMAGE_GRAYSCALE);
    for (int j = 0; j < N; j++)
    {
        for (int i = 0; i < M; i++)
        {
            cv::Rect ROI(i*SUB_WIDTH, j*SUB_HEIGHT, SUB_WIDTH, SUB_HEIGHT);

            // ******************************* 1.边缘方向一致性 **********************************
        /*    if (Scharr4_feature[t] > 0)
                ceil_img[t].setTo(255);
            else
                ceil_img[t].setTo(0);

            ceil_img[t].copyTo(MergeImage(ROI));*/

            // ******************************* 2.分块计算梯度方向一致性 ***************************
            //if (cohFeature[t] >= 0.7) {

            //    //ceil_img[t].copyTo(MergeImage(ROI));
            //    ceil_img[t].setTo(255);
            //    ceil_img[t].copyTo(MergeImage(ROI));
            //}
            //else
            //{
            //    ceil_img[t].setTo(0);
            //    ceil_img[t].copyTo(MergeImage(ROI));
            //}
            

            // ******************************** 3. 对比度和边缘方向强度*********************************

            if (contrastFeature[t] >= 16 && Scharr4_feature[t] > 0)
            //(    edgeFeature[t] >= 0.7)
            {
                //ceil_img[t].copyTo(MergeImage(ROI));
                ceil_img[t].setTo(255);
            }
            else 
            {
                ceil_img[t].setTo(0);
                
            }

            ceil_img[t].copyTo(MergeImage(ROI));
            //**************************************************************************************
        
            t++;
        }
    }

    /*dst = MergeImage.clone();
    return;*/

    // 对合并之后的图像进行开运算操作
    // 去除背景干扰的影响、连接或闭合条码区域
    cv::Mat MergeImage_bin, MergeImage_openOpera;
    //int otsu_val = OtsuAlgThreshold(MergeImage);        // Otsu寻找全局最佳阈值
    //threshold(MergeImage, MergeImage_bin, otsu_val, 255, CV_THRESH_BINARY);  // 二值化

    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(18, 18));
    morphologyEx(MergeImage, MergeImage_openOpera, cv::MORPH_OPEN, element);

    cv::Mat MergeImge_openOpera_roi;
    codeBar_detect(MergeImage_openOpera, src, dst);
    //codeBar_detect(MergeImage, src, dst);



    //MergeImage.copyTo(dst);
    

}

void ImageProcessing::removeBack(cv::Mat &src_gray, cv::Mat &dst)
{
    if (!src_gray.data)
    {
        printf("增强图像质量时,无法读取灰度图...\n");
        return;
    }

    dst = src_gray.clone();
    dst.setTo(0);

    int M = src_gray.rows;
    int N = src_gray.cols;

    float perc1 = 0.06;
    float perc2 = 0.2;

    float H_l = perc1 *M*N;
    float H_h = perc2 *M*N;

    int image_count = 1;//要计算直方图的图像的个数
    int channels[1] = { 0 };//图像的通道

    cv::Mat out;//计算所得直方图
    int dims = 1;//得到直方图的维数
    int histsize[1] = { 256 };//直方图横坐标的子区间数
    float hrange[2] = { 0, 255 };//区间的总范围
    const float *ranges[1] = { hrange };//指针数组
    calcHist(&src_gray, image_count, channels, cv::Mat(), out, dims, histsize, ranges);

    float H_l_threshold = 0, H_h_threshold = 0;

    int H = 191, L = 64;

    for (int h = 0; h <= L; h++)
    {
        float binVal = out.at<float>(h);

        H_l_threshold += binVal;

        if (H_l_threshold > H_l)
        {
            H_l = H_l_threshold;
            L = h;
            break;
        }
    }

    for (int h = out.rows - 1; h >= H; h--)
    {
        float binVal = out.at<float>(h);
        H_h_threshold += binVal;

        if (H_h_threshold > H_h)
        {
            H_h = H_h_threshold;
            H = h;
            break;
        }

    }

    //printf("低阈值L灰度级:%d,共计:%f像素\n", L, H_l);
    //printf("高阈值H灰度级:%d,共计:%f像素\n", H, H_h);

    for (int i = 0; i < src_gray.rows; i++)
    {
        for (int j = 0; j < src_gray.cols; j++)
        {
            float pix_val = src_gray.at(i, j);

            if (pix_val < L)     pix_val = 0;
            else if (pix_val >H) pix_val = 255;
            else
            {
                pix_val = (pix_val - L + 1) * 256 / (H - L + 1);
                pix_val = abs(pix_val) - 1;
            }

            dst.at(i, j) = pix_val;
        }
    }
}

void ImageProcessing::obj_Segment(cv::Mat &src_gray, cv::Mat &src_bin, int &otsu_Th, int &ostu_second)
{
    if (!((src_bin.rows == src_gray.rows) && (src_bin.cols == src_gray.cols)))
    {
        return;
    }

    cv::Mat src_bin_copy = src_bin.clone();

    std::vector > contours;
    //注意第5个参数为CV_RETR_EXTERNAL,只检索外框  
    findContours(src_bin, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓

    if (contours.size() <= 0 || contours.size() > 99999)
    {
        //src_bin.copyTo(dst);
        return;
    }

    double least_area = 64 * 48 ;
    double area_max = 0;
    int i_max = 0;

    // 绘制最小外接矩形集合
    std::vector boundRect(contours.size());
    // 定义最小外接矩形集合
    std::vector box(contours.size());
    cv::Point2f rect[4];

    for (int i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);

        //    std::cout << i << ", max_area = " << area << std::endl;

        if (area < least_area)
            continue;

        box[i] = cv::minAreaRect(cv::Mat(contours[i])); // 计算每个轮廓的最小外接矩形
        boundRect[i] = cv::boundingRect(cv::Mat(contours[i]));
        box[i].points(rect);  // 把最小外接矩形四个端点复制给rect数组

        std::vector<float> pix_x, pix_y;

        for (int t = 0; t < 4; t++)
        {
            pix_x.push_back(rect[t].x);
            pix_y.push_back(rect[t].y);
        }

        float x_maxValue = *max_element(pix_x.begin(), pix_x.end());
        float x_minValue = *min_element(pix_x.begin(), pix_x.end());
        float y_maxValue = *max_element(pix_y.begin(), pix_y.end());
        float y_minValue = *min_element(pix_y.begin(), pix_y.end());

        int x_left, y_left, rect_width, rect_height;
        x_left = x_minValue;
        y_left = y_minValue;
        rect_width = x_maxValue - x_minValue;
        rect_height = y_maxValue - y_minValue;

        if (x_left < 0) x_left = 0;
        if (y_left < 0) y_left = 0;

        if (x_left + rect_width > src_gray.cols - 1) rect_width = src_gray.cols - 1  - x_left;
        if (y_left + rect_height > src_gray.rows - 1) rect_height = src_gray.rows - 1 - y_left;

        //printf("x_left = %d, y_left=%d, rect_width=%d, rect_height=%d\n", x_left, y_left, rect_width, rect_height);

        cv::Rect rect(x_left, y_left, rect_width, rect_height);
        cv::Mat image_cut = cv::Mat(src_gray, rect);
        cv::Mat roi_copy = image_cut.clone();

        cv::Mat roi_copy_bin, roi_bin_opera;
        int ostu_second_val = OtsuAlgThreshold(roi_copy);

         //无高光
        if (otsu_Th <100)
        {
            ostu_second_val = otsu_Th * 0.3 + ostu_second_val*0.7;
            ostu_second_val *= 1.1;
        }
        else // 两种可能:1) 图像区域亮+背景暗 2) 图像区域亮+高光+背景暗
        {
            //ostu_second_val = otsu_Th * 0.5 + ostu_second_val*0.5;
            ostu_second_val *= 1.3;

            if (ostu_second_val > 255)
                ostu_second_val = 230;
        }
    
        ostu_second = ostu_second_val;

        //printf(" 第二次: 局部阈值 = %d\n", ostu_second);
        cv::threshold(roi_copy, roi_copy_bin, ostu_second, 255, CV_THRESH_BINARY);

    /*    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3));
        cv::dilate(roi_copy_bin, roi_bin_opera, element);*/
        //cv::morphologyEx(roi_copy_bin, roi_bin_opera, cv::MORPH_OPEN, element);

        roi_copy_bin.copyTo(src_bin_copy(rect));
    }

    src_bin = src_bin_copy.clone();

}

void ImageProcessing::find_ROI(cv::Mat &src, cv::Mat &src_bin, cv::Mat &dst)
{
    if (!((src_bin.rows == src.rows) && (src_bin.cols == src.cols)))
    {
        return;
    }

    std::vector > contours;
    //注意第5个参数为CV_RETR_EXTERNAL,只检索外框  
    findContours(src_bin, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓

    if (contours.size() <= 0 || contours.size() > 99999)
    {
        //src_bin.copyTo(dst);
        return;
    }

    cv::Mat src_gray, src_gray_bin, src_roi;
    //cvtColor(src, src_gray, CV_BGR2GRAY);

    src_roi = src.clone();

    double least_area = 64 * 48*0.5;
    double area_max = 0;
    int i_max = 0;

    // 绘制最小外接矩形集合
    std::vector boundRect(contours.size());
    // 定义最小外接矩形集合
    std::vector box(contours.size());
    cv::Point2f rect[4];

    for (int i = 0; i < contours.size(); i++)
    {
        double area = contourArea(contours[i]);

        //    std::cout << i << ", max_area = " << area << std::endl;

        if (area < least_area)
            continue;

        box[i] = cv::minAreaRect(cv::Mat(contours[i])); // 计算每个轮廓的最小外接矩形
        boundRect[i] = cv::boundingRect(cv::Mat(contours[i]));

        box[i].points(rect);  // 把最小外接矩形四个端点复制给rect数组

        /*cv::rectangle(src_roi, cv::Point(boundRect[i].x, boundRect[i].y),
            cv::Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height), cv::Scalar(0, 255, 0), 2, 8);
*/

        cv::circle(src_roi, cv::Point(box[i].center.x, box[i].center.y),
            5, cv::Scalar(0, 255, 0), -1, 8);   // 绘制最小外接矩形的中心点

    /*    cv::rectangle(src_roi, cv::Point(boundRect[i].x, boundRect[i].y),
            cv::Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height), cv::Scalar(0, 255, 0), 2, 8);
*/

        for (int j = 0; j<4; j++)
        {
            cv::line(src_roi, rect[j], rect[(j + 1) % 4], cv::Scalar(255), 2, 8);  //绘制最小外接矩形每条边
            cv::line(src_roi, rect[j], rect[(j + 1) % 4], cv::Scalar(0, 255, 0), 2, 8); //绘制最小外接矩形每条边
        }

    }


    dst = src_roi.clone();
    
}

void ImageProcessing::detect_medu(cv::Mat &src, cv::Mat &dst, int &otsu_val, int &ostsu_second)
{
    if (!src.data)
    {
        printf("读取src_bin错误");
        return;
    }

    cv::Mat src_gray, src_gray_bin;
    cvtColor(src, src_gray, CV_BGR2GRAY);

    otsu_val = OtsuAlgThreshold(src_gray);
    
    //printf("第一次全局阈值 =%d, ", otsu_val);

    cv::threshold(src_gray, src_gray_bin, otsu_val, 255, CV_THRESH_BINARY);  // 二值化
    
    obj_Segment(src_gray, src_gray_bin, otsu_val, ostsu_second);


    /*cv::Mat src_gray_roi;
    src_gray.copyTo(src_gray_roi, src_gray_bin);

    cv::threshold(src_gray_roi, dst, 195, 255, CV_THRESH_BINARY);*/

    find_ROI(src, src_gray_bin, dst);
    


}

void ImageProcessing::imgProcessMain(cv::Mat &src, cv::Mat &dst, int &otsu_Th, int &ostsu_second)
{
    // 形态学处理
    //morphology(src, dst);

    // 分块定位条码
    //cut_and_merge(src, dst);

    detect_medu(src, dst, otsu_Th, ostsu_second);


}
View Code

main.cpp

#include "imageProcessing.h"
#include 
#include 
#include 
#include 

void readAllImage(const std::string &allImageNameTxtFile, std::vectorstring> &imageNames)
{
    //读入训练样本图片路径和类别
    std::ifstream testData(allImageNameTxtFile, std::ios::out);
    std::string line;

    while (getline(testData, line)) 
    {
        if (line.empty())
            continue;

        std::stringstream stream(line);
        std::string imageName, imageName1,imageName2;
        stream >> imageName1;
        stream >> imageName2;

        imageName = imageName1 + " " + imageName2;

        imageNames.push_back(imageName );
    }

    testData.close();
}

void readAndSaveToTxt() 
{
    std::ifstream myfile("D:/image/样本数据_test/样本/武汉现场/testSample.txt");
    std::ofstream outfile("D:/image/样本数据_test/被处理后的的样本/武汉现场/out.txt", std::ofstream::app);

    std::string temp;

    if (!myfile.is_open()) 
    {
        std::cout << "未成功打开文件" << std::endl;
    }

    while (getline(myfile, temp))
    {
        outfile << temp;
        outfile << std::endl;
    }
    myfile.close();

}

int main()
{

    // readAndSaveToTxt();

    std::string readPath = "D:/image/线扫和多条码样本/all/";
    std::string savePath = "D:/image/线扫和多条码样本/处理后的样本/find_roi_02/";
    std::string imageNameTxt = "D:/image/线扫和多条码样本/testSample.txt";
    std::ofstream outfile("D:/image/线扫和多条码样本/alg_mine_out.txt", std::ofstream::app);

    std::vectorstring> imagePaths;
    readAllImage(imageNameTxt, imagePaths);
     
    std::cout << imagePaths.size() << std::endl;

    ImageProcessing imgProcess;

    double start, end;
    double cost;
    int img_global_val = 0, img_ostu_second = 0;

    for (int i = 0; i <= imagePaths.size() - 1; i++) 
    {
        start = clock();
        cv::Mat src, dst;
        std::string imgPath = readPath + imagePaths[i];

        src = cv::imread(imgPath, 1);
        cv::Mat src_resize;
        cv::resize(src, src_resize, cv::Size(640, 480));

        if (src_resize.empty())
        {
            printf("could not load image...\n");
            continue;
        }

        imgProcess.imgProcessMain(src_resize, dst, img_global_val, img_ostu_second);
        //imwrite(savePath + imagePaths[i], dst);

        end = clock();
        cost = end - start;
        std::cout << i << " , " << imgPath << ",  cost time: " << cost << "ms" << std::endl;

        std::string outInfo = std::to_string(i) + " " + imgPath + " costTime(ms): ," + std::to_string(cost) +
            "," + std::to_string(img_global_val) + ",  " + std::to_string(img_ostu_second);

        outfile << outInfo;
        outfile << std::endl;
    }

    system("pause");

    return 0;
}
View Code

 

你可能感兴趣的:((四)条码定位三种方案的相关代码)