一、先介绍几个API
(1)opencv中除了提供绘制各种图形的函数外,还提供了一个特殊的绘制函数——在图像上绘制文字。这个函数即是cv::putText()。
void cv::putText(
cv::Mat& img, // 待绘制的图像
const string& text, // 待绘制的文字
cv::Point origin, // 文本框的左下角
int fontFace, // 字体 (如cv::FONT_HERSHEY_PLAIN)
double fontScale, // 尺寸因子,值越大文字越大
cv::Scalar color, // 线条的颜色(RGB)
int thickness = 1, // 线条宽度
int lineType = 8, // 线型(4邻域或8邻域,默认8邻域)
bool bottomLeftOrigin = false // true='origin at lower left'
);
opencv支持的文字字体有以下几种:
另外,我们在实际绘制文字之前,还可以使用cv::getTextSize()接口先获取待绘制文本框的大小,以方便放置文本框。具体调用形式如下:
cv::Size cv::getTextSize(
const string& text,
cv::Point origin,
int fontFace,
double fontScale,
int thickness,
int* baseLine
);
下面就通过一个示例,来看看cv::getTextSize()与cv::putText()相结合的妙用:
//创建空白图用于绘制文字
cv::Mat image = cv::Mat::zeros(cv::Size(640, 480), CV_8UC3);
//设置蓝色背景
image.setTo(cv::Scalar(100, 0, 0));
//设置绘制文本的相关参数
std::string text = "Hello World!";
int font_face = cv::FONT_HERSHEY_COMPLEX;
double font_scale = 2;
int thickness = 2;
int baseline;
//获取文本框的长宽
cv::Size text_size = cv::getTextSize(text, font_face, font_scale, thickness, &baseline);
//将文本框居中绘制
cv::Point origin;
origin.x = image.cols / 2 - text_size.width / 2;
origin.y = image.rows / 2 + text_size.height / 2;
cv::putText(image, text, origin, font_face, font_scale, cv::Scalar(0, 255, 255), thickness, 8, 0);
//显示绘制解果
cv::imshow("image", image);
cv::waitKey(0);
return 0;
绘制文字如下:
上方的介绍转载于 https://blog.csdn.net/guduruyu/article/details/68491211
(2)再介绍一个API createTrackbar
创建一个跟踪条(轨迹条),并将跟踪条附到制定的窗口上。
C++:
int createTrackbar(const string& trackbarname, const string& winname, int* value, int count, TrackbarCallback onChange=0, void* userdata=0)
参数:
trackbarname=》所创建的跟踪条的名字
winname=》跟踪条所依附的窗口的名字
value=》可选的指向整型变量的指针,整型变量的值对应于滑动条的位置。初始创建时,滑动条的值就是这个整型变量的值。
count=》滑动条最大的值。最小值总是为0。
onChange=》指向回调函数的指针,每次滚动条改变位置时,这个函数就会被调用。这个函数的原型应该为:void Foo(int, void*);其中第一个参数是跟踪条的位置,第二个参数是用户数据(见下一个参数)。如果回调为空,表示没有回调函数被调用,仅仅value会有变化。
userdata=》通过回调函数传递的用户数据(传递参数)。它可以控制跟踪条事件而不需要使用全局变量。
这个createTrackbar函数创建一个具有特定名称和范围的轨迹条(滚动条,或者说是滑块范围控制工具),指定一个和轨迹条位置同步的变量。而且要指定回调函数,在轨迹条位置改变的时候来调用这个回调函数。创建的轨迹条显示在指定的winname所代表的窗口上。
二、腐蚀与膨胀
我们在《初始滤波之均值滤波》中有聊过滤波的本质,以及介绍了其中一种线性滤波(均值滤波)。对于常见的非线性滤波“中值滤波”也在《视频的读取和处理》中有介绍。今天我们来介绍另外两种滤波方式:
- 最大值滤波
- 最小值滤波
再了解这两种滤波之前,我们先来看一些概念。
啥是形态学?
简单来讲,形态学操作就是基于形状的一系列图像处理操作。通过将结构元素作用于输入图像来产生输出图像。
有啥作用?
常用的形态学处理方法包括:腐蚀、膨胀、开运算、闭运算、顶帽运算、底帽运算,其中膨胀与腐蚀是图像处理中最常用的形态学操作手段,其他方法是两者相互组合而产生的。
膨胀和腐蚀的运用广泛:
- 消除噪声
- 分割(isolate)独立的图像元素,以及连接(join)相邻的元素。
- 寻找图像中的明显的极大值区域或极小值区域。
如何实现?
我们可以想象跟卷积类似,首先选取一个结构元素(我们以3*3的矩形核为例),并且定义其锚定点(以重心为例),结构元素覆盖的部分。
以结构元素的最小值填充锚定点的做法为腐蚀
以结构元素的最大值填充锚定点的做法为膨胀
是不是变相的滤波?
下面介绍 膨胀和腐蚀 中所用到的API
首先获取结构元素(获取滤波核):
CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
- shape:表示内核的形状,有三种形状可以选择。
- 矩形:MORPH_RECT;
- 交叉形:MORPH_CROSS;
- 椭圆形:MORPH_ELLIPSE;
- ksize 是指结构元素大小
- anchor 中心锚点的位置
腐蚀:
-
CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue() );
- src 输入图像,任意通道的
- dst 输出图像,类型与通道数目必须跟输入保持一致
- kernel 结构元素
- anchor 中心位置锚定
- iterations 循环次数
- borderType 边缘填充类型(不用关心,使用默认值)
膨胀:
CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
- src 输入图像,任意通道的。深度可为CV_8U、CV_16U、CV_16S、CV_32F或CV_64F。
- dst 输出图像,类型与通道数目必须跟输入保持一致
- kernel 结构元素, 如果kernel=Mat()则为设定的3×3矩形。
- anchor 锚定位置,默认中心位置。
- iterations 循环次数
- borderType 边缘填充类型(不用关心,使用默认值)
介绍转载于:https://zhuanlan.zhihu.com/p/83078037
代码:
// ExpansionCorrosion.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
Mat src,erosion_dst,dilation_dst;
int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;
char text[] = "Element: 0:Rect 1:Coss 2:Ellipse";
void Erosion(int ,void*)
{
int erosion_type = 0;
if (erosion_elem == 0) {
erosion_type = MORPH_RECT; // 创建的核形状,矩形
}
if (erosion_elem == 1) {
erosion_type = MORPH_CROSS; // 十字形
}
if (erosion_elem == 2) {
erosion_type = MORPH_ELLIPSE; // 椭圆
}
// 返回构造性元素。
Mat element = getStructuringElement(erosion_type, Size(2 * erosion_size + 1, 2 * erosion_size + 1), Point(erosion_size, erosion_size));
// 腐蚀
erode(src, erosion_dst, element);
Point textOrg(100,250);
// 在图像上绘制文字,erosion_dst是待绘制的图像,text 绘制文本,textOrg 绘制文本框的左下角位置
//FONT_HERSHEY_COMPLEX 字体,1 为字体大小,Scalar(255,0,255) 为颜色,2 为字体粗细,4: 线型(4邻域或8邻域,默认8邻域)
putText(erosion_dst, text, textOrg, FONT_HERSHEY_COMPLEX, 1, Scalar(255,0,255), 1);
imshow("腐蚀", erosion_dst);
}
void Dilate(int, void*)
{
int dilation_type = 0;
if (dilation_elem == 0){
dilation_type = MORPH_RECT;
}
else if (dilation_elem == 1){
dilation_type = MORPH_CROSS;
}
else if (dilation_elem == 2) {
dilation_type = MORPH_ELLIPSE;
}
Mat element = getStructuringElement(dilation_type, Size(2 * dilation_size + 1, 2 * dilation_size + 1), Point(dilation_size, dilation_size));
//膨胀
dilate(src, dilation_dst, element);
Point textOrg(100, 250);
putText(dilation_dst, text, textOrg, FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 255), 2, 4);
imshow("膨胀", dilation_dst);
}
int main()
{
src = imread("C:\\Users\\Geek\\Desktop\\1281425_2019-07-18_10_0\\6232.bmp");
if (src.empty())
return 0;
namedWindow("腐蚀", WINDOW_AUTOSIZE);
createTrackbar("Element:", "腐蚀", &erosion_elem, max_elem, Erosion);
createTrackbar("kernel:","腐蚀", &erosion_size, max_kernel_size, Erosion);
// 一定要注意参数
namedWindow("膨胀", WINDOW_AUTOSIZE);
createTrackbar("Element:", "膨胀", &dilation_elem, max_elem, Dilate);
createTrackbar("kernel:", "膨胀", &dilation_size, max_kernel_size, Dilate);
Erosion(0,0);
Dilate(0, 0);
waitKey(0);
return 0;
}
不知道为什么滑动条kernel 只能4个单位4个单位的滑,谁能解答一下???