《OpenCV3编程入门》学习笔记七:图像变换

一:内容介绍

本节主要介绍OpenCV的imgproc模块的图像变换部分:
1. 边缘检测:canny算子,sobel算子,高斯拉普拉斯算子(LOG)
2. 霍夫变换
3. 重映射
4. 仿射变换
5. 直方图均衡化

二:学习笔记

1.造成边缘的原因

景深不连续,反射率不连续(如表面材质的改变),光照不连续(阴影等),如下图
这里写图片描述

2 . 边缘检测的一般步骤:

滤波(因为导数对噪声比较敏感,一般使用高斯滤波),增强边缘(即将图像像素点邻域强度值有显著变化的点凸显出来,这里可以通过计算梯度幅值来确定),检测(常通过阈值化方法来对这些点进行取舍,这里要用到非极大值抑制啦)。

3. 图像梯度

在计算机视觉中我们常听到图像梯度的概念,这里来介绍一下:
这里写图片描述

4.拉普拉斯滤波

我们知道高斯平滑是一个低通滤波器,而拉普拉斯则相当于一个高通滤波器,推导如下:
这里写图片描述

5.非极大值抑制NMS

这个概念应用很广泛,在canny中也有应用,canny算法很经典,务必要搞清楚。
这里给一页slide说明为什么要使用它
这里写图片描述

6 . 霍夫变换

原理最好自己推一遍,这里需要说明一下hough变换可用于检测任何能写出解析式的简单形状。这里说一下HoughLines()函数,它的输出(rho, theta),rho表示距离原点(图像左上角)的距离,theta表示以y轴反方向为参考的旋转角度。自己写了个demo尝试了一下,结果如下(几条线的交点坐标为(250, 250)):
这里写图片描述

7 . 关于仿射变换

即y=kx+b。摄像机从不同角度拍摄同一物体即是仿射变换,经过仿射变换原来在同一直线上的点仍然在同一直线上。

8 . 关于直方图均衡化

其中方法CLAHE可参见http://blog.csdn.net/nnnnnnnnnnnny/article/details/52681591

9 . 本节函数清单:

这里写图片描述

三:相关源码及解析

本章示例较多,示例列表:
1.边缘检测
2.霍夫变换
3.重映射
4.仿射变换
5.直方图均衡化

1. 边缘检测

源码:

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

//原图,原图的灰度图,目标图
Mat g_srcImage, g_srcGrayImage, g_dstImage;
//Canny边缘检测相关变量
Mat g_cannyDetectedEdges;
int g_cannyLowThreshold = 1; //TrackBar位置参数
//Sobel边缘检测相关变量
Mat g_sobelGradient_X, g_sobelGradient_Y;
Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;
int g_sobelKernelSize = 1; //TrackBar位置参数
//全局函数声明
static void on_Canny(int, void*);
static void on_Sobel(int, void*);
void Scharr();

int main()
{
    g_srcImage = imread("poster_girl_3.jpg");
    namedWindow("【原始图】");
    imshow("【原始图】", g_srcImage);
    cvtColor(g_srcImage, g_srcGrayImage, COLOR_BGR2GRAY);
    g_dstImage.create(g_srcImage.size(), g_srcImage.type());
    namedWindow("【效果图】Canny边缘检测");
    namedWindow("【效果图】Sobel边缘检测");
    createTrackbar("参数值:", "【效果图】Canny边缘检测", &g_cannyLowThreshold, 120, on_Canny);
    createTrackbar("参数值:", "【效果图】Sobel边缘检测", &g_sobelKernelSize, 3, on_Sobel);
    on_Canny(0, 0);
    on_Sobel(0, 0);
    while (waitKey(8) != 27);

    return 0;
}

void on_Canny(int, void*)
{
    blur(g_srcGrayImage, g_cannyDetectedEdges, Size(3, 3)); //先使用3*3内核来降噪
    Canny(g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold*3, 3); 
    g_dstImage = Scalar::all(0); //将g_dstImage内的所有元素设置为0
    //使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷到目标图g_dstImage中
    g_srcImage.copyTo(g_dstImage, g_cannyDetectedEdges);
    imshow("【效果图】Canny边缘检测", g_dstImage);
}

void on_Sobel(int, void*)
{
    Sobel(g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, (2*g_sobelKernelSize+1)); //求X方向梯度
    convertScaleAbs(g_sobelGradient_X, g_sobelAbsGradient_X); //计算绝对值,并将结果转换成8位
    Sobel(g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, (2 * g_sobelKernelSize + 1)); //求Y方向梯度
    convertScaleAbs(g_sobelGradient_Y, g_sobelAbsGradient_Y); //计算绝对值,并将结果转换成8位
    addWeighted(g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage);
    imshow("【效果图】Sobel边缘检测", g_dstImage);
}

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

2. 霍夫变换

源码:

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

Mat g_srcImage, g_dstImage, g_midImage;  //原始图、中间图和效果图
vector<Vec4i> g_lines; //定义一个矢量结构g_lines用于存放得到的线段矢量几何
int g_nthreshold = 100;//变量接受的TrackBar位置参数

static void on_HoughLines(int, void*);

int main()
{
    g_srcImage = imread("poster_building.jpg");
    imshow("【原始图】", g_srcImage);
    namedWindow("【效果图】");
    createTrackbar("值", "【效果图】", &g_nthreshold, 200, on_HoughLines);
    Canny(g_srcImage, g_midImage, 50, 200, 3); //进行一次canny边缘检测
    cvtColor(g_midImage, g_dstImage, COLOR_GRAY2BGR);  //转化边缘检测后的灰度图转化为三通道图
    on_HoughLines(g_nthreshold, 0);
    while (waitKey(1) != 27);
    return 0;
}

static void on_HoughLines(int, void*)
{
    Mat dstImage = g_dstImage.clone();
    Mat midImage = g_midImage.clone();
    vector<Vec4i> mylines;
    HoughLinesP(midImage, mylines, 1, CV_PI / 180, g_nthreshold+1, 50, 10);
    //绘制每一条线段
    for (size_t i = 0; i < mylines.size(); i++)
    {
        Vec4i l = mylines[i];
        line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(23, 180, 55), 1, LINE_AA);
    }
    imshow("【效果图】", dstImage);
}

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

3. 重映射

源码:

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

#define WINDOW_NAME "【程序窗口】"
Mat g_srcImage, g_dstImage;
Mat g_map_x, g_map_y;
int update_map(int key);

int main(int argc, char** argv)
{
    g_srcImage = imread("poster_IronMan.jpg");
    imshow("【原始图】", g_srcImage);
    g_dstImage.create(g_srcImage.size(), g_srcImage.type());
    g_map_x.create(g_srcImage.size(), CV_32FC1);
    g_map_y.create(g_srcImage.size(), CV_32FC1);

    namedWindow(WINDOW_NAME);
    //轮询按键,进行重映射操作并显示效果图
    while (1)
    {
        int key = waitKey(10);
        if (key == 27) break;
        update_map(key);
        remap(g_srcImage, g_dstImage, g_map_x, g_map_y, INTER_LINEAR);
        imshow(WINDOW_NAME, g_dstImage);
    }
    return 0;
}

int update_map(int key)
{
    for (int j = 0; j < g_srcImage.rows; j++)
    {
        for (int i = 0; i < g_srcImage.cols; i++)
        {
            switch (key)
            {
            case'1':
                if (i > g_srcImage.cols*0.25&&i<g_srcImage.cols*0.75 &&
                    j>g_srcImage.rows*0.25 && j < g_srcImage.rows*0.75) {
                    g_map_x.at<float>(j, i) = static_cast<float>(2*(i-g_srcImage.cols*0.25) + 0.5);
                    g_map_y.at<float>(j, i) = static_cast<float>(2 * (j - g_srcImage.rows*0.25) + 0.5);
                }
                else {
                    g_map_x.at<float>(j, i) = 0;
                    g_map_y.at<float>(j, i) = 0;
                }
                break;
            case'2':
                g_map_x.at<float>(j, i) = static_cast<float>(i);
                g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows-j);
                break;
            case'3':
                g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols-i);
                g_map_y.at<float>(j, i) = static_cast<float>(j);
                break;
            case'4':
                g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols - i);
                g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows - j);
                break;
            }
        }
    }
    return 1;
}

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

4. 仿射变换

源码:

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

#define WINDOW_NAME1 "【原始图窗口】"
#define WINDOW_NAME2 "【经过Warp后的图像】"
#define WINDOW_NAME3 "【经过Warp和Rotate后的图像】"

int main()
{
    Point2f srcTriangle[3];
    Point2f dstTriangle[3];
    Mat rotMat(2, 3, CV_32FC1);
    Mat warpMat(2, 3, CV_32FC1);
    Mat srcImage, dstImage_warp, dstImage_warp_rotate;

    srcImage = imread("poster_landscape_2.jpg");
    dstImage_warp = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
    //设置源图像和目标图像上的三组点以计算仿射变换
    srcTriangle[0] = Point2f(0, 0);
    srcTriangle[1] = Point2f(static_cast<float>(srcImage.cols-1), 0);
    srcTriangle[2] = Point2f(0, static_cast<float>(srcImage.rows - 1));
    dstTriangle[0] = Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.33));
    dstTriangle[1] = Point2f(static_cast<float>(srcImage.cols*0.65), static_cast<float>(srcImage.rows*0.35));
    dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols*0.15), static_cast<float>(srcImage.rows*0.6));
    warpMat = getAffineTransform(srcTriangle, dstTriangle);
    warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());
    //指定缩放旋转来实现仿射变换
    Point center = Point(dstImage_warp.cols/2, dstImage_warp.rows/2);
    double angle = -30.0;
    double scale = 0.8;
    rotMat = getRotationMatrix2D(center, angle, scale);
    warpAffine(dstImage_warp, dstImage_warp_rotate, rotMat, dstImage_warp.size());
    //显示结果
    imshow(WINDOW_NAME1, srcImage);
    imshow(WINDOW_NAME2, dstImage_warp);
    imshow(WINDOW_NAME3, dstImage_warp_rotate);
    while (waitKey(3) != 27);
    return 0;
}

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

5. 直方图均衡化

源码:

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

int main()
{
    Mat srcImage, dstImage;
    srcImage = imread("poster_landscape_3.jpg");
    cvtColor(srcImage, srcImage, COLOR_BGR2GRAY);
    imshow("【原始图】", srcImage);
    equalizeHist(srcImage, dstImage);
    imshow("【效果图】", dstImage);
    while (waitKey(5) != 27);
    return 0;
}

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值