图像的平滑处理

平滑,也称 模糊, 平滑处理时需要用到一个滤波器 。滤波器想象成一个包含加权系数的窗口,这个加权系数也叫做核或者模版。

    // 图像平滑处理分而学之.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
const int MAX_KERNEL_LENGTH = 31;
int _tmain(int argc, _TCHAR* argv[])
{
Mat img = imread("D:\\lenargb.jpg", 1);
if (img.empty())
{
cout << "无法读入图像" << endl;
return -1;
}
Mat dest; #pragma region 归一化平滑
for (int i = 1; i < MAX_KERNEL_LENGTH; i++)
{
blur(img, dest, Size(i, i), Point(-1, -1));//size(i,i)内核大小,最小为(1,1);
imshow("归一化平滑图像", dest);
waitKey(100); }
#pragma endregion
#pragma region 高斯平滑
for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
{
GaussianBlur(img, dest, Size(i, i), 0, 0);//size的两个参数必须都为正奇数,第四个参数是x方向的标准差,第五个参数是y方向的标准差,如果是0,表示从内核大小计算得到;
imshow("高斯平滑图像图像", dest);
waitKey(100);
}
#pragma endregion #pragma region 中值平滑
for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
{
medianBlur(img, dest, i);//第三个参数为核的边长,必须为奇数,一般中值平滑用的都是正方形所以只用一个参数就好;
imshow("中值平滑", dest);
waitKey(100);
}
#pragma endregion //waitKey(0);
return 0;
}

图像阈值操作

为了从一副图像中提取出我们需要的部分,应该用图像中的每一个像素点的灰度值与选取的阈值进行比较,并作出相应的判断。

一旦找到了需要分割的物体的像素点,我们可以对这些像素点设定一些特定的值来表示。(例如:可以将该物体的像素点的灰度值设定为:‘0’(黑色),其他的像素点的灰度值为:‘255’(白色);

OpenCV中提供了阈值(threshold)函数 有五种类型

1. 二进制阈值化

先要选定一个特定的阈值量,比如:120,这样,新的阈值产生规则可以解释为大于120的像素点的灰度值设定为最大值(如8位灰度值最大为255),灰度值小于120的像素点的灰度值设定为0。

2. 反二进制阈值化

该阈值化与二进制阈值化相似,先选定一个特定的灰度值作为阈值,不过最后的设定值相反。

3. 截断阈值化

同样首先需要选定一个阈值,图像中大于该阈值的像素点被设定为该阈值,小于该阈值的保持不变

4. 阈值化为0

首先需要选定一个阈值,像素点的灰度值大于该阈值的不进行任何改变;2 像素点的灰度值小于该阈值的,其灰度值全部变为0

5. 反阈值化为0

原理类似于0阈值,但是在对图像做处理的时候相反,即:像素点的灰度值小于该阈值的不进行任何改变,而大于该阈值的部分,其灰度值全部变为0。

filter2D函数能够对图像按照模版进行滤波

    // 基本阈值操作2.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv; void respond(int, void*);
const char * window_name = "图片";
Mat src_gray;
Mat dst;
int threshhold_type = 5;
const int max_type = 5;
int threshhold_value = 0;
int max_value = 255;
int _tmain(int argc, _TCHAR* argv[])
{
namedWindow(window_name, WINDOW_AUTOSIZE);
Mat src = imread("E:\\code\\test\\image\\tiantan.png", 1);
if (src.empty())
{
cout << "无法正常载入图片" << endl; return -1;
}
//转换为灰度图;
cvtColor(src, src_gray, CV_RGB2GRAY);
imshow(window_name, src_gray);
createTrackbar("阈值类型", window_name, &threshhold_type, max_type, respond);
createTrackbar("阈值大小", window_name, &threshhold_value, max_value, respond);
waitKey(0);
return 0;
}
void respond(int, void*)
{
/* 0:二进制阈值
1: 反二进制阈值
2: 截断阈值
3: 0阈值
4: 反0阈值
5:原灰度图;
*/
if (threshhold_type==5)
{
imshow(window_name, src_gray);
}
else
{
threshold(src_gray, dst, threshhold_value, max_value, threshhold_type);
imshow(window_name, dst);
}
}

实现自己的线性滤波器

OpenCV为我们提供了函数 filter2D ,来实现核卷积;

    // 实现自己的滤波器2.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
void respond(int, void*);
const char * window_name = "实现自己的滤波器";
int value = 0;
Mat src,dst,kernel;
const int max_value = 100; int _tmain(int argc, _TCHAR* argv[])
{
src = imread("E:\\code\\test\\image\\lena.png", 1);
if (src.data==NULL)
{
cout << "无法加载图片" << endl;
return -1;
}
namedWindow(window_name, WINDOW_AUTOSIZE);
imshow(window_name, src);
createTrackbar("核的大小", window_name, &value, max_value, respond);
while (true)
{ char c = waitKey(0);
if (c==27)
{
return 0;
}
} return 0;
} void respond(int, void*)
{
int kernel_size = 1 + value * 2;
kernel = Mat::ones(kernel_size, kernel_size, CV_32F)/(float)(kernel_size*kernel_size);
filter2D(src, dst, -1, kernel, Point(-1, -1));
imshow(window_name, dst);
}

Sobel导数

Sobel 算子是一个离散微分算子 (discrete differentiation operator)。 它用来计算图像灰度函数的近似梯度。Sobel 算子结合了高斯平滑和微分求导。

    // Sobel导数.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h" #include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h> using namespace cv; /** @function main */
int main(int argc, char** argv)
{ Mat src, src_gray;
Mat grad;
char* window_name = "Sobel Demo - Simple Edge Detector";
int scale = 1;
int delta = 0;
int ddepth = CV_16S; //int c; /// 装载图像
src = imread("E://code//test//image//lena.png",1); if (!src.data)
{
return -1;
} GaussianBlur(src, src, Size(3, 3), 0, 0, BORDER_DEFAULT);
/// 创建显示窗口
namedWindow(window_name, CV_WINDOW_AUTOSIZE);
imshow(window_name, src);
waitKey(3000);
/// 转换为灰度图
cvtColor(src, src_gray, CV_RGB2GRAY); imshow(window_name, src_gray);
waitKey(3000);
/// 创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y; /// 求 X方向梯度 Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
//Sobel(src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x); /// 求Y方向梯度
Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
//Sobel(src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y); /// 合并梯度(近似) addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad); imshow(window_name, grad); waitKey(0); return 0;
}
//其实不简单也没有关系,因为只是相对大小,在图像的对比中,依然能够找到边界;

Laplace 算子

一阶导数的极值位置,二阶导数为0。所以我们也可以用这个特点来作为检测图像边缘的方法。 但是, 二阶导数的0值不仅仅出现在边缘(它们也可能出现在无意义的位置),但是我们可以过滤掉这些点。

实际上,由于 Laplacian使用了图像梯度,它内部调用了 Sobel 算子。

    // Laplace算子.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h" #include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h> using namespace cv; /** @函数 main */
int main(int argc, char** argv)
{
Mat src, src_gray, dst;
int kernel_size = 3;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
char* window_name = "Laplace Demo"; int c; /// 装载图像
src = imread("E://code//test//image//lena.png", 1); if (!src.data)
{
return -1;
} /// 使用高斯滤波消除噪声
GaussianBlur(src, src, Size(3, 3), 0, 0, BORDER_DEFAULT); /// 转换为灰度图
cvtColor(src, src_gray, CV_RGB2GRAY); /// 创建显示窗口
namedWindow(window_name, CV_WINDOW_AUTOSIZE);
imshow(window_name, src);
waitKey(2000);
imshow(window_name, src_gray);
waitKey(3000); /// 使用Laplace函数
Mat abs_dst; Laplacian(src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT);
//ddepth: 输出图像的深度。 因为输入图像的深度是 CV_8U ,这里我们必须定义 ddepth = CV_16S 以避免外溢。
//下面大概求绝对值;
convertScaleAbs(dst, abs_dst); /// 显示结果
imshow(window_name, abs_dst); waitKey(0); return 0;
}

Canny 边缘检测

步骤

1. 消除噪声。 使用高斯平滑滤波器卷积降噪。

2. 计算梯度幅值和方向。 此处,使用Sobel滤波器

3. 非极大值 抑制。 这一步排除非边缘像素, 仅仅保留了一些细线条(候选边缘)。

4. 滞后阈值: 最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值): Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。

    // Canny边缘检测.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h> using namespace cv; /// 全局变量 Mat src, src_gray;
Mat dst, detected_edges; //int edgeThresh = 1;
int lowThreshold=1;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
char* window_name = "Edge Map"; /**
* @函数 CannyThreshold
* @简介: trackbar 交互回调 - Canny阈值输入比例1:3
*/
void CannyThreshold(int, void*)
{
/// 使用 3x3内核降噪
blur(src_gray, detected_edges, Size(3, 3)); /// 运行Canny算子
Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
//第一个参数是原图像,第二个参数是输出图像,支持本地计算,第三个参数是低阈值,第四个参数是高阈值,第五个参数是内部sobel算子使用的内核大小。
/// 使用 Canny算子输出边缘作为掩码显示原图像
dst = Scalar::all(0);
//把dst填充为黑色
src.copyTo(dst, detected_edges);
//第一个参数为输出图像,第二个参数为掩码;即把第二个图像中非0的部分在src中的像素复制给dst; imshow(window_name, dst);
} /** @函数 main */
int main(int argc, char** argv)
{
/// 装载图像
src = imread("E:\\code\\test\\image\\lena.png",1); if (!src.data)
{
return -1;
} /// 创建与src同类型和大小的矩阵(dst)
dst.create(src.size(), src.type()); /// 原图像转换为灰度图像
cvtColor(src, src_gray, CV_BGR2GRAY); /// 创建显示窗口
namedWindow(window_name, CV_WINDOW_AUTOSIZE); /// 创建trackbar
createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold); /// 显示图像
CannyThreshold(0, 0); /// 等待用户反应
waitKey(0); return 0;
}

    /// 使用 3x3内核降噪
blur(src_gray, detected_edges, Size(3, 3)); /// 运行Canny算子
Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
//第一个参数是原图像,第二个参数是输出图像,支持本地计算,第三个参数是低阈值,第四个参数是高阈值,第五个参数是内部sobel算子使用的内核大小。
/// 使用 Canny算子输出边缘作为掩码显示原图像

2.opencv图像处理常用操作的更多相关文章

  1. c++ MFC图像处理CImage类常用操作代码

    原文作者:aircraft 原文地址:https://www.cnblogs.com/DOMLX/p/9598974.html MFC图像处理CImage类常用操作 CImage类头文件为#inclu ...

  2. 使用matlab进行图像处理的一些常用操作和tip

    本人还是习惯使用Python语言,有时候不得不使用matlab的时候就变得举步维艰,下面记录一下使用matlab进行图像处理的一些常用操作以及代码,方便之后查阅: 1. 图像的读取 %% 读取原图像 ...

  3. Atitit 图像处理 常用8大滤镜效果 Jhlabs 图像处理类库 java常用图像处理类库

    Atitit 图像处理 常用8大滤镜效果 Jhlabs 图像处理类库 java常用图像处理类库1.1. 5种常用的Photoshop滤镜,分别针对照片的曝光.风格色调.黑白照片处理.锐利度.降噪这五大 ...

  4. OpenCV图像处理篇之边缘检测算子

    OpenCV图像处理篇之边缘检测算子 转载: http://xiahouzuoxin.github.io/notes/ 3种边缘检测算子 一阶导数的梯度算子 高斯拉普拉斯算子 Canny算子 Open ...

  5. 1.5快速上手OpenCV图像处理

    在上一节中,已经完成了OPENCV的配置,在本节接触几个Opencv图像处理相关的程序,看看opencv用简洁的代码能够实现哪些有趣的图像效果. 1.第一个程序:图像显示 #include<op ...

  6. OpenCV图像处理以及人脸识别

    OpenCV基础 OpenCV是一个开源的计算机视觉库.提供了很多图像处理常用的工具 批注:本文所有图片数据都在我的GitHub仓库 读取图片并显示 import numpy as np import ...

  7. 《OpenCV图像处理编程实例》

    <OpenCV图像处理编程实例>例程复现 随书代码下载:http://www.broadview.com.cn/28573 总结+遇到的issue解决: 第一章 初识OpenCV 1.VS ...

  8. OpenCV图像处理学习笔记-Day1

    OpenCV图像处理学习笔记-Day1 目录 OpenCV图像处理学习笔记-Day1 第1课:图像读入.显示和保存 1. 读入图像 2. 显示图像 3. 保存图像 第2课:图像处理入门基础 1. 基本 ...

  9. 【三】用Markdown写blog的常用操作

    本系列有五篇:分别是 [一]Ubuntu14.04+Jekyll+Github Pages搭建静态博客:主要是安装方面 [二]jekyll 的使用 :主要是jekyll的配置 [三]Markdown+ ...

随机推荐

  1. 剑指Offer39 数组中寻找和为sum的两个数字

    /************************************************************************* > File Name: 39_TwoNum ...

  2. hdu 4711 动态规划

    思路:其实这题是个挺水的动态规划,一开始就能AC,可是不知道错哪了,瞎改瞎交,WA了数十次.AC之后怎么改都是AC,也不知道改了什么地方,郁闷死了~~~难道开始时的测试数据有问题??? dp[i][j ...

  3. hdu 4099 Revenge of Fibonacci 大数+压位+trie

    最近手感有点差,所以做点水题来锻炼一下信心. 下周的南京区域赛估计就是我的退役赛了,bless all. Revenge of Fibonacci Time Limit: 10000/5000 MS ...

  4. 大部分人努力程度之低,根本轮不到拼天赋 [转自w3cschool]

    2014-05-31 w3cschool 在过去的三个多月里,每周六一天的心理咨询师的培训课成了我一周中最重要最开心的事情之一.因为国庆节的缘故,从9月中旬到10月中旬培训中心都没有安排课程,因此习惯 ...

  5. IDG合伙人李丰:O2O中的C2C蕴藏巨大商机

    [ 亿欧导读 ] IDG合伙人李丰表示,每个新趋势出现,都是在解决上一轮行业革新时没有解决好的市场需求.而O2O中的C2C将会出现巨大商机的原因在于移动互联网的出现创造了新的交互方式,可以更快速的匹配 ...

  6. backbone.Router History源码笔记

    Backbone.History和Backbone.Router history和router都是控制路由的,做一个单页应用,要控制前进后退,就可以用到他们了. History类用于监听URL的变化, ...

  7. .NET程序编译和运行

    一次面试的时候遇到的一道题目,简要说明.NET的编译过程,在网上看了很多资料,简单总结如下: 1.一般的编译过程 通常高级语言的程序编译过程是:首先写好的程序是源代码,然后编译器编译为本地机器语言,最 ...

  8. C#的Process类的一些用法

    c#之process类相关整理 一.根据进程名获取进程的用户名? 需要添加对 System.Management.dll 的引用 using System.Diagnostics; using Sys ...

  9. win8升级win10后的windows.old怎么删除

    现在win10只是出了预览版本,还没有出正式版,但是相信一部分朋友都与小D一样,喜欢尝鲜,已上用上了win10了. 有些人是通过win8或是8.1直接安装升级上去的,这样操作是安装方便,但是系统会为了 ...

  10. SliverLight(how to show data point on the column series)

    You should know that Silverlight comes with win form drawing software is different, it has no the la ...