数字形态学(OpenCV)

形态学 

 形态学(morphology)是生物学的一个分支,其关注的是动植物的形态和结构等。这里用同一词语表示图像处理的数学形态学的内容。数学形态学的语言是集合论,其中的集合表示图像的对象,如:二值化图像中,所有白色像素的集合是该图像的一个完整形态学描述。集合中每两个分量提供一个像素的坐标,第三个分量则对应于其离散灰度值。更高维度空间的集合可以包含其他的图像属性,譬如颜色和随时间变化的分量。除了基本的集合定义之外,集合的反射和平移的概念在形态学中用得也很广泛。一个集合B的反射表示B',定义如下:B'={w|w=-b,b∈B}。如果B是描述图像中物体的像素的集合(二维点),则B'是B中(x,y)坐标被(-x,-y)替代的点的集合。集合B按照点z=(z1,z2)表示(B)z的平移定义:(B)z={c|c=b+z,b∈B}。图像表示如下:

  集合反射操作类似于空间卷积中执行的翻转(旋转)操作。在形态学中集合的反射和平移用来表达基于结构元(SE)的操作:研究一幅图像中感兴趣特性所用的小集合或子图像。对于结构元的操作可以使用下图做一些简单的介绍:

  当我们使用“结构元包含在几何中”这样的术语时,我们明确地指出A和B的元素完全重叠。

 

 

 

 

形态学梯度运算:

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

int main()
{ 
    Mat image = imread("1.jpg");    
    imshow("【原始图】形态学梯度", image);

    Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
    morphologyEx(image, image, MORPH_GRADIENT, element);
    imshow("【效果图】形态学梯度", image);

    waitKey(0);
    return 0;
}

形态学综合示例:

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

Mat g_srcImage, g_dstImage;//原始图和效果图
int g_nElementShape = MORPH_RECT;//元素结构的形状

//变量接收的TrackBar位置参数
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;
int g_nErodeDilateNum = 0;
int g_nTopBlackHatNum = 0;

static void on_OpenClose(int, void*);//回调函数
static void on_ErodeDilate(int, void*);//回调函数
static void on_TopBlackHat(int, void*);//回调函数

int main()
{
    g_srcImage = imread("1.jpg");
    if (!g_srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; }
    imshow("【原始图】", g_srcImage);

    namedWindow("【开运算/闭运算】", 1);
    namedWindow("【腐蚀/膨胀】", 1);
    namedWindow("【顶帽/黑帽】", 1);

    //参数赋值
    g_nOpenCloseNum = 9;
    g_nErodeDilateNum = 9;
    g_nTopBlackHatNum = 2;

    //分别为三个窗口创建滚动条
    createTrackbar("迭代值", "【开运算/闭运算】", &g_nOpenCloseNum, g_nMaxIterationNum * 2 + 1, on_OpenClose);
    createTrackbar("迭代值", "【腐蚀/膨胀】", &g_nErodeDilateNum, g_nMaxIterationNum * 2 + 1, on_ErodeDilate);
    createTrackbar("迭代值", "【顶帽/黑帽】", &g_nTopBlackHatNum, g_nMaxIterationNum * 2 + 1, on_TopBlackHat);

    //轮询获取按键信息
    while (1)
    {
        int c;

        //执行回调函数
        on_OpenClose(g_nOpenCloseNum, 0);
        on_ErodeDilate(g_nErodeDilateNum, 0);
        on_TopBlackHat(g_nTopBlackHatNum, 0);

        //获取按键
        c = waitKey(0);

        //按下键盘按键Q或者ESC,程序退出
        if ((char)c == 'q' || (char)c == 27)
            break;
        //按下键盘按键1,使用椭圆(Elliptic)结构元素结构元素MORPH_ELLIPSE
        if ((char)c == 49)//键盘按键1的ASII码为49
            g_nElementShape = MORPH_ELLIPSE;
        //按下键盘按键2,使用矩形(Rectangle)结构元素MORPH_RECT
        else if ((char)c == 50)//键盘按键2的ASII码为50
            g_nElementShape = MORPH_RECT;
        //按下键盘按键3,使用十字形(Cross-shaped)结构元素MORPH_CROSS
        else if ((char)c == 51)//键盘按键3的ASII码为51
            g_nElementShape = MORPH_CROSS;
        //按下键盘按键space,在矩形、椭圆、十字形结构元素中循环
        else if ((char)c == ' ')
            g_nElementShape = (g_nElementShape + 1) % 3;
    }

    return 0;
}

static void on_OpenClose(int, void*)
{
    //偏移量的定义
    int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
    int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
    //自定义核
    Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset));
    //进行操作
    if (offset < 0)
        //此句代码的OpenCV2版为:
        //morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element);
        //此句代码的OpenCV3版为:
        morphologyEx(g_srcImage, g_dstImage, MORPH_OPEN, element);
    else
        //此句代码的OpenCV2版为:
        //morphologyEx(g_srcImage, g_dstImage, CV_MOP_CLOSE, element);
        //此句代码的OpenCV3版为:
        morphologyEx(g_srcImage, g_dstImage, MORPH_CLOSE, element);



    //显示图像
    imshow("【开运算/闭运算】", g_dstImage);
}

static void on_ErodeDilate(int, void*)
{
    //偏移量的定义
    int offset = g_nErodeDilateNum - g_nMaxIterationNum;    //偏移量
    int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
    //自定义核
    Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset));
    //进行操作
    if (offset < 0)
        erode(g_srcImage, g_dstImage, element);
    else
        dilate(g_srcImage, g_dstImage, element);
    //显示图像
    imshow("【腐蚀/膨胀】", g_dstImage);
}

static void on_TopBlackHat(int, void*)
{
    //偏移量的定义
    int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
    int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
    //自定义核
    Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset));
    //进行操作
    if (offset < 0)
        morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT, element);
    else
        morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
    //显示图像
    imshow("【顶帽/黑帽】", g_dstImage);
}
morphologyEx

 填充:

#include <opencv2/opencv.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
using namespace cv;

int main()
{
    Mat src = imread("1.jpg");
    imshow("【原始图】", src);
    Rect ccomp;
    floodFill(src, Point(50, 300), Scalar(155, 255, 55), &ccomp, Scalar(20, 20, 20), Scalar(20, 20, 20));
    imshow("【效果图】", src);
    waitKey(0);
    return 0;
}
floodfill

图像金字塔

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

#define WINDOW_NAME "【程序窗口】"        //为窗口标题定义的宏

static void ShowHelpText();
Mat g_srcImage, g_dstImage, g_tmpImage;

int main()
{
    g_srcImage = imread("1.jpg");//工程目录下需要有一张名为1.jpg的测试图像,且其尺寸需被2的N次方整除,N为可以缩放的次数
    if (!g_srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; }

    namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
    imshow(WINDOW_NAME, g_srcImage);

    g_tmpImage = g_srcImage;
    g_dstImage = g_tmpImage;

    int key = 0;

    ShowHelpText();

    while (1)
    {
        key = waitKey();//读取键值到key变量中

        //根据key变量的值,进行不同的操作
        switch (key)
        {
            //======================【程序退出相关键值处理】=======================  
        case 27://按键ESC
            return 0;
            break;

        case 'q'://按键Q
            return 0;
            break;

            //======================【图片放大相关键值处理】=======================  
        case 'a'://按键A按下,调用pyrUp函数
            pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
            printf(">检测到按键【A】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n");
            break;

        case 'w'://按键W按下,调用resize函数
            resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
            printf(">检测到按键【W】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n");
            break;

        case '1'://按键1按下,调用resize函数
            resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
            printf(">检测到按键【1】被按下,开始进行基于【resize】函数的图片放大:图片尺寸×2 \n");
            break;

        case '3': //按键3按下,调用pyrUp函数
            pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
            printf(">检测到按键【3】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸×2 \n");
            break;
            //======================【图片缩小相关键值处理】=======================  
        case 'd': //按键D按下,调用pyrDown函数
            pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
            printf(">检测到按键【D】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n");
            break;

        case  's': //按键S按下,调用resize函数
            resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
            printf(">检测到按键【S】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n");
            break;

        case '2'://按键2按下,调用resize函数
            resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
            printf(">检测到按键【2】被按下,开始进行基于【resize】函数的图片缩小:图片尺寸/2\n");
            break;

        case '4': //按键4按下,调用pyrDown函数
            pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
            printf(">检测到按键【4】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2\n");
            break;
        }

        imshow(WINDOW_NAME, g_dstImage);
        g_tmpImage = g_dstImage;
    }

    return 0;
}

static void ShowHelpText()
{
    //输出一些帮助信息
    printf("\n\n\n\t欢迎来到OpenCV图像金字塔和resize示例程序~\n\n");
    printf("\n\n\t按键操作说明: \n\n"
        "\t\t键盘按键【ESC】或者【Q】- 退出程序\n"
        "\t\t键盘按键【1】或者【W】- 进行基于【resize】函数的图片放大\n"
        "\t\t键盘按键【2】或者【S】- 进行基于【resize】函数的图片缩小\n"
        "\t\t键盘按键【3】或者【A】- 进行基于【pyrUp】函数的图片放大\n"
        "\t\t键盘按键【4】或者【D】- 进行基于【pyrDown】函数的图片缩小\n"
    );
}
resize

 基本阈值操作

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;

#define WINDOW_NAME "【程序窗口】"        //为窗口标题定义的宏 

int g_nThresholdValue = 100;
int g_nThresholdType = 3;
Mat g_srcImage, g_grayImage, g_dstImage;

static void ShowHelpText();//输出帮助文字
void on_Threshold(int, void*);//回调函数

int main()
{
    ShowHelpText();

    g_srcImage = imread("1.jpg");
    if (!g_srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }
    imshow("原始图", g_srcImage);

    cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);

    namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);

    createTrackbar("模式",
        WINDOW_NAME, &g_nThresholdType,
        4, on_Threshold);

    createTrackbar("参数值",
        WINDOW_NAME, &g_nThresholdValue,
        255, on_Threshold);

    on_Threshold(0, 0);

    while (1)
    {
        int key;
        key = waitKey(20);
        if ((char)key == 27) { break; }
    }

}

void on_Threshold(int, void*)
{
    //调用阈值函数
    threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, g_nThresholdType);

    //更新效果图
    imshow(WINDOW_NAME, g_dstImage);
}

static void ShowHelpText()
{
    //输出欢迎信息和OpenCV版本
    printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION);
    printf("\n\n  ----------------------------------------------------------------------------\n");

    //输出一些帮助信息  
    printf("\n\t欢迎来到【基本阈值操作】示例程序~\n\n");
    printf("\n\t按键操作说明: \n\n"
        "\t\t键盘按键【ESC】- 退出程序\n"
        "\t\t滚动条模式0- 二进制阈值\n"
        "\t\t滚动条模式1- 反二进制阈值\n"
        "\t\t滚动条模式2- 截断阈值\n"
        "\t\t滚动条模式3- 反阈值化为0\n"
        "\t\t滚动条模式4- 阈值化为0\n");
}
threshold

 轮廓

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;

#define WINDOW_NAME1 "【原始图窗口】"                    //为窗口标题定义的宏 
#define WINDOW_NAME2 "【效果图窗口】"                    //为窗口标题定义的宏 

Mat g_srcImage; Mat g_grayImage;
int g_nThresh = 50;
int g_maxThresh = 255;
RNG g_rng(12345);
Mat srcImage_copy = g_srcImage.clone();
Mat g_thresholdImage_output;
vector<vector<Point> > g_vContours;
vector<Vec4i> g_vHierarchy;

static void ShowHelpText();
void on_ThreshChange(int, void*);
void ShowHelpText();

int main()
{
    ShowHelpText();

    g_srcImage = imread("1.jpg", 1);

    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
    blur(g_grayImage, g_grayImage, Size(3, 3));

    namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
    imshow(WINDOW_NAME1, g_srcImage);

    createTrackbar(" 阈值:", WINDOW_NAME1, &g_nThresh, g_maxThresh, on_ThreshChange);
    on_ThreshChange(0, 0);//调用一次进行初始化

    waitKey(0);
    return(0);
}

void on_ThreshChange(int, void*)
{
    // 对图像进行二值化,控制阈值
    threshold(g_grayImage, g_thresholdImage_output, g_nThresh, 255, THRESH_BINARY);

    // 寻找轮廓
    findContours(g_thresholdImage_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

    // 遍历每个轮廓,寻找其凸包
    vector<vector<Point> >hull(g_vContours.size());
    for (unsigned int i = 0; i < g_vContours.size(); i++)
    {
        convexHull(Mat(g_vContours[i]), hull[i], false);
    }

    // 绘出轮廓及其凸包
    Mat drawing = Mat::zeros(g_thresholdImage_output.size(), CV_8UC3);
    for (unsigned int i = 0; i < g_vContours.size(); i++)
    {
        Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));
        drawContours(drawing, g_vContours, i, color, 1, 8, vector<Vec4i>(), 0, Point());
        drawContours(drawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point());
    }

    imshow(WINDOW_NAME2, drawing);
}

void ShowHelpText()
{
    printf("当前使用的OpenCV版本为:" CV_VERSION);
}
convexhull

 绘制包围轮廓的矩形和圆

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;

#define WINDOW_NAME1 "【原始图窗口】"        //为窗口标题定义的宏 
#define WINDOW_NAME2 "【效果图窗口】"        //为窗口标题定义的宏 

Mat g_srcImage;
Mat g_grayImage;
int g_nThresh = 50;//阈值
int g_nMaxThresh = 255;//阈值最大值
RNG g_rng(12345);//随机数生成器

void on_ContoursChange(int, void*);
static void ShowHelpText();

int main()
{
    ShowHelpText();

    //【1】载入3通道的原图像
    g_srcImage = imread("1.jpg", 1);
    if (!g_srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; }

    //【2】得到原图的灰度图像并进行平滑
    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
    blur(g_grayImage, g_grayImage, Size(3, 3));

    //【3】创建原始图窗口并显示
    namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
    imshow(WINDOW_NAME1, g_srcImage);

    //【4】设置滚动条并调用一次回调函数
    createTrackbar(" 阈值:", WINDOW_NAME1, &g_nThresh, g_nMaxThresh, on_ContoursChange);
    on_ContoursChange(0, 0);

    waitKey(0);

    return(0);
}

void on_ContoursChange(int, void*)
{
    //定义一些参数
    Mat threshold_output;
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    // 使用Threshold检测边缘
    threshold(g_grayImage, threshold_output, g_nThresh, 255, THRESH_BINARY);

    // 找出轮廓
    findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

    // 多边形逼近轮廓 + 获取矩形和圆形边界框
    vector<vector<Point> > contours_poly(contours.size());
    vector<Rect> boundRect(contours.size());
    vector<Point2f>center(contours.size());
    vector<float>radius(contours.size());

    //一个循环,遍历所有部分,进行本程序最核心的操作
    for (unsigned int i = 0; i < contours.size(); i++)
    {
        approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);//用指定精度逼近多边形曲线 
        boundRect[i] = boundingRect(Mat(contours_poly[i]));//计算点集的最外面(up-right)矩形边界
        minEnclosingCircle(contours_poly[i], center[i], radius[i]);//对给定的 2D点集,寻找最小面积的包围圆形 
    }

    // 绘制多边形轮廓 + 包围的矩形框 + 圆形框
    Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);
    for (int unsigned i = 0; i < contours.size(); i++)
    {
        Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));//随机设置颜色
        drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point());//绘制轮廓
        rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);//绘制矩形
        circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);//绘制圆
    }

    // 显示效果图窗口
    namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);
    imshow(WINDOW_NAME2, drawing);
}

static void ShowHelpText()
{
    printf("当前使用的OpenCV版本为:" CV_VERSION);
    printf("欢迎来到【创建包围轮廓的矩形和圆形边界框】示例程序\n");
    printf("按键操作说明: \n"
        "键盘按键【ESC】- 退出程序\n"
        "滑动滚动条 - 改变阈值\n");
}

 轮廓面积与中心距

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;

#define WINDOW_NAME1 "【原始图】"                    //为窗口标题定义的宏 
#define WINDOW_NAME2 "【图像轮廓】"        //为窗口标题定义的宏 

Mat g_srcImage; Mat g_grayImage;
int g_nThresh = 100;
int g_nMaxThresh = 255;
RNG g_rng(12345);
Mat g_cannyMat_output;
vector<vector<Point> > g_vContours;
vector<Vec4i> g_vHierarchy;

void on_ThreshChange(int, void*);
static void ShowHelpText();

int main(int argc, char** argv)
{
    
    ShowHelpText();
    // 读入原图像, 返回3通道图像数据
    g_srcImage = imread("1.jpg", 1);

    // 把原图像转化成灰度图像并进行平滑
    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
    blur(g_grayImage, g_grayImage, Size(3, 3));

    // 创建新窗口
    namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
    imshow(WINDOW_NAME1, g_srcImage);

    //创建滚动条并进行初始化
    createTrackbar(" 阈值", WINDOW_NAME1, &g_nThresh, g_nMaxThresh, on_ThreshChange);
    on_ThreshChange(0, 0);

    waitKey(0);
    return(0);
}

void on_ThreshChange(int, void*)
{
    // 使用Canndy检测边缘
    Canny(g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh * 2, 3);

    // 找到轮廓
    findContours(g_cannyMat_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

    // 计算矩
    vector<Moments> mu(g_vContours.size());
    for (unsigned int i = 0; i < g_vContours.size(); i++)
    {
        mu[i] = moments(g_vContours[i], false);
    }

    //  计算中心矩
    vector<Point2f> mc(g_vContours.size());
    for (unsigned int i = 0; i < g_vContours.size(); i++)
    {
        mc[i] = Point2f(static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00));
    }

    // 绘制轮廓
    Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);
    for (unsigned int i = 0; i < g_vContours.size(); i++)
    {
        Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));//随机生成颜色值
        drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point());//绘制外层和内层轮廓
        circle(drawing, mc[i], 4, color, -1, 8, 0);;//绘制圆
    }

    // 显示到窗口中
    namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);
    imshow(WINDOW_NAME2, drawing);

    // 通过m00计算轮廓面积并且和OpenCV函数比较
    printf("\t 输出内容: 面积和轮廓长度\n");
    for (unsigned int i = 0; i < g_vContours.size(); i++)
    {
        printf(" >通过m00计算出轮廓[%d]的面积: (M_00) = %.2f \n OpenCV函数计算出的面积=%.2f , 长度: %.2f \n\n", i, mu[i].m00, contourArea(g_vContours[i]), arcLength(g_vContours[i], true));
        Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));
        drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point());
        circle(drawing, mc[i], 4, color, -1, 8, 0);
    }
}

void ShowHelpText()
{
    printf("当前使用的OpenCV版本为:" CV_VERSION);
}
contourmoment

 分水岭算法

#include <opencv2/core/utility.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <cstdio>
#include <iostream>
using namespace cv;
using namespace std;
static void help(char** argv)
{
    cout << "\nThis program demonstrates the famous watershed segmentation algorithm in OpenCV: watershed()\n"
        "Usage:\n" << argv[0] << " [image_name -- default is 1.jpg]\n" << endl;
    cout << "Hot keys: \n"
        "\tESC - quit the program\n"
        "\tr - restore the original image\n"
        "\tw or SPACE - run watershed segmentation algorithm\n"
        "\t\t(before running it, *roughly* mark the areas to segment on the image)\n"
        "\t  (before that, roughly outline several markers on the image)\n";
}
Mat markerMask, img;
Point prevPt(-1, -1);
static void onMouse(int event, int x, int y, int flags, void*)
{
    if (x < 0 || x >= img.cols || y < 0 || y >= img.rows)
        return;
    if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
        prevPt = Point(-1, -1);
    else if (event == EVENT_LBUTTONDOWN)
        prevPt = Point(x, y);
    else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
    {
        Point pt(x, y);
        if (prevPt.x < 0)
            prevPt = pt;
        line(markerMask, prevPt, pt, Scalar::all(255), 5, 8, 0);
        line(img, prevPt, pt, Scalar::all(255), 5, 8, 0);
        prevPt = pt;
        imshow("image", img);
    }
}
int main(int argc, char** argv)
{
    cv::CommandLineParser parser(argc, argv, "{help h | | }{ @input | 1.jpg | }");
    if (parser.has("help"))
    {
        help(argv);
        return 0;
    }
    string filename = samples::findFile(parser.get<string>("@input"));
    Mat img0 = imread(filename, 1), imgGray;
    if (img0.empty())
    {
        cout << "Couldn't open image ";
        help(argv);
        return 0;
    }
    help(argv);
    namedWindow("image", 1);
    img0.copyTo(img);
    cvtColor(img, markerMask, COLOR_BGR2GRAY);
    cvtColor(markerMask, imgGray, COLOR_GRAY2BGR);
    markerMask = Scalar::all(0);
    imshow("image", img);
    setMouseCallback("image", onMouse, 0);
    for (;;)
    {
        char c = 'w';
        c = (char)waitKey(0);
        if (c == 27)
            break;
        if (c == 'r')
        {
            markerMask = Scalar::all(0);
            img0.copyTo(img);
            imshow("image", img);
        }
        if (c == 'w' || c == ' ')
        {
            int i, j, compCount = 0;
            vector<vector<Point> > contours;
            vector<Vec4i> hierarchy;
            findContours(markerMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
            if (contours.empty())
                continue;
            Mat markers(markerMask.size(), CV_32S);
            markers = Scalar::all(0);
            int idx = 0;
            for (; idx >= 0; idx = hierarchy[idx][0], compCount++)
                drawContours(markers, contours, idx, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_MAX);
            if (compCount == 0)
                continue;
            vector<Vec3b> colorTab;
            for (i = 0; i < compCount; i++)
            {
                int b = theRNG().uniform(0, 255);
                int g = theRNG().uniform(0, 255);
                int r = theRNG().uniform(0, 255);
                colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
            }
            double t = (double)getTickCount();
            watershed(img0, markers);
            t = (double)getTickCount() - t;
            printf("execution time = %gms\n", t * 1000. / getTickFrequency());
            Mat wshed(markers.size(), CV_8UC3);
            // paint the watershed image
            for (i = 0; i < markers.rows; i++)
                for (j = 0; j < markers.cols; j++)
                {
                    int index = markers.at<int>(i, j);
                    if (index == -1)
                        wshed.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
                    else if (index <= 0 || index > compCount)
                        wshed.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
                    else
                        wshed.at<Vec3b>(i, j) = colorTab[index - 1];
                }
            wshed = wshed * 0.5 + imgGray * 0.5;
            imshow("watershed transform", wshed);
        }
    }
    return 0;
}
watershed

 修补图像

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/photo/photo.hpp"
#include <iostream>
using namespace cv;
using namespace std;

#define WINDOW_NAME0 "【原始图参考】"        //为窗口标题定义的宏 
#define WINDOW_NAME1 "【原始图】"        //为窗口标题定义的宏 
#define WINDOW_NAME2 "【修补后的效果图】"        //为窗口标题定义的宏 

Mat srcImage0, srcImage1, inpaintMask;
Point previousPoint(-1, -1);//原来的点坐标

static void ShowHelpText()
{

    printf("当前使用的OpenCV版本为:" CV_VERSION);
    printf("\n欢迎来到【图像修复】示例程序\n");
    printf("请在进行图像修复操作之前,在【原始图】窗口中进行适量的绘制"
        "\n按键操作说明: \n"
        "【鼠标左键】-在图像上绘制白色线条\n"
        "键盘按键【ESC】- 退出程序\n"
        "键盘按键【1】或【SPACE】-进行图像修复操作 \n");
}

static void On_Mouse(int event, int x, int y, int flags, void*)
{
    //鼠标左键弹起消息
    if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
        previousPoint = Point(-1, -1);
    //鼠标左键按下消息
    else if (event == EVENT_LBUTTONDOWN)
        previousPoint = Point(x, y);
    //鼠标按下并移动,进行绘制
    else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
    {
        Point pt(x, y);
        if (previousPoint.x < 0)
            previousPoint = pt;
        //绘制白色线条
        line(inpaintMask, previousPoint, pt, Scalar::all(255), 5, 8, 0);
        line(srcImage1, previousPoint, pt, Scalar::all(255), 5, 8, 0);
        previousPoint = pt;
        imshow(WINDOW_NAME1, srcImage1);
    }
}

int main(int argc, char** argv)
{
    //显示帮助文字
    ShowHelpText();

    //载入原始图并进行掩膜的初始化
    Mat srcImage = imread("1.jpg", -1);
    if (!srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }
    srcImage0 = srcImage.clone();
    srcImage1 = srcImage.clone();
    inpaintMask = Mat::zeros(srcImage1.size(), CV_8U);

    //显示原始图参考
    imshow(WINDOW_NAME0, srcImage0);
    //显示原始图
    imshow(WINDOW_NAME1, srcImage1);
    //设置鼠标回调消息
    setMouseCallback(WINDOW_NAME1, On_Mouse, 0);

    //轮询按键,根据不同的按键进行处理
    while (1)
    {
        //获取按键键值
        char c = (char)waitKey();

        //键值为ESC,程序退出
        if (c == 27)
            break;

        //键值为2,恢复成原始图像
        if (c == '2')
        {
            inpaintMask = Scalar::all(0);
            srcImage.copyTo(srcImage1);
            imshow(WINDOW_NAME1, srcImage1);
        }

        //键值为1或者空格,进行图像修补操作
        if (c == '1' || c == ' ')
        {
            Mat inpaintedImage;
            inpaint(srcImage1, inpaintMask, inpaintedImage, 3, INPAINT_TELEA);
            imshow(WINDOW_NAME2, inpaintedImage);
        }
    }

    return 0;
}
inpant

 

 

--------------------------continue----------------------------------------

posted @ 2020-10-28 21:00  望星草  阅读(570)  评论(0编辑  收藏  举报