图像的平滑处理

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

    // 图像平滑处理分而学之.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. 安卓弹出对话框——Alertdialog

    在Android开发当中,在界面上弹出一个Dialog对话框使我们经常需要做的,本篇随笔将详细的讲解Dialog对话框这个概念,包括定义不同样式的对话框. 一.Dialog 我们首先来看看androi ...

  2. 用Eclipse插件Bytecode Outline来查看Java字节码

    在遇到一些小问题的时候我们经常会使用Javap反编译取得字节码来分析,虽然Javap能完成这个工作,但是有两个缺点,一方面操作麻烦,需要很多步骤,一方面没有文档注释,对新手来说看起字节码来比较麻烦. ...

  3. 强大的JQuery(二)--动画效果

    上篇博客我们讲过了jquery的基础知识--强大的JQuery(一)--基础篇,作为web开发人员,网页的动画效果是不可缺少的,本篇博客重点来说说jquery的动画效果的实现. 因为动画的效果不能截图 ...

  4. mysql-完整性约束条件

    PRIMARY :   主键 AUTO_INCREMENT  : 自增长 FOREIGN KEY : 外键 NOT NULL : 非空 UNIQUE KEY : 唯一 DEFAULT :  默认值 主 ...

  5. 进程控制块的task_struct结构

    >进程控制块 在linux中进程信息存放在叫做进程控制块的数据结构中,每个进程在内核中都有⼀个进程控制块(PCB)来维护进程相关的信息,Linux内核的 进程控制块是task_struct结构体 ...

  6. Shodan!

    Shodan! 简介 首先先介绍一下Shodan CNNMoney的一篇文章写道,虽然目前人们都认为谷歌是最强劲的搜索引擎,但Shodan才是互联网上最可怕的搜索引擎. 与谷歌不同的是,Shodan不 ...

  7. 【原】 twemproxy ketama一致性hash分析

    转贴请注明原帖位置:http://www.cnblogs.com/basecn/p/4288456.html 测试Twemproxy集群,双主双活 向twemproxy集群做写操作时,发现key的分布 ...

  8. .NET DLL 保护措施详解(二)关于性能的测试

    先说结果: 加了缓存的结果与C#原生代码差异不大了 我对三种方式进行了测试: 第一种,每次调用均动态编译 第二种,缓存编译好的对象 第三种,直接调用原生C#代码 .net dll保护系列 ------ ...

  9. swift-自定义无限轮播图

    一  前言 1.之前一直在用OC编程,最近才开始接触使用swift就发现使用OC越来越不习惯,感觉已经爱上了swift. 2.这个自定义轮播图只是对之前OC版本进行了翻译,欢迎指正. 3.我决定一步步 ...

  10. C# WinForm动态调用远程Web服务

    本文转自:http://blog.csdn.net/muyangjun/article/details/7930871 1.添加服务引用 2.在弹出的添加服务引用对话框地址栏中输入WebService ...