继续OpenCv的图像处理
对于上一节的inRange得到两幅图像等情况,可以使用addWeighted处理。
(1).然后讲形态学滤波
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace cv;
int main()
{
VideoCapture capture(0); // 从摄像头读入视频
while (1)
{
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
capture >> frame; //读取当前帧
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //自定义卷积核
morphologyEx(frame, frame, MORPH_OPEN, element);
imshow("A",frame);
waitKey(30); //延时30ms
}
return 0;
}
getStructuringElement函数会返回指定形状和尺寸的结构元素。
这个函数的第一个参数表示内核的形状,有三种形状可以选择。
矩形:MORPH_RECT;
交叉形:MORPH_CORSS;
椭圆形:MORPH_ELLIPSE;
第二和第三个参数分别是内核的尺寸以及锚点的位置。
然后就可以使用形态学滤波(morphologyEx)
表示形态学运算的类型:
MORPH_OPEN – 开运算(Opening operation)
MORPH_CLOSE – 闭运算(Closing operation)
MORPH_GRADIENT - 形态学梯度(Morphological gradient)
MORPH_TOPHAT - 顶帽(Top hat)
MORPH_BLACKHAT - 黑帽(Black hat)
(2).然后再说说轮廓识别
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace cv;
int alpha_slider1, alpha_slider2;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[总]");
createTrackbar("Cmin", "[总]", &alpha_slider1, 255, on_trackbar);
createTrackbar("Cmax", "[总]", &alpha_slider2, 255, on_trackbar);
VideoCapture capture(0); // 从摄像头读入视频
while (1)
{
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
capture >> frame; //读取当前帧
imshow("原图", frame);
cvtColor(frame, frame, COLOR_BGR2GRAY); //转化为灰度图
Canny(frame, frame, alpha_slider1, alpha_slider2);
imshow("[总]", frame);
waitKey(30); //延时30ms
}
return 0;
}
图像的边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘。
函数原型:
void cvCanny(
const CvArr * image,// 第一个参数表示输入图像,必须为单通道灰度图
CvArr * edges,// 第二个参数表示输出的边缘图像,为单通道黑白图
double threshold1,
double threshold2,// 第三个参数和第四个参数表示阈值,这二个阈值中当中的小阈值用来控制边缘连接,
// 大的阈值用来控制强边缘的初始分割即如果一个像素的梯度大与上限值,则被认为 是边缘像素,如果小于下限阈值,则被抛弃。
// 如果该点的梯度在两者之间则当这个点与高于上限值的像素点连接时我们才保留,否则删除。
int aperture_size = 3// 第五个参数表示Sobel 算子大小,默认为3即表示一个3*3的矩阵。
// Sobel 算子与高斯拉普拉斯算子都是常用的边缘算子
);
(3).轮廓提取
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int alpha_slider1 = 50, alpha_slider2 = 50;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[总]");
createTrackbar("Cmin", "[总]", &alpha_slider1, 255, on_trackbar);
createTrackbar("Cmax", "[总]", &alpha_slider2, 255, on_trackbar);
VideoCapture capture(0); //【1】从摄像头读入视频
while (1)
{
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
Mat dst;
capture >> frame; //读取当前帧
cvtColor(frame, frame, COLOR_RGB2GRAY); //转化为灰度图
Canny(frame, frame, alpha_slider1, alpha_slider2);
vector< vector< Point> > contours;
cv::findContours(
frame,
contours,
cv::noArray(),
cv::RETR_LIST,
/*cv::RETR_EXTERNAL:表示只提取最外面的轮廓;
cv::RETR_LIST:表示提取所有轮廓并将其放入列表;
cv::RETR_CCOMP:表示提取所有轮廓并将组织成一个两层结构,其中顶层轮廓是外部轮廓,第二层轮廓是“洞”的轮廓;
cv::RETR_TREE:表示提取所有轮廓并组织成轮廓嵌套的完整层级结构。*/
cv::CHAIN_APPROX_TC89_L1
/*cv::CHAIN_APPROX_NONE:将轮廓中的所有点的编码转换成点;
cv::CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角直线段,仅保留它们的端点;
cv::CHAIN_APPROX_TC89_L1 or cv::CHAIN_APPROX_TC89_KCOS:应用Teh-Chin链近似算法中的一种风格*/
);
dst = frame.clone();
dst = cv::Scalar::all(0); //创建全黑图像
cv::drawContours(dst, contours, -1, cv::Scalar::all(255)); //绘制白色轮廓
imshow("A", frame); //原canny滤波图像
imshow("B", dst); //对frame进行轮廓查找并重新绘制的图像
waitKey(30); //延时30ms
}
return 0;
}
轮廓的查找——cv::findContours()
函数cv::findContour是从二值图像中来计算轮廓的,它可以使用cv::Canny()函数处理的图像,因为这样的图像含有边缘像素;也可以使用cv::threshold()或者cv::adaptiveThreshold()处理后的图像,其边缘隐含在正负区域的交界处。
void cv::findContours(
cv::InputOutputArray image, // 输入的8位单通道“二值”图像
cv::OutputArrayOfArrays contours, // 包含points的vectors的vector
int mode, // 轮廓检索模式
int method, // 近似方法
cv::Point offset = cv::Point() // (可选) 所有点的偏移
);
输入图像的所有非零像素之间都是相等的
轮廓的绘制——cv::drawContours()。
(4).最后说说通过分析信息定位
在上一步的程序中定义了vector< vector< Point> > contours;,其中保存了图像的轮廓信息,vector<Point>表示一系列的角点,函数findContours会将每个轮廓的点按顺序压入。使用arcLength(,true);可以得到一个轮廓周长。
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
vector<Point> point_1; //定义二维点向量
point_1.push_back(Point(0, 0));
point_1.push_back(Point(1, 0));
point_1.push_back(Point(1, 1));
point_1.push_back(Point(0, 1)); //压入值
vector< vector < Point > > point; //定义二维向量,元素为点
point.push_back(point_1); //压入二维点向量
//计算轮廓的矩
double dstLength = arcLength(point[0], true); //计算刚压入轮廓的周长
cout << "原始轮廓的长度为:" << dstLength << endl;
getchar();
return 0;
}
↑这个程序说明了vector<Point>的存储及使用。
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int alpha_slider1 = 30, alpha_slider2 = 100;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[总]");
createTrackbar("Cmin", "[总]", &alpha_slider1, 500, on_trackbar);
createTrackbar("Cmax", "[总]", &alpha_slider2, 500, on_trackbar);
VideoCapture capture(0); // 从摄像头读入视频
while (1)
{
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
capture >> frame; //读取当前帧
Mat dst = frame.clone();
cvtColor(frame, frame, COLOR_RGB2GRAY); //转化为灰度图
GaussianBlur(frame, frame, Size(9, 9), 0); //高斯滤波
Canny(frame, frame, alpha_slider1, alpha_slider2); //边缘检测
vector< vector< Point> > contours;
cv::findContours(
frame,
contours,
cv::noArray(),
cv::RETR_LIST,
cv::CHAIN_APPROX_SIMPLE
); //获取轮廓
for (int i = 0; i < contours.size(); i++)
{
RotatedRect rect = minAreaRect(contours[i]); //返回最小矩形
Size s = rect.size;
Point2f P[4];
rect.points(P);
if ((contourArea(contours[i], true) / s.area()) - 0.7853 < 0.1 && (contourArea(contours[i], true) / s.area()) - 0.7853 > -0.1) //(圆)轮廓面积除以最小矩形面积约为
{
circle(dst, rect.center, 3, Scalar(255, 0, 0));
circle(dst, rect.center, 5, Scalar(255, 0, 0));
}
}
imshow("A", frame);
imshow("B", dst);
waitKey(30); //延时30ms
}
return 0;
}
↑这个程序包含图像灰度转换、滤波、边缘检测、轮廓获取、以及圆形识别。相比与霍夫圆变换,速度更快,不过效果没有它好。