写在前面
这次分享的为一个很理想的情况下的目标识别与分类,对象为螺丝、螺帽、圆环这三个东西,其实就是图一乐呵,为什么说理想化呢?看一下本文使用的实验图片。
可以看到,图像中三个目标非常清楚,因为该图背景非常单一,这张图为在我床单上拍的。最近也还在补充图像处理相关的数学基础理论,要达到能在复杂背景下的目标检测与识别,需要学习的地方还很多。
方法流程
- 图像预处理,包括去噪、去除背景、阈值化
- 图像分割
- 特征提取
- 机器分类学习
下面根据该顺序具体介绍相关代码以及效果。
1.图像预处理
首先是去除噪声,本文使用高斯滤波去噪。然后是去除背景,在这一步骤由于图像是理性化环境下拍摄的,所以特别好处理,只需要用大核卷积模糊图像来而得到背景图,然后使用图片与背景图进行差分,就可以达到背景去除的目的。最后是阈值化,选取阈值将图像变为二值图像,便于图像分割。
//获得背景图像
cv::Mat calculateLightPattern(cv::Mat img)
{
cv::Mat pattern;
//通过使用相对于图像大小的大内核尺寸模糊得到背景图
blur(img, pattern, cv::Size(img.cols / 3, img.cols / 3));
return pattern;
}
//移除背景
cv::Mat removeLight(cv::Mat img, cv::Mat pattern)
{
cv::Mat result;
cv::Mat img32, pattern32;
img.convertTo(img32, CV_32F);
pattern.convertTo(pattern32, CV_32F);
//通过背景图像移除背景
result = 1 - (pattern32/img32);
result = result * 255;
result.convertTo(result, CV_8U);
/*result = img-pattern;*/
return result;
}
以上为预处理阶段需要的子函数,在预处理函数中将两个函数调用
//预处理
cv::Mat preprocessImage(cv::Mat input)
{
cv::Mat result;
//去噪
cv::Mat img_noise;
GaussianBlur(input, img_noise, cv::Size(5, 5), 0, 0);
//去除背景
cv::Mat img_no_light;
cv::Mat light_pattern = calculateLightPattern(img_noise);
img_no_light = removeLight(img_noise, light_pattern);
//阈值化处理
threshold(img_no_light, result, 30, 255, cv::THRESH_BINARY);
return result;
}
在目标检测函数中调用,结果如下所示(第一幅图为灰度图像,第二幅图为通过图像模糊得到的背景图,第三幅图为通过背景图将灰度图像背景之后的结果图。得到结果图后阈值化,得到第四幅图):
2.图像分割
本文中采用连通组件算法进行图像分割。因为上述操作已经将图像转为二值图像,采用八个或者四个连接像素来标记图像。如果两个像素具有相同的值并且是邻居,则把他们连接起来。得到一个个对象就是识别出来的结果,下面是图像识别函数
int targetDectation(std::string path)
{
//读取图像
cv::Mat imageGray,imageRGB = cv::imread(path, cv::IMREAD_COLOR);
if (imageRGB.empty())
{
std::cout << "Fail to read image:" << path << std::endl;
return -1;
}
cv::cvtColor(imageRGB, imageGray, cv::COLOR_RGB2GRAY);
cv::imshow("ImageGray", imageGray);
cv::waitKey(0);
//去除噪声
GaussianBlur(imageGray, imageGray, cv::Size(5, 5), 0, 0);//使用opencv自带高斯滤波预处理
//获得背景
cv::Mat imgPattern = calculateLightPattern(imageGray);
//移除背景
imageGray = removeLight(imageGray, imgPattern);
cv::imshow("ImageGray", imageGray);
cv::waitKey(0);
//阈值化
cv::Mat imageThr;
cv::threshold(imageGray, imageThr, 50, 255, cv::THRESH_BINARY);
cv::imshow("thr", imageThr);
cv::waitKey(0);
//通过连通组件算法分割
cv::Mat labels, stats, centroids;
int num_objects = connectedComponentsWithStats(imageThr, labels, stats, centroids);
//检查检测出的数目数量
if (num_objects < 2){
std::cout << "No objects detected" << std::endl;
return -1;
}
else{
std::cout << "Number of objects detected: " << num_objects - 1 << std::endl;
}
//展示图像分割结果
cv::Mat output = cv::Mat::zeros(imageThr.rows, imageThr.cols, CV_8UC3);
cv::RNG randomNumGenerator(0xFFFFFFFF);