感谢
基于C++的OpenCV项目实战——零部件的自动光学检测_opencv 滤波增强 零件测量-CSDN博客和
基于opencv的c++图像处理(图像二值化)_opencv c++ 二值化-CSDN博客
代码实现
main函数
#include<opencv2/opencv.hpp>
#include<iostream>
#include<string>
#include "Display.h"
#include "Blur.h"
using namespace cv;
using namespace std;
// 使用智能指针
shared_ptr<Display> multi_window;
int main(int argc, char** argv)
{
//图片和背景图
String total_path = "./resource/原图.jpeg";
String background_path = "./resource/背景图.jpg";
Mat image = imread(total_path);
Mat backgroud_image = imread(background_path);
//图片去噪处理
Mat image_denoising = image.clone();
Mat backgroud = backgroud_image.clone();
Blur br;
image_denoising = br.smooth_image(image);
backgroud = br.smooth_image(backgroud_image);
//去除背景
Mat image_no_backgroud = br.remove_background_minus(image_denoising, backgroud);
//连通域检测
Mat image_connection_check = br.connection_check(image_no_backgroud);
// 边缘检测
Mat image_contour = br.get_contour(image_no_backgroud);
//展示
multi_window = make_shared<Display>("Review for all", 3, 2, WINDOW_NORMAL);
multi_window->add_window("image", image);
multi_window->add_window("denoise", image_denoising);
multi_window->add_window("bg", backgroud);
multi_window->add_window("remove bg", image_no_backgroud);
multi_window->add_window("segment", image_connection_check);
multi_window->add_window("contour", image_contour);
// 保存结果
imwrite("./result/结果展示.png", multi_window->canvas);
waitKey(0);
return 0;
}
构建多窗口展示Display类
Display.cpp文件
#include "Display.h"
//初始化窗口
Display::Display(String t, int c, int r, int flags) :title(t), cols(c), rows(r) {
height = 1080;
width = 1920;
namedWindow(title, flags);
canvas = Mat(height, width, CV_8UC3);
imshow(title, canvas);
}
//增加图片
int Display::add_window(String win_name, Mat image, bool flag) {
win_names.push_back(win_name);
images.push_back(image);
if (flag) {
draw();
}
return win_names.size();
}
// 删除图片
int Display::delete_window(String win_name) {
int index = 0;
for (const auto& it : win_names) {
if (it == win_name) break;
index++;
}
win_names.erase(win_names.begin() + index);
images.erase(images.begin() + index);
//删除后更新绘图
draw();
return win_names.size();
}
//绘图
void Display::draw() {
canvas.setTo(Scalar(20, 20, 20));
int single_width = width / cols;
int single_height = height / rows;
int max_win = win_names.size() > cols * rows ? cols * rows : win_names.size();
int i = 0;
auto iw = win_names.begin();
for (auto it = images.begin(); it != images.end() && i < max_win; it++, i++, iw++) {
String win_name = *iw;
Mat img = *it;
int x = (single_width) * (i % cols);
int y = (single_height)*floor(i * 1.0 / cols);
Rect mask(x, y, single_width, single_height);
rectangle(canvas, mask, Scalar(255, 255, 255), 9);
Mat resized_img;
resize(img, resized_img, Size(single_width, single_height));
Mat sub_canvas(canvas, mask);
if (resized_img.channels() == 1) {
cvtColor(resized_img, resized_img, COLOR_GRAY2BGR);
}
resized_img.copyTo(sub_canvas);
putText(sub_canvas, win_name, Point(50, 50), FONT_HERSHEY_COMPLEX, 2, Scalar(0, 0, 255), 3, LINE_AA);
}
imshow(title, canvas);
}
Display.h
#pragma once
#include<opencv2/opencv.hpp>
#include<string>
#include<iostream>
using namespace cv;
using namespace std;
//多窗口展示类
class Display {
private:
int cols, rows, width, height;
String title;
vector<String> win_names;
vector<Mat> images;
public:
Mat canvas;
Display(String t, int c, int r, int flags);
//添加图片
int add_window(String win_name, Mat image, bool flag = true);
int delete_window(String win_name);// 实现删除窗口
void draw();
};
构建图像处理类(降噪、去除背景、连通域检测、边缘检测等)
Blur.cpp文件
#include "Blur.h"
Mat Blur::get_backgroud(Mat& image) {
Mat img_out;
medianBlur(image, img_out,3);
GaussianBlur(img_out, img_out, Size(3, 3), 0);
return img_out;
}
Mat Blur::smooth_image(Mat& image) {
Mat img_out;
medianBlur(image, img_out, 3);
GaussianBlur(img_out, img_out, Size(5, 5), 0);
return img_out;
}
//去除背景,除法
Mat Blur::remove_background_divide(Mat& image,Mat& backgroud) {
Mat img_out, fg, bg;
image.convertTo(fg, CV_32F);
backgroud.convertTo(bg, CV_32F);
img_out = 1 - (fg / bg);
img_out.convertTo(img_out, CV_8U, 255);
return img_out;
}
//去除背景,减法
Mat Blur::remove_background_minus(Mat& image, Mat& background) {
return background - image;
}
//连通域检测
Mat Blur::connection_check(Mat& image) {
if (image.type() != CV_8U) {
image.convertTo(image, CV_8U);
}
Mat srcGray;
if (image.channels() == 3)
{
cvtColor(image, srcGray, COLOR_RGB2GRAY);
}
else {
srcGray = image.clone();
}
//二值化
Mat otsuImage;
const int maxVal = 255;
int threshType = 0;
ImgB.OtsuBinarizate(srcGray, otsuImage, maxVal, threshType);
//连通域检测
Mat labels, stats, centroids;
int num = connectedComponentsWithStats(otsuImage, labels, stats, centroids);
if (num <= 1) {
cout << "No stuff detect!!" << endl;
return labels;
}
else
{
cout << num << " objects detected!!" << endl;
}
Mat display = Mat::zeros(otsuImage.rows, otsuImage.cols, CV_8UC3);
RNG rng(12345);
for (int i = 1; i < num; i++) {
// 得到连通域的质心点
Point2i pt(centroids.at<double>(i, 0), centroids.at<double>(i, 1));
if (stats.at<int>(i, CC_STAT_AREA) < 1000) { // 面积小于1000的不要
continue;
}
// 打印标签和连通域坐标和面积
cout << "Stuff #" << i << ", Position: " << pt << " ,Area: " << stats.at<int>(i, CC_STAT_AREA) << endl;
Mat mask = (labels == i);
int b = rng.uniform(0, 256);
int g = rng.uniform(0, 256);
int r = rng.uniform(0, 256);
display.setTo(Scalar(b,g,r), mask);
stringstream ss;
ss << stats.at<int>(i, CC_STAT_AREA);
putText(display, ss.str(), pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 255), 2);
}
return display;
}
//边缘检测
Mat Blur::get_contour(Mat& image) {
if (image.type() != CV_8U) {
image.convertTo(image, CV_8U);
}
Mat srcGray;
if (image.channels() == 3)
{
cvtColor(image, srcGray, COLOR_RGB2GRAY);
}
else {
srcGray = image.clone();
}
//二值化
Mat otsuImage;
const int maxVal = 255;
int threshType = 0;
ImgB.OtsuBinarizate(srcGray, otsuImage, maxVal, threshType);
vector<vector<Point>> contours;
findContours(otsuImage, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
Mat display = Mat::zeros(image.size(), CV_8UC3);
if (contours.size() == 0) {
cout << "没有轮廓" << endl;
return display;
}
else {
cout << "检测到轮廓: " << contours.size() << endl;
}
RNG rng(12345);
for (int i = 0; i < contours.size(); i++) {
int b = rng.uniform(0, 256);
int g = rng.uniform(0, 256);
int r = rng.uniform(0, 256);
drawContours(display, contours, i, Scalar(b, g, r));
}
return display;
}
Blur.h文件
#pragma once
#include<opencv2/opencv.hpp>
#include<iostream>
#include "Binarizate.h"
using namespace cv;
using namespace std;
class Blur
{
public:
ImgEnhance::Binarizate ImgB;//二值化
//降噪
Mat get_backgroud(Mat& image);
Mat smooth_image(Mat& image);
//去除背景图
Mat remove_background_divide(Mat& image, Mat& backgroud);
Mat remove_background_minus(Mat& image, Mat& background);
//连通域检测
Mat connection_check(Mat& image);
//边缘检测
Mat get_contour(Mat& image);
};
附:图像二值化方法
Binarizate.h
#pragma once
#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;
#define PROCESS_IMG_SUCESS 0
#define PROCESS_IMG_FAIL 1
namespace ImgEnhance
{
//二值化
class Binarizate
{
public:
Binarizate() { cout << "Binarizate is being created" << endl; } // 这是构造函数声明
~Binarizate() { cout << "Binarizate is being deleted" << endl; } // 这是析构函数声明
int OTSU(cv::Mat srcImage);// 大律法函数实现
int OtsuBinarizate(cv::Mat srcImage, cv::Mat& dstImage, int maxVal, int threshType);//大律法二值化
int FixedBinarizate(cv::Mat srcImage, cv::Mat& dstImage, int thresh, int maxVal, int threshType);//固定化阈值
int AdaptiveBinarizate(cv::Mat srcImage, cv::Mat& dstImage, int maxVal, int adaptiveMethod, int thresholdType, int blockSize, int constValue);//自适应阈值化
};
}
Binarizate.cpp
#include "binarizate.h"
int ImgEnhance::Binarizate::OTSU(cv::Mat srcImage)
{
int nCols = srcImage.cols;
int nRows = srcImage.rows;
int threshold = 0;
// 初始化统计参数
int nSumPix[256];
float nProDis[256];
for (int i = 0; i < 256; i++)
{
nSumPix[i] = 0;
nProDis[i] = 0;
}
// 统计灰度级中每个像素在整幅图像中的个数
for (int i = 0; i < nCols; i++)
{
for (int j = 0; j < nRows; j++)
{
nSumPix[(int)srcImage.at<uchar>(i, j)]++;
}
}
// 计算每个灰度级占图像中的概率分布
for (int i = 0; i < 256; i++)
{
nProDis[i] = (float)nSumPix[i] / (nCols * nRows);
}
// 遍历灰度级[0,255],计算出最大类间方差下的阈值
float w0, w1, u0_temp, u1_temp, u0, u1, delta_temp;
double delta_max = 0.0;
for (int i = 0; i < 256; i++)
{
// 初始化相关参数
w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;
for (int j = 0; j < 256; j++)
{
//背景部分
if (j <= i)
{
// 当前i为分割阈值,第一类总的概率
w0 += nProDis[j];
u0_temp += j * nProDis[j];
}
//前景部分
else
{
// 当前i为分割阈值,第一类总的概率
w1 += nProDis[j];
u1_temp += j * nProDis[j];
}
}
// 分别计算各类的平均灰度
u0 = u0_temp / w0;
u1 = u1_temp / w1;
delta_temp = (float)(w0 * w1 * pow((u0 - u1), 2));
// 依次找到最大类间方差下的阈值
if (delta_temp > delta_max)
{
delta_max = delta_temp;
threshold = i;
}
}
return threshold;
}
int ImgEnhance::Binarizate::OtsuBinarizate(cv::Mat srcImage, cv::Mat& dstImage, int maxVal, int threshType)
{
if (!srcImage.data || srcImage.channels() != 1)
{
return 1;
}
// 初始化阈值参数
int thresh = OTSU(srcImage);
// 初始化阈值化处理的类型
/* 0: 二进制阈值 1: 反二进制阈值 2: 截断阈值
3: 0阈值 4: 反0阈值*/
//int threshType = 0;
// 预设最大值
//const int maxVal = 255;
// 固定阈值化操作
cv::threshold(srcImage, dstImage, thresh,
maxVal, threshType);
return 0;
}
int ImgEnhance::Binarizate::FixedBinarizate(cv::Mat srcImage, cv::Mat& dstImage, int thresh, int maxVal, int threshType)
{
if (!srcImage.data || srcImage.channels() != 1)
{
return 1;
}
// 初始化阈值参数
//int thresh = 130;
// 初始化阈值化处理的类型
/* 0: 二进制阈值 1: 反二进制阈值 2: 截断阈值
3: 0阈值 4: 反0阈值 8:大均法*/
//int threshType = 0;
// 预设最大值
//const int maxVal = 255;
// 固定阈值化操作
cv::threshold(srcImage, dstImage, thresh,
maxVal, threshType);
return 0;
}
int ImgEnhance::Binarizate::AdaptiveBinarizate(cv::Mat srcImage, cv::Mat& dstImage, int maxVal, int adaptiveMethod, int thresholdType, int blockSize, int constValue)
{
if (!srcImage.data || srcImage.channels() != 1)
{
return 1;
}
// 初始化自适应阈值参数
//int blockSize = 5;
//int constValue = 10;
//const int maxVal = 255;
/* 自适应阈值算法
0:ADAPTIVE_THRESH_MEAN_C
1: ADAPTIVE_THRESH_GAUSSIAN_C
阈值类型
0: THRESH_BINARY
1: THRESH_BINARY_INV */
//int adaptiveMethod = 0;
//int thresholdType = 1;
// 图像自适应阈值操作
cv::adaptiveThreshold(srcImage, dstImage, maxVal, adaptiveMethod, thresholdType, blockSize, constValue);
return 0;
}
附件:原图.jpeg和背景图.jpg
图片来源:【DIY贴片机】基于opencv识别定位电子元件_电感元件定位 opencv-CSDN博客
运行后的结果
结果展示.png