激光条纹中心提取——骨架提取法

粗暴的骨架法

将形态学算法引入光条纹中心提取 单纯提取骨架导致精度不高;反复细化操作导致运算速度降低
在这里插入图片描述

上代码

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <stdio.h>
using namespace std;
using namespace cv;

//-----------------------------------【全局变量声明部分】--------------------------------------
//		描述:全局变量声明
//-----------------------------------------------------------------------------------------------
Mat g_srcImage, g_dstImage, g_midImage, g_grayImage, imgHSVMask;//原始图、中间图和效果图
int threshold_value = 60;	//阈值
int size = 800;				//面积因子
float start_time, end_time, sum_time;	//处理时间

//-----------------------------------【全局函数声明部分】--------------------------------------
//		描述:全局函数声明
//-----------------------------------------------------------------------------------------------
void ThinSubiteration1(Mat& pSrc, Mat& pDst);
void ThinSubiteration2(Mat& pSrc, Mat& pDst);
void normalizeLetter(Mat& inputarray, Mat& outputarray);
void Line_reflect(Mat& inputarray, Mat& outputarray);
void Delete_smallregions(Mat& pSrc, Mat& pDst);

//-----------------------------------【main( )函数】--------------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main()
{
    //载入原始图
    g_srcImage = imread("C:\\Users\\Charm Luo\\Desktop\\Laser extraction\\opncv提取中心线\\train\\129.png");  //读取素材图
    // 加载的图片的大小
    cout << g_srcImage.size() << endl;
    //记录开始处理的时间
    start_time = getTickCount();	

    //显示灰度图  
    cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);
    cout << g_grayImage.size() << endl;
    imshow("【灰度图】", g_grayImage);

    //二值化
    threshold(g_grayImage, imgHSVMask, threshold_value, 255, THRESH_BINARY);
    imshow("【二值化】", imgHSVMask);

    g_midImage = Mat::zeros(imgHSVMask.size(), CV_8UC1);  //绘制

    //去除小面积区域
    Delete_smallregions(imgHSVMask, g_midImage);
    imshow("【目标图】", g_midImage);
    imwrite("Target_image129.jpg", g_midImage);

    //normalizeLetter显示效果图  
    normalizeLetter(g_midImage, g_dstImage);
    imshow("【效果图】", g_dstImage);

    //曲线映射到原图
    /*threshold(g_grayImage, g_midImage, threshold_value, 255, THRESH_BINARY);	
    imshow("【二值化图】", g_midImage);	*/										
    Line_reflect(g_dstImage, g_midImage);
    imshow("【映射图】", g_midImage);
    imwrite("Reflect_image129.jpg", g_midImage);

    //转换类型,保存skeleton图像
    normalize(g_dstImage, g_midImage, 0, 255, NORM_MINMAX, CV_8U);
    imwrite("Thinning_image129.jpg", g_midImage);

    //计算运行时间
    end_time = getTickCount();
    // 算法所用时间
    sum_time = (end_time - start_time) / getTickFrequency();
    printf("%lf s", sum_time);

    waitKey(0);

    return 0;

}


void ThinSubiteration1(Mat& pSrc, Mat& pDst) {
    int rows = pSrc.rows;
    int cols = pSrc.cols;
    pSrc.copyTo(pDst);
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (pSrc.at<float>(i, j) == 1.0f) {
                /// get 8 neighbors
                /// calculate C(p)
                int neighbor0 = (int)pSrc.at<float>(i - 1, j - 1);
                int neighbor1 = (int)pSrc.at<float>(i - 1, j);
                int neighbor2 = (int)pSrc.at<float>(i - 1, j + 1);
                int neighbor3 = (int)pSrc.at<float>(i, j + 1);
                int neighbor4 = (int)pSrc.at<float>(i + 1, j + 1);
                int neighbor5 = (int)pSrc.at<float>(i + 1, j);
                int neighbor6 = (int)pSrc.at<float>(i + 1, j - 1);
                int neighbor7 = (int)pSrc.at<float>(i, j - 1);
                int C = int(~neighbor1 & (neighbor2 | neighbor3)) +
                    int(~neighbor3 & (neighbor4 | neighbor5)) +
                    int(~neighbor5 & (neighbor6 | neighbor7)) +
                    int(~neighbor7 & (neighbor0 | neighbor1));
                if (C == 1) {
                    /// calculate N
                    int N1 = int(neighbor0 | neighbor1) +
                        int(neighbor2 | neighbor3) +
                        int(neighbor4 | neighbor5) +
                        int(neighbor6 | neighbor7);
                    int N2 = int(neighbor1 | neighbor2) +
                        int(neighbor3 | neighbor4) +
                        int(neighbor5 | neighbor6) +
                        int(neighbor7 | neighbor0);
                    int N = min(N1, N2);
                    if ((N == 2) || (N == 3)) {
                        /// calculate criteria 3
                        int c3 = (neighbor1 | neighbor2 | ~neighbor4) & neighbor3;
                        if (c3 == 0) {
                            pDst.at<float>(i, j) = 0.0f;
                        }
                    }
                }
            }
        }
    }
}


void ThinSubiteration2(Mat& pSrc, Mat& pDst) {
    int rows = pSrc.rows;
    int cols = pSrc.cols;
    pSrc.copyTo(pDst);
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (pSrc.at<float>(i, j) == 1.0f) {
                /// get 8 neighbors
                /// calculate C(p)
                int neighbor0 = (int)pSrc.at<float>(i - 1, j - 1);
                int neighbor1 = (int)pSrc.at<float>(i - 1, j);
                int neighbor2 = (int)pSrc.at<float>(i - 1, j + 1);
                int neighbor3 = (int)pSrc.at<float>(i, j + 1);
                int neighbor4 = (int)pSrc.at<float>(i + 1, j + 1);
                int neighbor5 = (int)pSrc.at<float>(i + 1, j);
                int neighbor6 = (int)pSrc.at<float>(i + 1, j - 1);
                int neighbor7 = (int)pSrc.at<float>(i, j - 1);
                int C = int(~neighbor1 & (neighbor2 | neighbor3)) +
                    int(~neighbor3 & (neighbor4 | neighbor5)) +
                    int(~neighbor5 & (neighbor6 | neighbor7)) +
                    int(~neighbor7 & (neighbor0 | neighbor1));
                if (C == 1) {
                    /// calculate N
                    int N1 = int(neighbor0 | neighbor1) +
                        int(neighbor2 | neighbor3) +
                        int(neighbor4 | neighbor5) +
                        int(neighbor6 | neighbor7);
                    int N2 = int(neighbor1 | neighbor2) +
                        int(neighbor3 | neighbor4) +
                        int(neighbor5 | neighbor6) +
                        int(neighbor7 | neighbor0);
                    int N = min(N1, N2);
                    if ((N == 2) || (N == 3)) {
                        int E = (neighbor5 | neighbor6 | ~neighbor0) & neighbor7;
                        if (E == 0) {
                            pDst.at<float>(i, j) = 0.0f;
                        }
                    }
                }
            }
        }
    }
}


void normalizeLetter(Mat& inputarray, Mat& outputarray) {
    bool bDone = false;
    int rows = inputarray.rows;
    int cols = inputarray.cols;

    inputarray.convertTo(inputarray, CV_32FC1);

    inputarray.copyTo(outputarray);

    outputarray.convertTo(outputarray, CV_32FC1);

    /// pad source
    Mat p_enlarged_src = Mat(rows + 2, cols + 2, CV_32FC1);
    for (int i = 0; i < (rows + 2); i++) {
        p_enlarged_src.at<float>(i, 0) = 0.0f;
        p_enlarged_src.at<float>(i, cols + 1) = 0.0f;
    }
    for (int j = 0; j < (cols + 2); j++) {
        p_enlarged_src.at<float>(0, j) = 0.0f;
        p_enlarged_src.at<float>(rows + 1, j) = 0.0f;
    }
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (inputarray.at<float>(i, j) >= threshold_value) {			//调参
                p_enlarged_src.at<float>(i + 1, j + 1) = 1.0f;
            }
            else
                p_enlarged_src.at<float>(i + 1, j + 1) = 0.0f;
        }
    }

    /// start to thin
    Mat p_thinMat1 = Mat::zeros(rows + 2, cols + 2, CV_32FC1);
    Mat p_thinMat2 = Mat::zeros(rows + 2, cols + 2, CV_32FC1);
    Mat p_cmp = Mat::zeros(rows + 2, cols + 2, CV_8UC1);

    while (bDone != true) {
        /// sub-iteration 1
        ThinSubiteration1(p_enlarged_src, p_thinMat1);
        /// sub-iteration 2
        ThinSubiteration2(p_thinMat1, p_thinMat2);
        /// compare
        compare(p_enlarged_src, p_thinMat2, p_cmp, CMP_EQ);	//比较输入的src1和src2中的元素,真为255,否则为0
        /// check
        int num_non_zero = countNonZero(p_cmp);					//返回灰度值不为0的像素数
        if (num_non_zero == (rows + 2) * (cols + 2)) {
            bDone = true;
        }
        /// copy
        p_thinMat2.copyTo(p_enlarged_src);
    }
    // copy result
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            outputarray.at<float>(i, j) = p_enlarged_src.at<float>(i + 1, j + 1);
        }
    }
}

void Line_reflect(Mat& inputarray, Mat& outputarray)
{
    int rows = inputarray.rows;
    int cols = inputarray.cols;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (inputarray.at<float>(i, j) == 1.0f) {
                outputarray.at<float>(i, j) = 0.0f;
            }
        }
    }
}

// 提取连通区域,并剔除小面积联通区域
void Delete_smallregions(Mat& pSrc, Mat& pDst)
{
    vector<vector<Point>> contours;           //二值图像轮廓的容器
    vector<Vec4i> hierarchy;                  //4个int向量,分别表示后、前、父、子的索引编号
    findContours(pSrc, contours, hierarchy, RETR_LIST, CHAIN_APPROX_NONE);             //检测所有轮廓

    vector<vector<Point>>::iterator k;                    //迭代器,访问容器数据

    for (k = contours.begin(); k != contours.end();)      //遍历容器,设置面积因子
    {
        if (contourArea(*k, false) < 400)
        {//删除指定元素,返回指向删除元素下一个元素位置的迭代器
            k = contours.erase(k);
        }
        else
            ++k;
    }

    //contours[i]代表第i个轮廓,contours[i].size()代表第i个轮廓上所有的像素点
    for (int i = 0; i < contours.size(); i++)
    {
        for (int j = 0; j < contours[i].size(); j++)
        {
            //获取轮廓上点的坐标
            Point P = Point(contours[i][j].x, contours[i][j].y);
        }
        drawContours(pDst, contours, i, Scalar(255), -1, 8);
    }
}

贴结果

在这里插入图片描述

在这里插入图片描述
我建立了一个激光条纹提取的群,欢迎小伙伴加入,学习交流:点击链接加入群聊【激光条纹中心提取】 先申明本人是学生,不涉及营销、宣传、推广、盈利,单纯为了学习交流,所以心怀不轨的请绕道!!!
在这里插入图片描述

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值