zhang快速并行细化算法

相关介绍  http://www.doc88.com/p-7724593025971.html

比较好的介绍  https://www.cnblogs.com/xianglan/archive/2011/01/01/1923779.html

https://www.cnblogs.com/Summerio/p/8284602.html

https://blog.csdn.net/weixinhum/article/details/80261296

//#include "stdafx.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <time.h>
#include <iostream>

using namespace cv;
using namespace std;

/*
* @brief 对输入图像进行细化,骨骼化
* @param src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
* @param dst为对src细化后的输出图像,格式与src格式相同,元素中只有0与1,1代表有元素,0代表为空白
*/
void thinImage(Mat & src, Mat & dst)
{
    int width = src.cols;
    int height = src.rows;
    src.copyTo(dst);
    vector<uchar *> mFlag; //用于标记需要删除的点    
    while (true)
    {
        //步骤一   
        for (int i = 0; i < height; ++i)
        {
            uchar * p = dst.ptr<uchar>(i);
            for (int j = 0; j < width; ++j)
            {
                //获得九个点对象,注意边界问题
                uchar p1 = p[j];
                if (p1 != 1) continue;
                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);
                uchar p8 = (j == 0) ? 0 : *(p + j - 1);
                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);
                if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)//条件1判断
                {
                    //条件2计算
                    int ap = 0;
                    if (p2 == 0 && p3 == 1) ++ap;
                    if (p3 == 0 && p4 == 1) ++ap;
                    if (p4 == 0 && p5 == 1) ++ap;
                    if (p5 == 0 && p6 == 1) ++ap;
                    if (p6 == 0 && p7 == 1) ++ap;
                    if (p7 == 0 && p8 == 1) ++ap;
                    if (p8 == 0 && p9 == 1) ++ap;
                    if (p9 == 0 && p2 == 1) ++ap;
                    //条件2、3、4判断
                    if (ap == 1 && p2 * p4 * p6 == 0 && p4 * p6 * p8 == 0)
                    {
                        //标记    
                        mFlag.push_back(p + j);
                    }
                }
            }
        }
        //将标记的点删除    
        for (vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
        {
            **i = 0;
        }
        //直到没有点满足,算法结束    
        if (mFlag.empty())
        {
            break;
        }
        else
        {
            mFlag.clear();//将mFlag清空    
        }

        //步骤二,根据情况该步骤可以和步骤一封装在一起成为一个函数
        for (int i = 0; i < height; ++i)
        {
            uchar * p = dst.ptr<uchar>(i);
            for (int j = 0; j < width; ++j)
            {
                //如果满足四个条件,进行标记    
                //  p9 p2 p3    
                //  p8 p1 p4    
                //  p7 p6 p5    
                uchar p1 = p[j];
                if (p1 != 1) continue;
                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);
                uchar p8 = (j == 0) ? 0 : *(p + j - 1);
                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);
                if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
                {
                    int ap = 0;
                    if (p2 == 0 && p3 == 1) ++ap;
                    if (p3 == 0 && p4 == 1) ++ap;
                    if (p4 == 0 && p5 == 1) ++ap;
                    if (p5 == 0 && p6 == 1) ++ap;
                    if (p6 == 0 && p7 == 1) ++ap;
                    if (p7 == 0 && p8 == 1) ++ap;
                    if (p8 == 0 && p9 == 1) ++ap;
                    if (p9 == 0 && p2 == 1) ++ap;
                    if (ap == 1 && p2 * p4 * p8 == 0 && p2 * p6 * p8 == 0)
                    {
                        //标记    
                        mFlag.push_back(p + j);
                    }
                }
            }
        }
        //将标记的点删除    
        for (vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
        {
            **i = 0;
        }
        //直到没有点满足,算法结束    
        if (mFlag.empty())
        {
            break;
        }
        else
        {
            mFlag.clear();//将mFlag清空    
        }
    }
}

/*
* @brief 对输入图像进行端点和交叉点检测,前提是输入图像已经经过骨骼化提取
*/
void endPointAndintersectionPointDetection(Mat & src)
{
    int width = src.cols;
    int height = src.rows;
    vector<CvPoint> endpoint;
    vector<CvPoint> intersectionPoint;
    //遍历骨骼化后的图像,找到端点和交叉点,分别放入容器中
    for (int i = 0; i < height; ++i)
    {
        uchar * p = src.ptr<uchar>(i);
        for (int j = 0; j < width; ++j)
        {
            //获得九个点对象,注意边界问题
            uchar p1 = p[j];
            if (p1 != 1) continue;
            uchar p2 = (i == 0) ? 0 : *(p - src.step + j);
            uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - src.step + j + 1);
            uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
            uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + src.step + j + 1);
            uchar p6 = (i == height - 1) ? 0 : *(p + src.step + j);
            uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + src.step + j - 1);
            uchar p8 = (j == 0) ? 0 : *(p + j - 1);
            uchar p9 = (i == 0 || j == 0) ? 0 : *(p - src.step + j - 1);

            if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) == 1)//端点判断
            {
                printf("端点:%d %d\n", i, j);
                endpoint.push_back(cvPoint(j, i));
            }
            else //交叉点判断
            {
                int ap = 0;
                if (p2 == 0 && p3 == 1) ++ap;
                if (p3 == 0 && p4 == 1) ++ap;
                if (p4 == 0 && p5 == 1) ++ap;
                if (p5 == 0 && p6 == 1) ++ap;
                if (p6 == 0 && p7 == 1) ++ap;
                if (p7 == 0 && p8 == 1) ++ap;
                if (p8 == 0 && p9 == 1) ++ap;
                if (p9 == 0 && p2 == 1) ++ap;
                if (ap >= 3)
                {
                    printf("交叉点:%d %d\n", i, j);
                    intersectionPoint.push_back(cvPoint(j, i));
                }
            }
        }
    }
    //画出端点
    for (vector<CvPoint>::iterator i = endpoint.begin(); i != endpoint.end(); ++i)
    {
        circle(src, cvPoint(i->x, i->y), 5, Scalar(255), -1);
    }
    //画出交叉点
    for (vector<CvPoint>::iterator i = intersectionPoint.begin(); i != intersectionPoint.end(); ++i)
    {
        circle(src, cvPoint(i->x, i->y), 5, Scalar(255));
    }
    endpoint.clear();//数据回收 
    intersectionPoint.clear();   
}

int main()
{
    Mat src = imread("5.png", IMREAD_GRAYSCALE);
    GaussianBlur(src, src, Size(7, 7), 0, 0);//高斯滤波
    threshold(src, src, 140, 1, cv::THRESH_BINARY_INV);//二值化,前景为1,背景为0
    Mat dst;
    thinImage(src, dst);//图像细化(骨骼化)
    endPointAndintersectionPointDetection(dst);
    src = src * 255;
    imshow("原始图像", src);
    dst = dst * 255;
    imshow("细化图像", dst);
    waitKey(0);
}

在源文件的同级目录下放入要编辑的图片(png格式),命名为5.png

编译(需安装opencv):

g++ gujiatiqu.cpp -o gujia `pkg-config opencv –cflags –libs`

上面的引号是键盘左上角的`,就是数字键1左边的那个键,谨记!

 

 

点赞