《OpenCV3编程入门》学习笔记六:图像处理

中间隔了好长时间没写啊,这段也没怎么用。

一:内容介绍

本节主要介绍OpenCV的imgproc模块的图像处理部分:
1. 线性滤波:均值滤波与高斯滤波
2. 非线性滤波:中值滤波、双边滤波
3. 图像形态学:腐蚀与膨胀,开运算、闭运算,形态学梯度,顶帽、黑帽
4. 漫水填充
5. 图像金字塔及图片尺寸缩放
6. 阈值化

二:学习笔记

  1. 方框滤波(box filter)是不一定归一化的,而这里说的几种线性和非线性滤波方法都是必须归一化的。
  2. 这里需要说一下卷积和滤波还是有点区别的,不过当核对称时它们俩就一样了。
    2维滤波公式: h[m,n]=k,lg[k,l]f[m+k,n+l]
    2维卷积公式: h[m,n]=k,lg[k,l]f[mk,nl]
  3. 高斯分布一定要牢记,随手写出。
    期望为0的一维高斯分布: G(x)=12πσex22σ2
    二维: G(x)=12πσ2ex2+y22σ2
  4. 这里说一下双边滤波,是结合图像的空间邻近度和像素值相似度的一种折中处理,既能平滑过渡区域、又可以保持边缘等细节信息,达到保边去噪的目的。但是其速度很慢。
  5. 图像形态学,是基于形状的一系列图像处理操作,最基本的就是膨胀(dilate)和腐蚀(erode)。腐蚀膨胀操作也可以理解为 max, min操作。如图:
    这里写图片描述
    话说感觉好多形态学并不是太常用啊。
  6. 图像金字塔是一种以多分辨率来解释图像的有效但概念简单的结构,主要用到了卷积。
  7. 关于阈值化,现在有很多自动阈值化的方法。如 大津算法(OTSU)
  8. 本节函数清单:
    这里写图片描述

三:相关源码及解析

本章示例较多,示例列表:
1.图像滤波
2.腐蚀与膨胀
3.漫水填充
4.图像金字塔
5.基本阈值操作

1. 图像滤波

源码:

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

Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4, g_dstImage5;
int g_nBoxFilterValue = 6; //方框滤波内核值
int g_nMeanBlurValue = 10; //均值滤波内核值
int g_nGaussianBlurValue = 6; //高斯滤波内核值
int g_nMedianBlurValue = 10; //中值滤波内核值
int g_nBilateralFilterValue = 10; //双边滤波内核值
static void on_BoxFilter(int, void *);  //方框滤波
static void on_MeanBlur(int, void *);  //均值滤波
static void on_GaussianBlur(int, void *); //高斯滤波
static void on_MedianBlur(int, void *);  //中值滤波
static void on_BilateralFilter(int, void *);  //双边滤波

int main()
{
    g_srcImage = imread("poster_girl_2.jpg");
    g_dstImage1 = g_srcImage.clone();
    g_dstImage2 = g_srcImage.clone();
    g_dstImage3 = g_srcImage.clone();
    g_dstImage4 = g_srcImage.clone();
    g_dstImage5 = g_srcImage.clone();

    namedWindow("【原图窗口】");
    imshow("【原图窗口】", g_srcImage);
    //方框滤波
    namedWindow("【方框滤波】");
    createTrackbar("内核值:", "【方框滤波】", &g_nBoxFilterValue, 50, on_BoxFilter);
    on_BoxFilter(g_nBoxFilterValue, 0);
    //均值滤波
    namedWindow("【均值滤波】");
    createTrackbar("内核值:", "【均值滤波】", &g_nMeanBlurValue, 50, on_MeanBlur);
    on_MeanBlur(g_nMeanBlurValue, 0);
    //高斯滤波
    namedWindow("【高斯滤波】");
    createTrackbar("内核值:", "【高斯滤波】", &g_nGaussianBlurValue, 50, on_GaussianBlur);
    on_GaussianBlur(g_nGaussianBlurValue, 0);
    //中值滤波
    namedWindow("【中值滤波】");
    createTrackbar("内核值:", "【中值滤波】", &g_nMedianBlurValue, 50, on_MedianBlur);
    on_MedianBlur(g_nMedianBlurValue, 0);
    //双边滤波
    namedWindow("【双边滤波】");
    createTrackbar("内核值:", "【双边滤波】", &g_nBilateralFilterValue, 50, on_BilateralFilter);
    on_BilateralFilter(g_nBilateralFilterValue, 0);

    cout << "运行成功,请调整滚动条观察图像效果" << endl << "按下'q'键时,程序退出";
    while (char(waitKey(1)) != 'q');

    return 0;
}

static void on_BoxFilter(int, void *)
{
    boxFilter(g_srcImage, g_dstImage1, -1, Size(g_nBoxFilterValue+1, g_nBoxFilterValue+1));
    imshow("【方框滤波】", g_dstImage1);
}

static void on_MeanBlur(int, void *)
{
    blur(g_srcImage, g_dstImage2, Size(g_nMeanBlurValue + 1, g_nMeanBlurValue + 1));
    imshow("【均值滤波】", g_dstImage2);
}

static void on_GaussianBlur(int, void *)
{
    GaussianBlur(g_srcImage, g_dstImage3,  Size(g_nGaussianBlurValue*2 + 1, g_nGaussianBlurValue*2 + 1), 0);
    imshow("【高斯滤波】", g_dstImage3);
}

static void on_MedianBlur(int, void *)
{
    medianBlur(g_srcImage, g_dstImage4, g_nMedianBlurValue*2 + 1);
    imshow("【中值滤波】", g_dstImage4);
}

static void on_BilateralFilter(int, void *)
{
    bilateralFilter(g_srcImage, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue*2, g_nBilateralFilterValue / 2);
    imshow("【双边滤波】", g_dstImage5);
}

素材:
无
效果图:
无
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
提示:

2. 腐蚀与膨胀

源码:

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

Mat g_srcImage, g_dstImage;
int g_erodeOrDilate = 0; //0表示腐蚀erode,1表示膨胀dilate
int g_nStructElementSize = 3;

void Process();  //腐蚀和膨胀的处理函数
void on_TrackbarNumChange(int, void*); 
void on_ElementSizeChange(int, void*);


int main()
{
    g_srcImage = imread("poster_cat_1.jpg");
    namedWindow("【原始图】");
    imshow("【原始图】", g_srcImage);

    namedWindow("【效果图】");
    //获取自定义核
    Mat element = getStructuringElement(MORPH_RECT, Size(2*g_nStructElementSize+1, 2 * g_nStructElementSize + 1));
    erode(g_srcImage, g_dstImage, element);
    imshow("【效果图】", g_dstImage);
    //创建轨迹条
    createTrackbar("腐蚀/膨胀", "【效果图】", &g_erodeOrDilate, 1, on_TrackbarNumChange);
    createTrackbar("内核尺寸", "【效果图】", &g_nStructElementSize, 21, on_ElementSizeChange);

    cout << "运行成功,请调整滚动条观察图像效果" << endl << "按下'q'键时,程序退出";
    while (char(waitKey(1)) != 'q');
    return 0;
}

//进行自定义的腐蚀和膨胀操作
void Process()
{
    Mat element = getStructuringElement(MORPH_RECT, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1));
    if (g_erodeOrDilate==0) {
        erode(g_srcImage, g_dstImage, element);
    }
    else {
        dilate(g_srcImage, g_dstImage, element);
    }
    imshow("【效果图】", g_dstImage);
}

void on_TrackbarNumChange(int, void *)
{
    Process();
}

void on_ElementSizeChange(int, void *)
{
    Process();
}

素材:
这里写图片描述
效果图:
这里写图片描述
提示:

3 . 漫水填充

源码:

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

Mat g_srcImage, g_dstImage, g_grayImage, g_maskImage; //定义原始图,目标图,灰度图,掩码图
int g_nFillMode = 1; //漫水填充的模式
int g_nLowDifference = 20, g_nUpDifference = 20;  //负差最大值、正差最大值
int g_nConnectivity = 4; //表示floodFill函数标识符低八位的连通值
int g_bIsColor = true; //是否为彩色图
bool g_bUseMask = false; //是否显示掩模窗口
int g_nNewMaskVal = 255; //重新绘制的像素值

static void onMouse(int event, int x, int y, int, void*);

int main()
{
    g_srcImage = imread("poster_home.jpg");
    g_dstImage=g_srcImage.clone();
    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY); 
    g_maskImage.create(g_srcImage.rows+2, g_srcImage.cols+2, CV_8UC1);
    namedWindow("【效果图】");
    createTrackbar("负差最大值", "【效果图】", &g_nLowDifference, 255, 0); //Trackbar
    createTrackbar("正差最大值", "【效果图】", &g_nUpDifference, 255, 0);
    setMouseCallback("【效果图】", onMouse, 0); //鼠标回调函数
    while (1) //循环轮询按键
    {
        imshow("【效果图】", g_bIsColor ? g_dstImage:g_grayImage);
        int c = waitKey(0); //获取键盘按键
        if ((char)c == 27)
        {
            cout << "程序退出........." << endl;
            break;
        }
        switch ((char)c)
        {
        case'1':
            if (g_bIsColor) {
                cout << "键盘'1'被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】" << endl;
                cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
                g_maskImage = Scalar::all(0);
                g_bIsColor = false;
            }
            else {
                cout << "键盘'1'被按下,切换彩色/灰度模式,当前操作为将【灰度模式】切换为【彩色模式】" << endl;
                g_srcImage.copyTo(g_dstImage);
                g_maskImage = Scalar::all(0);
                g_bIsColor = true;
            }
            break;
        case'2':
            if (g_bUseMask) {
                destroyWindow("【mask】");
                g_bUseMask = false;
            }
            else {
                namedWindow("【mask】", WINDOW_NORMAL);
                g_maskImage = Scalar::all(0);
                imshow("【mask】", g_maskImage);
                g_bUseMask = true;
            }
            break;
        case'3':
            cout << "按键'3'被按下,恢复原始图像" << endl;
            g_srcImage.copyTo(g_dstImage);
            cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
            g_maskImage = Scalar::all(0);
            break;
        case'4':  //此处空范围即上下像素差均为0
            cout << "按键'4'被按下,使用空范围的漫水填充" << endl;
            g_nFillMode = 0;
            break;
        case'5':
            cout << "按键'5'被按下,使用渐变、固定范围的漫水填充" << endl;
            g_nFillMode = 1;
            break;
        case'6':
            cout << "按键'6'被按下,使用渐变、浮动范围的漫水填充" << endl;
            g_nFillMode = 2;
            break;
        case'7':
            cout << "按键'7'被按下,操作标志符的低八位使用4位的连接模式" << endl;
            g_nConnectivity = 4;
            break;
        case'8':
            cout << "按键'8'被按下,操作标志符的低八位使用8位的连接模式" << endl;
            g_nConnectivity = 8;
            break;
        default:
            break;
        }
    }

    return 0;
}

static void onMouse(int event, int x, int y, int, void*)
{
    if (event != EVENT_LBUTTONDOWN)
        return;
    //--------------------调用floodFill函数之前的参数准备部分--------------------
    Point seed = Point(x, y);
    int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;  //空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference
    int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;
    int flags = g_nConnectivity + (g_nNewMaskVal<<8) + (g_nFillMode==1?FLOODFILL_FIXED_RANGE:0);
    //随机生成bgr值
    int b = (unsigned)theRNG() & 255; //生成随机值
    int g = (unsigned)theRNG() & 255; //生成随机值
    int r = (unsigned)theRNG() & 255; //生成随机值
    Rect ccomp;  //定义重绘区域的最小边界矩形区域
    Scalar newVal = g_bIsColor ? Scalar(b, g, r) : Scalar(r*0.299+g*0.587+b*0.114);//在重绘区域像素的新值
    Mat dst = g_bIsColor ? g_dstImage : g_grayImage;  //目标图的赋值
    int area;

    //----------------正式调用floodFill函数--------------------------
    if (g_bUseMask) {
        threshold(g_maskImage, g_maskImage, 1, 128, THRESH_BINARY); //将之前的都变为灰色
        area = floodFill(dst, g_maskImage, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference),
            Scalar(UpDifference, UpDifference, UpDifference), flags);
        imshow("【mask】", g_maskImage);
    }
    else {
        area = floodFill(dst, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference),
            Scalar(UpDifference, UpDifference, UpDifference), flags);
    }
    imshow("【效果图】", dst);
    cout << area << " 个像素被重绘" << endl;
}

素材:
这里写图片描述
效果图:
这里写图片描述
提示:

4 . 图像金字塔

源码:

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

#define WINDOW_NAME "【程序窗口】"
Mat g_srcImage, g_dstImage, g_tmpImage;

int main()
{
    g_srcImage = imread("poster_car_1.jpg");
    namedWindow(WINDOW_NAME);
    imshow(WINDOW_NAME, g_srcImage);
    g_tmpImage = g_srcImage;
    g_dstImage = g_tmpImage;
    int key = 0;
    while (1) //轮询获取按键信息
    {
        key = waitKey(9); //读取键值到key变量中
        switch (key) {
        case 27:  //按键ESC
        case 'q':
            return 0;
            break;
        case '1':
            pyrUp(g_tmpImage, g_dstImage, Size(g_tmpImage.cols*2, g_tmpImage.rows*2));
            cout << "检测到按键【1】被按下,开始进行基于【pyrUp】函数的图片放大:图片尺寸*2" << endl;
            break;
        case '2':
            pyrDown(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
            cout << "检测到按键【2】被按下,开始进行基于【pyrDown】函数的图片缩小:图片尺寸/2" << endl;
            break;
        case '3':
            resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols * 2, g_tmpImage.rows * 2));
            cout << "检测到按键【3】被按下,开始进行基于【resize】函数的图片放大:图片尺寸*2" << endl;
            break;
        case '4':
            resize(g_tmpImage, g_dstImage, Size(g_tmpImage.cols / 2, g_tmpImage.rows / 2));
            cout << "检测到按键【4】被按下,开始进行基于【pyrUp】函数的图片缩小:图片尺寸/2" << endl;
            break;
        }
        imshow(WINDOW_NAME, g_dstImage);
        g_tmpImage = g_dstImage; //将dst赋值给tmp,方便下一次循环
    }

    return 0;
}

素材:
这里写图片描述
效果图:
这里写图片描述
提示:

5 . 基本阈值操作

源码:

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

void on_Threshold(int, void*);
#define WINDOW_NAME "【程序窗口】"
int g_nThresholdValue = 100;
int g_nThresholdType = 0;
Mat g_srcImage, g_grayImage, g_dstImage;

int main()
{
    g_srcImage = imread("poster_landscape_1.jpg");
    cvtColor(g_srcImage, g_grayImage, COLOR_RGB2GRAY);
    namedWindow(WINDOW_NAME);
    createTrackbar("模式", WINDOW_NAME, &g_nThresholdType, 4, on_Threshold);
    createTrackbar("参数值", WINDOW_NAME, &g_nThresholdValue, 255, on_Threshold);
    on_Threshold(0, 0);
    while (waitKey(10) != 27);
    return 0;
}

void on_Threshold(int, void*)
{
    threshold(g_grayImage, g_dstImage, g_nThresholdValue, 255, g_nThresholdType);
    imshow(WINDOW_NAME, g_dstImage);
}

素材:
这里写图片描述
效果图:
这里写图片描述
提示:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值