实现目标:瓷砖检测(色、形、纹理、特征+分类器/匹配器)在线识别
实现难度:目标物体分割不准确带来的问题;光照不均导致的特征提取鲁棒性不足。
目录
实现目标:瓷砖检测(色、形、纹理、特征+分类器/匹配器)在线识别
特征提取——颜色、形状、纹理、局部特征(防止检测无法使用全局)
分割——去除背景和干扰
方法一:分水岭
// 分水岭
/*
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
*/
// 分水岭
void waterSegment(InputArray& _src, OutputArray& _dst, int& noOfSegment)
{
Mat src;// =_src.getMat();//dst = _dst.getMat();
_src.copyTo(src);
resize(src, src, Size(0.9*src.cols, 0.9*src.rows));//不这么做就会出错
Mat grayImage;
cvtColor(src, grayImage, CV_BGR2GRAY);
threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);
normalize(grayImage, grayImage, 0, 1, NORM_MINMAX);
grayImage.convertTo(grayImage, CV_8UC1);
threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Mat showImage = Mat::zeros(grayImage.size(), CV_32SC1);
findContours(grayImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
for (size_t i = 0; i < contours.size(); i++)
{
//这里static_cast<int>(i+1)是为了分水岭的标记不同,区域1、2、3。。。。这样才能分割
drawContours(showImage, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i + 1)), 2);
}
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(src, src, MORPH_ERODE, k);//形态变换
watershed(src, showImage);
//随机分配颜色
vector<Vec3b> colors;
for (size_t i = 0; i < contours.size(); i++) {
int r = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int b = theRNG().uniform(0, 255);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
#if 1
// 显示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
int index = 0;
for (int row = 0; row < showImage.rows; row++) {
for (int col = 0; col < showImage.cols; col++) {
index = showImage.at<int>(row, col);
if (index > 0 && index <= contours.size()) {
dst.at<Vec3b>(row, col) = colors[index - 1];
}
else if (index == -1)
{
dst.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
}
else {
dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
}
}
}
resize(dst, dst, Size(10 / 9 * dst.cols, 10 / 9 * dst.rows));//不这么做就会出错
dst.copyTo(_dst);
#endif
#if 0
// 筛选中心区为一类的目标和非中心区
// 显示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
Mat edge_dst_ = Mat::zeros(showImage.size(), CV_8UC3);
int index = showImage.at<int>(showImage.rows / 2, showImage.cols / 2);;
int index_temp = 0;
for (int row = 0; row < showImage.rows; row++) {
for (int col = 0; col < showImage.cols; col++) {
index_temp = showImage.at<int>(row, col);
if (index_temp == index) {
dst.at<Vec3b>(row, col) = src.at<Vec3b>(row, col);//colors[index - 1];
edge_dst_.at<Vec3b>(row, col) = colors[index - 1];
}
}
}
dst.copyTo(_dst);
edge_dst_.copyTo(edge_dst);
#endif
}
分水岭+目标选择
// 分水岭
void waterSegment(InputArray& _src, OutputArray& _dst, OutputArray& edge_dst, int& noOfSegment)
{
Mat src;// =_src.getMat();//dst = _dst.getMat();
_src.copyTo(src);
resize(src, src, Size(0.9*src.cols, 0.9*src.rows));//不这么做就会出错
Mat grayImage;
cvtColor(src, grayImage, CV_BGR2GRAY);
threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);
normalize(grayImage, grayImage, 0, 1, NORM_MINMAX);
grayImage.convertTo(grayImage, CV_8UC1);
threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Mat showImage = Mat::zeros(grayImage.size(), CV_32SC1);
findContours(grayImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
for (size_t i = 0; i < contours.size(); i++)
{
//这里static_cast<int>(i+1)是为了分水岭的标记不同,区域1、2、3。。。。这样才能分割
drawContours(showImage, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i + 1)), 2);
}
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(src, src, MORPH_ERODE, k);//形态变换
watershed(src, showImage);
//随机分配颜色
vector<Vec3b> colors;
for (size_t i = 0; i < contours.size(); i++) {
int r = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int b = theRNG().uniform(0, 255);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
#if 0
// 显示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
int index = 0;
for (int row = 0; row < showImage.rows; row++) {
for (int col = 0; col < showImage.cols; col++) {
index = showImage.at<int>(row, col);
if (index > 0 && index <= contours.size()) {
dst.at<Vec3b>(row, col) = colors[index - 1];
}
else if (index == -1)
{
dst.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
}
else {
dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
}
}
}
resize(dst, dst, Size(10 / 9 * dst.cols, 10 / 9 * dst.rows));//不这么做就会出错
#endif
// 筛选中心区为一类的目标和非中心区
// 显示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
Mat edge_dst_ = Mat::zeros(showImage.size(), CV_8UC3);
int index = showImage.at<int>(showImage.rows / 2, showImage.cols / 2);;
int index_temp = 0;
for (int row = 0; row < showImage.rows; row++) {
for (int col = 0; col < showImage.cols; col++) {
index_temp = showImage.at<int>(row, col);
if (index_temp == index) {
dst.at<Vec3b>(row, col) = src.at<Vec3b>(row, col);//colors[index - 1];
edge_dst_.at<Vec3b>(row, col) = colors[index - 1];
}
}
}
dst.copyTo(_dst);
edge_dst_.copyTo(edge_dst);
}
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\方格.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\条纹3.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\空纹理.bmp");
Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\花纹.bmp");
GaussianBlur(src, src, cv::Size(5, 5), 0, 0);
int offSegment;
Mat outputImage = Mat::zeros(src.size(), CV_8UC3);
Mat outputedge = Mat::zeros(src.size(), CV_8UC3);
waterSegment(src, outputImage, outputedge, offSegment);
imshow("outputImage", outputImage);
waitKey();
子函数:时刻返回目标图+背景置零图
// 分水岭
void waterSegment(InputArray& _src, OutputArray& _dst, OutputArray& edge_dst, int& noOfSegment)
{
Mat src;// =_src.getMat();//dst = _dst.getMat();
_src.copyTo(src);
resize(src, src, Size(0.9*src.cols, 0.9*src.rows));//不这么做就会出错
Mat grayImage;
cvtColor(src, grayImage, CV_BGR2GRAY);
threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);
normalize(grayImage, grayImage, 0, 1, NORM_MINMAX);
grayImage.convertTo(grayImage, CV_8UC1);
threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Mat showImage = Mat::zeros(grayImage.size(), CV_32SC1);
findContours(grayImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
for (size_t i = 0; i < contours.size(); i++)
{
//这里static_cast<int>(i+1)是为了分水岭的标记不同,区域1、2、3。。。。这样才能分割
drawContours(showImage, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i + 1)), 2);
}
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(src, src, MORPH_ERODE, k);//形态变换
watershed(src, showImage);
//随机分配颜色
vector<Vec3b> colors;
for (size_t i = 0; i < contours.size(); i++) {
int r = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int b = theRNG().uniform(0, 255);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
#if 0
// 显示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
int index = 0;
for (int row = 0; row < showImage.rows; row++) {
for (int col = 0; col < showImage.cols; col++) {
index = showImage.at<int>(row, col);
if (index > 0 && index <= contours.size()) {
dst.at<Vec3b>(row, col) = colors[index - 1];
}
else if (index == -1)
{
dst.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
}
else {
dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
}
}
}
resize(dst, dst, Size(10 / 9 * dst.cols, 10 / 9 * dst.rows));//不这么做就会出错
#endif
// 筛选中心区为一类的目标和非中心区
// 显示
Mat dst = Mat::zeros(showImage.size(), CV_8UC3);
Mat edge_dst_ = Mat::zeros(showImage.size(), CV_8UC3);
int index = showImage.at<int>(showImage.rows / 2, showImage.cols / 2);;
int index_temp = 0;
for (int row = 0; row < showImage.rows; row++) {
for (int col = 0; col < showImage.cols; col++) {
index_temp = showImage.at<int>(row, col);
if (index_temp == index) {
dst.at<Vec3b>(row, col) = src.at<Vec3b>(row, col);//colors[index - 1];
edge_dst_.at<Vec3b>(row, col) = colors[index - 1];
}
}
}
dst.copyTo(_dst);
edge_dst_.copyTo(edge_dst);
}
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\方格.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\条纹2.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\空纹理.bmp");
Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\花纹.bmp");
int offSegment;
Mat outputImage = Mat::zeros(src.size(), CV_8UC3);
Mat outputedge = Mat::zeros(src.size(), CV_8UC3);
waterSegment(src, outputImage, outputedge, offSegment);
imshow("outputImage", outputImage);
waitKey();
特征提取——颜色、形状、纹理、局部特征(防止检测无法使用全局)
特征提取——颜色
//计算三阶矩
double calc3orderMom(Mat &channel) //计算三阶矩
{
uchar *p;
double mom = 0;
double m = mean(channel)[0]; //计算单通道图像的均值
int nRows = channel.rows;
int nCols = channel.cols;
if (channel.isContinuous()) //连续存储有助于提升图像扫描速度
{
nCols *= nRows;
nRows = 1;
}
for (int i = 0; i < nRows; i++) //计算立方和
{
p = channel.ptr<uchar>(i);
for (int j = 0; j < nCols; j++)
mom += pow((p[j] - m), 3);
}
float temp;
temp = cvCbrt((float)(mom / (nRows*nCols))); //求均值的立方根
mom = (double)temp;
return mom;
}
//计算9个颜色矩:3个通道的1、2、3阶矩
double *colorMom(Mat &img)
{
double *Mom = new double[9]; //存放9个颜色矩
if (img.channels() != 3)
cout << "Error,input image must be a color image" << endl;
Mat r(img.rows, img.cols, CV_8U);
Mat g(img.rows, img.cols, CV_8U);
Mat b(img.rows, img.cols, CV_8U);
Mat channels[] = { b, g, r };
split(img, channels);
Mat tmp_m, tmp_sd;
//计算b通道的颜色矩
meanStdDev(b, tmp_m, tmp_sd);
Mom[0] = tmp_m.at<double>(0, 0);
Mom[3] = tmp_sd.at<double>(0, 0);
Mom[6] = calc3orderMom(b);
// cout << Mom[0] << " " << Mom[1] << " " << Mom[2] << " " << endl;
//计算g通道的颜色矩
meanStdDev(g, tmp_m, tmp_sd);
Mom[1] = tmp_m.at<double>(0, 0);
Mom[4] = tmp_sd.at<double>(0, 0);
Mom[7] = calc3orderMom(g);
// cout << Mom[3] << " " << Mom[4] << " " << Mom[5] << " " << endl;
//计算r通道的颜色矩
meanStdDev(r, tmp_m, tmp_sd);
Mom[2] = tmp_m.at<double>(0, 0);
Mom[5] = tmp_sd.at<double>(0, 0);
Mom[8] = calc3orderMom(r);
// cout << Mom[6] << " " << Mom[7] << " " << Mom[8] << " " << endl;
return Mom;//返回颜色矩数组
}
Mat src = imread("图像前景图.bmp");
double *Mom;
Mom = colorMom(src);
cout << "Color moments:" << endl;
for (int i = 0; i < 9; i++)//输出颜色矩
cout << Mom[i] << endl;
特征提取——形状
//计算三阶矩
double calc3orderMom(Mat &channel) //计算三阶矩
{
uchar *p;
double mom = 0;
double m = mean(channel)[0]; //计算单通道图像的均值
int nRows = channel.rows;
int nCols = channel.cols;
if (channel.isContinuous()) //连续存储有助于提升图像扫描速度
{
nCols *= nRows;
nRows = 1;
}
for (int i = 0; i < nRows; i++) //计算立方和
{
p = channel.ptr<uchar>(i);
for (int j = 0; j < nCols; j++)
mom += pow((p[j] - m), 3);
}
float temp;
temp = cvCbrt((float)(mom / (nRows*nCols))); //求均值的立方根
mom = (double)temp;
return mom;
}
//计算9个颜色矩:3个通道的1、2、3阶矩
double *colorMom(Mat &img)
{
double *Mom = new double[9]; //存放9个颜色矩
if (img.channels() != 3)
cout << "Error,input image must be a color image" << endl;
imshow("img", img);
Mat b(img.rows, img.cols, CV_8U);
Mat r(img.rows, img.cols, CV_8U);
Mat g(img.rows, img.cols, CV_8U);
Mat channels[] = { b, g, r };
split(img, channels);
imshow("r", channels[0]);
imshow("g", channels[1]);
imshow("b", channels[2]);
waitKey(0);
Mat tmp_m, tmp_sd;
//计算b通道的颜色矩
meanStdDev(b, tmp_m, tmp_sd);
Mom[0] = tmp_m.at<double>(0, 0);
Mom[3] = tmp_sd.at<double>(0, 0);
Mom[6] = calc3orderMom(b);
// cout << Mom[0] << " " << Mom[1] << " " << Mom[2] << " " << endl;
//计算g通道的颜色矩
meanStdDev(g, tmp_m, tmp_sd);
Mom[1] = tmp_m.at<double>(0, 0);
Mom[4] = tmp_sd.at<double>(0, 0);
Mom[7] = calc3orderMom(g);
// cout << Mom[3] << " " << Mom[4] << " " << Mom[5] << " " << endl;
//计算r通道的颜色矩
meanStdDev(r, tmp_m, tmp_sd);
Mom[2] = tmp_m.at<double>(0, 0);
Mom[5] = tmp_sd.at<double>(0, 0);
Mom[8] = calc3orderMom(r);
// cout << Mom[6] << " " << Mom[7] << " " << Mom[8] << " " << endl;
return Mom;//返回颜色矩数组
}
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\花纹.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\方格.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\条纹2.bmp");
Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\空纹理.bmp");
int offSegment;
Mat outputImage = Mat::zeros(src.size(), CV_8UC3);
Mat outputedge = Mat::zeros(src.size(), CV_8UC3);
waterSegment(src, outputImage, outputedge, offSegment);
cvtColor(outputedge, outputedge, CV_RGB2GRAY);
//imshow("outputedge", outputedge);
//waitKey();
Canny(outputedge, outputedge, 200, 120);
imshow("outputedge", outputedge);
//waitKey();
double Hu[7]; //存储得到的Hu矩阵
Moments mo; //矩变量
mo = moments(outputedge);
HuMoments(mo, Hu);
for (int r = 0; r < 7; r++)
cout << (float)Hu[r] << endl;
特征提取——纹理
/*头文件GLCM.h*/
#include<iostream>
#include<opencv2/highgui.hpp>
#include<opencv2/core.hpp>
#include<opencv2/opencv.hpp>
#include <vector>
#include <fstream>
#include <io.h>
using namespace std;
using namespace cv;
#pragma once
class GLCM
{
public:
const int gray_level = 16;//纹理区域块的大小,通常将图像划分成若干个纹理块计算
vector<double> glamvalue;//全局变量
//【】第一步:j计算共生矩阵
// 0度灰度共生矩阵
void getglcm_0(Mat& input, Mat& dst)//0度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)//寻找像素灰度最大值
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;//像素灰度最大值加1即为该矩阵所拥有的灰度级数
if (max_gray_level > 16)//若灰度级数大于16,则将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
else//若灰度级数小于16,则生成相应的灰度共生矩阵
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
// 45度灰度共生矩阵
void getglcm_45(Mat& input, Mat& dst)//45度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;
if (max_gray_level > 16)
{
for (int i = 0; i < height; i++)//将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
else
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
// 90度灰度共生矩阵
void getglcm_90(Mat& input, Mat& dst)//90度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;
if (max_gray_level > 16)
{
for (int i = 0; i < height; i++)//将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j];
dst.ptr<int>(rows)[cols]++;
}
}
}
else
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
// 135度灰度共生矩阵
void getglcm_135(Mat& input, Mat& dst)//135度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;
if (max_gray_level > 16)
{
for (int i = 0; i < height; i++)//将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 1; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j - 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
else
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 1; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j - 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
// 【】第二步:计算纹理特征
// 特征值计算—— double& Asm, double& Con, double& Ent, double& Idm
void feature_computer(Mat&src, double& Asm, double& Con, double& Ent, double& Idm)//计算特征值
{
int height = src.rows;
int width = src.cols;
int total = 0;
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
total += srcdata[j];//求图像所有像素的灰度值的和
}
}
Mat copy;
copy.create(height, width, CV_64FC1);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
double*copydata = copy.ptr<double>(i);
for (int j = 0; j < width; j++)
{
copydata[j] = (double)srcdata[j] / (double)total;//图像每一个像素的的值除以像素总和
}
}
for (int i = 0; i < height; i++)
{
double*srcdata = copy.ptr<double>(i);
for (int j = 0; j < width; j++)
{
Asm += srcdata[j] * srcdata[j]; //能量
if (srcdata[j]>0)
{
Ent -= srcdata[j] * log(srcdata[j]); //熵
}
Con += (double)(i - j)*(double)(i - j)*srcdata[j]; //对比度
Idm += srcdata[j] / (1 + (double)(i - j)*(double)(i - j)); //逆差矩
}
}
}
};
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\花纹.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\方格.bmp");
//Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\条纹2.bmp");
Mat src = imread("C:\\Users\\Administrator\\Desktop\\样品\\瓷砖\\空纹理.bmp");
int offSegment;
Mat outputImage = Mat::zeros(src.size(), CV_8UC3);
Mat outputedge = Mat::zeros(src.size(), CV_8UC3);
waterSegment(src, outputImage, outputedge, offSegment);
imshow("outputImage", outputImage);
waitKey();
Mat kernel(3, 3, CV_32F, Scalar(-1));
// 分配像素置
// kernel.at<float>(1,1) = 8;
kernel.at<float>(1, 1) = 8.9;
filter2D(outputImage, outputImage, outputImage.depth(), kernel);
imshow("锐化结果", outputImage);
waitKey();
Mat src_gray;
cvtColor(outputImage, src_gray, CV_RGB2GRAY);
Canny(src_gray, src_gray, 0, 120);
imshow("shouedge", src_gray);
waitKey();
GLCM MyGlcm;
Mat dst_0, dst_90, dst_45, dst_135;
MyGlcm.getglcm_0(src_gray, dst_0);
double asm_0 = 0, con_0 = 0, ent_0 = 0, idm_0 = 0;
MyGlcm.feature_computer(dst_0, asm_0, con_0, ent_0, idm_0);
cout << "0度,45度,90度,135度" <<"asm,con,eng,idm" << endl;
cout << asm_0 << endl;
cout << con_0 << endl;
cout << ent_0 << endl;
cout << idm_0 << endl;
MyGlcm.getglcm_45(src_gray, dst_45);
double asm_45 = 0, con_45 = 0, ent_45 = 0, idm_45 = 0;
MyGlcm.feature_computer(dst_45, asm_45, con_45, ent_45, idm_45);
cout << asm_45 << endl;
cout << con_45 << endl;
cout << ent_45 << endl;
cout << idm_45 << endl;
MyGlcm.getglcm_90(src_gray, dst_90);
double asm_90 = 0, con_90 = 0, ent_90 = 0, idm_90 = 0;
MyGlcm.feature_computer(dst_90, asm_90, con_90, ent_90, idm_90);
cout << asm_90 << endl;
cout << con_90 << endl;
cout << ent_90 << endl;
cout << idm_90 << endl;
MyGlcm.getglcm_135(src_gray, dst_135);
double asm_135 = 0, con_135 = 0, ent_135 = 0, idm_135 = 0;
MyGlcm.feature_computer(dst_135, asm_135, con_135, ent_135, idm_135);
cout << asm_135 << endl;
cout << con_135 << endl;
cout << ent_135 << endl;
cout << idm_135 << endl;
double AMS[4] = { asm_0, asm_45, asm_90, asm_135 };
double COM[4] = { con_0, con_45, con_90, con_135 };
double ENT[4] = { ent_0, ent_45, ent_90, ent_135 };
double IDM[4] = { idm_0, idm_45, idm_90, idm_135 };
特征提取——特征点
(比较特殊,按步来进行分析,再进行总体进行)
想要知道什么是特征点、特征描述、特征匹配看此链接(https://blog.csdn.net/qq_37764129/article/details/80967628)
/*图像预处理灰度化+锐化一下*/
/*
#include <opencv2/opencv.hpp>
#include <iostream>
#include "testcode.h"
using namespace cv;
using namespace std;
*/
// 初始化 AKAZE 检测描述子
Ptr<AKAZE> detector = AKAZE::create();
vector<KeyPoint> keypoints_obj;
Mat descriptor_obj;
// 计时开始
double t1 = getTickCount();
// 检测到特征点:keypoints_obj + 计算得到特征描述符:descriptor_obj
detector->detectAndCompute(result, Mat(), keypoints_obj, descriptor_obj);
// 特征描述符打印:
namedWindow("descriptor_obj", 0);
imshow("descriptor_obj", descriptor_obj);
waitKey();
// 计时结束
double t2 = getTickCount();
double tkaze = 1000 * (t2 - t1) / getTickFrequency();
printf("AKAZE Time consume(ms) : %f\n", tkaze);
// 特征点:keypoints_obj 打印
std::cout << "keypoints_obj尺寸:" << keypoints_obj.size() << endl;
for (size_t i = 0; i < keypoints_obj.size(); i++)
{
std::cout << "keypoints_obj关键点的方向:" << keypoints_obj[i].angle << endl;
std::cout << "keypoints_obj关键点的坐标:" << keypoints_obj[i].pt.x << keypoints_obj[i].pt.y << endl;
}
// 特征描述符:descriptor_obj 用于匹配计算
特征描述符打印:
分类器
分类器——SVM
色、形、纹属性进行分类器训练
/*
针对glcm提取的特征进行svm分类
分四类、6个样本、测试样本2个。
#include <opencv2\ml\ml.hpp>
#include "灰度共生矩阵的头文件"
using namespace cv;
*/
float train_data[4][16] = { 0 };
/*
#include <iostream>
using namespace std;
*/
std::string img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\目标分割结果\\";
for (int i = 0; i < 4; i++){
string pos;
stringstream ss;
ss << i;
ss >> pos;
string img_name = img_dir + "test" + pos + ".bmp"; //cout << img_name << endl;
Mat target = imread(img_name, 1);
// 灰度化
Mat src_gray;
cv::cvtColor(target, src_gray, CV_RGB2GRAY);
// 锐化
Mat kernel(3, 3, CV_32F, Scalar(-1));
kernel.at<float>(1, 1) = 8.9;
cv::filter2D(src_gray, src_gray, src_gray.depth(), kernel); //cv::imshow("锐化结果", target); waitKey();
cv::Canny(src_gray, src_gray, 0, 120);
Mat dkernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(src_gray, src_gray, dkernel);
//cv::imshow("shouedge", src_gray);
cv::waitKey();
GLCM MyGlcm;
Mat dst_0, dst_90, dst_45, dst_135;
MyGlcm.getglcm_0(src_gray, dst_0);
float asm_0 = 0, con_0 = 0, ent_0 = 0, idm_0 = 0;
MyGlcm.feature_computer(dst_0, asm_0, con_0, ent_0, idm_0);
MyGlcm.getglcm_45(src_gray, dst_45);
float asm_45 = 0, con_45 = 0, ent_45 = 0, idm_45 = 0;
MyGlcm.feature_computer(dst_45, asm_45, con_45, ent_45, idm_45);
MyGlcm.getglcm_90(src_gray, dst_90);
float asm_90 = 0, con_90 = 0, ent_90 = 0, idm_90 = 0;
MyGlcm.feature_computer(dst_90, asm_90, con_90, ent_90, idm_90);
MyGlcm.getglcm_135(src_gray, dst_135);
float asm_135 = 0, con_135 = 0, ent_135 = 0, idm_135 = 0;
MyGlcm.feature_computer(dst_135, asm_135, con_135, ent_135, idm_135);
float AMS[4] = { asm_0, asm_45, asm_90, asm_135 };
float COM[4] = { con_0, con_45, con_90, con_135 };
float ENT[4] = { ent_0, ent_45, ent_90, ent_135 };
float IDM[4] = { idm_0, idm_45, idm_90, idm_135 };
float glcm_data[16] = {
asm_0, asm_45, asm_90, asm_135,
con_0, con_45, con_90, con_135,
ent_0, ent_45, ent_90, ent_135,
idm_0, idm_45, idm_90, idm_135
};
std::cout << "特征数据:" << endl;
for (size_t j = 0; j < 16; j++)
{
train_data[i][j] = glcm_data[j];
std::cout << train_data[i][j] << " ";
}
std::cout << endl;
}//特征数据提取完毕
std::cout << "特征数据提取完毕" << endl;
int label[4] = { 1, 2, 3, 4 };
//转为Mat以调用
Mat dataMat(4, 16, CV_32FC1, train_data);
Mat labelMat(4, 1, CV_32SC1, label);
//训练的初始化
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
//开始训练
svm->train(dataMat, ROW_SAMPLE, labelMat);
// save_model
svm->save("svm.xml");
svm->load("svm.xml");
float test_data[1][16] = { 0 };
float testData[16] = { 0 };
std::string img_dir1 = "C:\\Users\\Administrator\\Desktop\\样品\\目标分割结果\\";
for (int i = 5; i < 6; i++){
string pos;
stringstream ss;
ss << i;
ss >> pos;
string img_name = img_dir1 + "test" + pos + ".bmp"; //
cout << img_name << endl;
Mat target = imread(img_name, 1);
// 灰度化
Mat src_gray;
cv::cvtColor(target, src_gray, CV_RGB2GRAY);
// 锐化
Mat kernel(3, 3, CV_32F, Scalar(-1));
kernel.at<float>(1, 1) = 8.9;
cv::filter2D(src_gray, src_gray, src_gray.depth(), kernel); //cv::imshow("锐化结果", target); waitKey();
cv::Canny(src_gray, src_gray, 0, 120);
//进行膨胀操作
Mat dkernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(src_gray, src_gray, dkernel);
imshow("lalalalaltest", src_gray);
waitKey();
MyGLCM MyGlcm;
Mat dst_0, dst_90, dst_45, dst_135;
MyGlcm.getglcm_0(src_gray, dst_0);
float asm_0 = 0, con_0 = 0, ent_0 = 0, idm_0 = 0;
MyGlcm.feature_computer(dst_0, asm_0, con_0, ent_0, idm_0);
MyGlcm.getglcm_45(src_gray, dst_45);
float asm_45 = 0, con_45 = 0, ent_45 = 0, idm_45 = 0;
MyGlcm.feature_computer(dst_45, asm_45, con_45, ent_45, idm_45);
MyGlcm.getglcm_90(src_gray, dst_90);
float asm_90 = 0, con_90 = 0, ent_90 = 0, idm_90 = 0;
MyGlcm.feature_computer(dst_90, asm_90, con_90, ent_90, idm_90);
MyGlcm.getglcm_135(src_gray, dst_135);
float asm_135 = 0, con_135 = 0, ent_135 = 0, idm_135 = 0;
MyGlcm.feature_computer(dst_135, asm_135, con_135, ent_135, idm_135);
float glcm_data[16] = {
asm_0, asm_45, asm_90, asm_135,
con_0, con_45, con_90, con_135,
ent_0, ent_45, ent_90, ent_135,
idm_0, idm_45, idm_90, idm_135
};
std::cout << "特征数据:" << endl;
for (size_t j = 0; j < 16; j++)
{
testData[j] = glcm_data[j];
std::cout << testData[j] << " ";
}
std::cout << endl;
}//特征数据提取完毕
Mat result;
Mat query(1, 16, CV_32FC1, testData);
svm->predict(query, result);
std::cout << "分类结果为:" << endl;
std::cout << result << endl;
int a = result.at<Point2f>(0, 0).x;
std::cout << a << endl;
模块化检测
#include <opencv2/opencv.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>
#include "Water_Cut.h"
#include "Feature.h"
using namespace cv;
using namespace std;
using namespace cv::ml;
Feature feature_class;
int main()
{
// 训练——训练时候关闭测试
#if 0
// 遍历图片——为了循环提取特征用
string train_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\训练";
string train_img_namehead = "test";
string train_img_type = "bmp";
//size_t train_img_num = 4;
string img_name = train_img_dir + "\\" + "*." + train_img_type; //cout << img_name << endl;
int train_img_num = feature_class.read_images_in_folder(img_name);
cout << "训练图个数:" << train_img_num << endl;
// 训练用的输入和标签
Mat trainmat;
trainmat = cv::Mat::zeros(train_img_num, 32, CV_32FC1);
Mat labelmat;
labelmat = cv::Mat::zeros(train_img_num, 1, CV_32SC1);
// 遍历图并提取特征
vector<Mat> train_img = feature_class.data_search(train_img_dir, train_img_namehead, train_img_type, train_img_num);
for (size_t i = 0; i < train_img_num; i++)
{
resize(train_img[i], train_img[i], Size(train_img[i].cols / 2, train_img[i].rows / 2));
namedWindow("vetmat", 0);
imshow("vetmat", train_img[i]);//train_img[i].clone();
waitKey(0);
// 图像分割
Mat src = train_img[i].clone();
Mat dst = Mat::zeros(train_img[i].size(), CV_8UC3);
Mat edge = Mat::zeros(train_img[i].size(), CV_8UC3);
Water_Cut(src, dst, edge);
// 图像特征_HU
Mat hu_dst = dst.clone();
double Hu[7] = { 0 };
feature_class.feature_hu(hu_dst, Hu);
// 图像特征_COLOR
Mat color_dst = dst.clone();
float Mom[9] = { 0 };
feature_class.feature_color(color_dst, Mom);
// 图像特征_GLCM
Mat glcm_dst = dst.clone();
cv::cvtColor(glcm_dst, glcm_dst, CV_RGB2GRAY);
float glcm_data[16] = { 0 };
feature_class.feature_glcm(glcm_dst, glcm_data);
float train_data[32] = { 0 };
for (size_t j = 0; j < 7; j++)
{
train_data[j] = (float)Hu[j];
}
for (size_t j = 0; j < 9; j++)
{
train_data[7 + j] = (float)Mom[j];
}
for (size_t j = 0; j < 16; j++)
{
train_data[16 + j] = (float)glcm_data[j];
}
vector<float> traindata; // 特征值——一类(一张图)的特征
for (size_t k = 0; k < 32; k++)
{
traindata.push_back(train_data[k]);
}
std::cout << "traindata size:";
std::cout << traindata.size() << endl;
for (size_t j = 0; j < traindata.size(); j++)
{
trainmat.at<float>(i, j) = traindata[j];
}
labelmat.at<int>(i, 0) = i + 1; //每张一类
}
// 训练的初始化
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
std::cout << "开始训练" << endl;
svm->train(trainmat, ROW_SAMPLE, labelmat);
std::cout << "开始结束" << endl;
svm->save("svm.xml");
#endif
// 测试——测试时候关闭训练
#if 1
// 遍历测试文件
// 遍历图片——为了循环提取特征用
//string test_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\测试\\方格1号";
//string test_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\测试\\花纹2号";
//string test_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\测试\\空纹理3号";
//string test_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\测试\\条纹4号";
string test_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\测试\\";
string test_img_namehead = "test";
string test_img_type = "bmp";
string img_name = test_img_dir + "\\" + "*." + test_img_type; //cout << img_name << endl;
int test_img_num = feature_class.read_images_in_folder(img_name);
std::cout << "测试图个数:" << test_img_num << endl;
// 训练用的输入和标签
Mat testmat;
testmat = cv::Mat::zeros(test_img_num, 32, CV_32F);
// 遍历图并提取特征
vector<Mat> test_img = feature_class.data_search(test_img_dir, test_img_namehead, test_img_type, test_img_num);
for (size_t i = 0; i < test_img_num; i++)
{
resize(test_img[i], test_img[i], Size(test_img[i].cols / 2, test_img[i].rows / 2));
cv::namedWindow("vetmat", 0);
cv::imshow("vetmat", test_img[i]);//test_img[i].clone();
// 图像分割
Mat src = test_img[i].clone();
Mat dst = Mat::zeros(test_img[i].size(), CV_8UC3);
Mat edge = Mat::zeros(test_img[i].size(), CV_8UC3);
Water_Cut(src, dst, edge);
// 图像特征_HU
Mat hu_dst = dst.clone();
double Hu[7] = { 0 };
feature_class.feature_hu(hu_dst, Hu);
// 图像特征_COLOR
Mat color_dst = dst.clone();
float Mom[9] = { 0 };
feature_class.feature_color(color_dst, Mom);
// 图像特征_GLCM
Mat glcm_dst = dst.clone();
cv::cvtColor(glcm_dst, glcm_dst, CV_RGB2GRAY);
float glcm_data[16] = { 0 };
feature_class.feature_glcm(glcm_dst, glcm_data);
cv::waitKey();
float test_data[32] = { 0 };
for (size_t j = 0; j < 7; j++)
{
test_data[j] = (float)Hu[j];
}
for (size_t j = 0; j < 9; j++)
{
test_data[7 + j] = (float)Mom[j];
}
for (size_t j = 0; j < 16; j++)
{
test_data[16 + j] = (float)glcm_data[j];
}
vector<float> testdata; // 特征值——一类(一张图)的特征
for (size_t k = 0; k < 32; k++)
{
testdata.push_back(test_data[k]);
}
std::cout << "testdata size:";
std::cout << testdata.size() << endl;
for (size_t j = 0; j < testdata.size(); j++)
{
testmat.at<float>(i, j) = testdata[j];
}
}
Ptr<SVM> svmtest = Algorithm::load<SVM>("svm.xml"); // SVM::load()是一个静态函数,不能单独用
Mat result;
float temp = svmtest->predict(testmat, result);
std::cout << "分类结果" << endl;
std::cout << result << endl;
for (size_t i = 0; i < test_img_num; i++)
{
int a = result.at<Point2f>(i, 0).x;
std::cout << "最终分类为:" << "第" << a << "号瓷砖" << endl;
}
#endif
#if 0
// 模块化
string test_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\测试\\方格1号";
//string test_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\测试\\";
string test_img_namehead = "test";
string test_img_type = "bmp";
string img_name = test_img_dir + "\\" + "*." + test_img_type; //cout << img_name << endl;
int test_img_num = feature_class.read_images_in_folder(img_name);
std::cout << "测试图个数:" << test_img_num << endl;
// 训练用的输入和标签
Mat testmat;
testmat = cv::Mat::zeros(test_img_num, 32, CV_32F);
// 遍历图并提取特征
vector<Mat> test_img = data_search(test_img_dir, test_img_namehead, test_img_type, test_img_num);
for (size_t i = 0; i < test_img_num; i++)
{
resize(test_img[i], test_img[i], Size(test_img[i].cols / 2, test_img[i].rows / 2));
cv::namedWindow("vetmat", 0);
cv::imshow("vetmat", test_img[i]);//test_img[i].clone();
// 图像分割
Mat src = test_img[i].clone();
Mat dst = Mat::zeros(srcImage.size(), CV_8UC3);
Mat edge = Mat::zeros(srcImage.size(), CV_8UC3);
Water_Cut(src, dst, edge);
cv::waitKey();
// 图像特征_HU
Mat hu_dst = dst.clone();
double Hu[7] = { 0 };
feature_class.feature_hu(hu_dst, Hu);
// 图像特征_COLOR
Mat color_dst = dst.clone();
float Mom[9] = { 0 };
feature_class.feature_color(color_dst, Mom);
// 图像特征_GLCM
Mat glcm_dst = dst.clone();
cv::cvtColor(glcm_dst, glcm_dst, CV_RGB2GRAY);
float glcm_data[16] = { 0 };
feature_class.feature_glcm(glcm_dst, glcm_data);
// 特征集合test_data——》testdata——》testmat
float test_data[32] = { 0 };
for (size_t j = 0; j < 7; j++)
{
test_data[j] = (float)Hu[j];
}
for (size_t j = 0; j < 9; j++)
{
test_data[7 + j] = (float)Mom[j];
}
for (size_t j = 0; j < 16; j++)
{
test_data[16 + j] = (float)glcm_data[j];
}
vector<float> testdata; // 特征值——一类(一张图)的特征
for (size_t k = 0; k < 32; k++)
{
testdata.push_back(test_data[k]);
}
std::cout << "testdata size:";
std::cout << testdata.size() << endl;
for (size_t j = 0; j < testdata.size(); j++)
{
testmat.at<float>(i, j) = testdata[j];
}
}
Ptr<SVM> svmtest = Algorithm::load<SVM>("svm.xml"); // SVM::load()是一个静态函数,不能单独用
Mat result;
float temp = svmtest->predict(testmat, result);
std::cout << "分类结果" << endl;
std::cout << result << endl;
for (size_t i = 0; i < test_img_num; i++)
{
int a = result.at<Point2f>(i, 0).x;
std::cout << "最终分类为:" << "第" << a << "号瓷砖" << endl;
}
#endif
system("pause");
return 0;
}
子程序
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include "time.h"
using namespace cv;
using namespace std;
using namespace cv::ml;
class Feature
{
public:
/*
第一步:建立类
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include "time.h"
using namespace cv;
using namespace std;
第二步:包含类
Feature feature_class;
第三步:
集合颜色+形状+纹理
// 图像特征_HU
Mat hu_dst = dst.clone();
double Hu[7] = { 0 };
feature_class.feature_hu(hu_dst, Hu);
// 图像特征_COLOR
Mat color_dst = dst.clone();
float Mom[9] = { 0 };
feature_class.feature_color(color_dst, Mom);
// 图像特征_GLCM
Mat glcm_dst = dst.clone();
cv::cvtColor(glcm_dst, glcm_dst, CV_RGB2GRAY);
float glcm_data[16] = { 0 };
feature_class.feature_glcm(glcm_dst, glcm_data);
第四步:
// 特征集合7+9+16
float test_data[32] = { 0 };
for (size_t j = 0; j < 7; j++)
{
test_data[j] = (float)Hu[j];
}
for (size_t j = 0; j < 9; j++)
{
test_data[7 + j] = (float)Mom[j];
}
for (size_t j = 0; j < 16; j++)
{
test_data[16 + j] = (float)glcm_data[j];
}
*/
/* 【颜色】 */
// 颜色 计算三阶矩
double calc3orderMom(Mat &channel) //计算三阶矩
{
uchar *p;
double mom = 0;
double m = mean(channel)[0]; //计算单通道图像的均值
int nRows = channel.rows;
int nCols = channel.cols;
if (channel.isContinuous()) //连续存储有助于提升图像扫描速度
{
nCols *= nRows;
nRows = 1;
}
for (int i = 0; i < nRows; i++) //计算立方和
{
p = channel.ptr<uchar>(i);
for (int j = 0; j < nCols; j++)
mom += pow((p[j] - m), 3);
}
float temp;
temp = cvCbrt((float)(mom / (nRows*nCols))); //求均值的立方根
mom = (double)temp;
return mom;
}
// 颜色 计算9个颜色矩:3个通道的1、2、3阶矩
double *colorMom(Mat &img)
{
double *Mom = new double[9]; //存放9个颜色矩
if (img.channels() != 3)
std::cout << "Error,input image must be a color image" << endl;
Mat b(img.rows, img.cols, CV_8U);
Mat r(img.rows, img.cols, CV_8U);
Mat g(img.rows, img.cols, CV_8U);
Mat channels[] = { b, g, r };
split(img, channels);
//cv::imshow("r", channels[0]);
//cv::imshow("g", channels[1]);
//cv::imshow("b", channels[2]);
//waitKey(0);
Mat tmp_m, tmp_sd;
//计算b通道的颜色矩
meanStdDev(b, tmp_m, tmp_sd);
Mom[0] = tmp_m.at<double>(0, 0);
Mom[3] = tmp_sd.at<double>(0, 0);
Mom[6] = calc3orderMom(b);
// cout << Mom[0] << " " << Mom[1] << " " << Mom[2] << " " << endl;
//计算g通道的颜色矩
meanStdDev(g, tmp_m, tmp_sd);
Mom[1] = tmp_m.at<double>(0, 0);
Mom[4] = tmp_sd.at<double>(0, 0);
Mom[7] = calc3orderMom(g);
// cout << Mom[3] << " " << Mom[4] << " " << Mom[5] << " " << endl;
//计算r通道的颜色矩
meanStdDev(r, tmp_m, tmp_sd);
Mom[2] = tmp_m.at<double>(0, 0);
Mom[5] = tmp_sd.at<double>(0, 0);
Mom[8] = calc3orderMom(r);
// cout << Mom[6] << " " << Mom[7] << " " << Mom[8] << " " << endl;
return Mom;//返回颜色矩数组
}
// 颜色
bool feature_color(Mat src, float Mom[9])
{
if (src.channels() == 3)
{
// 图像特征_COLOR
Mat color_dst = src.clone();
cv::cvtColor(color_dst, color_dst, CV_RGB2HSV);
double *MOM;
MOM = colorMom(color_dst);
for (int i = 0; i < 9; i++)
{
std::cout << (float)MOM[i] << endl;
Mom[i] = (float)MOM[i];
}
return true;
}
else
{
std::cout << "channels!=3";
return false;
}
}
/* 【形状】 */
bool feature_hu(Mat src, double Hu[7])
{
if (src.channels() == 3)
{
// 图像特征_HU
Mat hu_dst = src.clone();
cv::cvtColor(hu_dst, hu_dst, CV_RGB2GRAY);
Canny(hu_dst, hu_dst, 0, 120);
//double Hu[7]; //存储得到的Hu矩阵
Moments mo = moments(hu_dst);//矩变量
HuMoments(mo, Hu);
for (int i = 0; i < 7; i++)
{
std::cout << (float)Hu[i] << endl;
}
return true;
}
else if ((src.channels() == 1))
{
Mat hu_dst = src.clone();
Canny(hu_dst, hu_dst, 0, 120);
//double Hu[7]; //存储得到的Hu矩阵
Moments mo = moments(hu_dst);//矩变量
HuMoments(mo, Hu);
for (int i = 0; i < 7; i++)
{
std::cout << (float)Hu[i] << endl;
}
return true;
}
else
{
return false;
}
}
// 纹理
const int gray_level = 16;//纹理区域块的大小,通常将图像划分成若干个纹理块计算
vector<double> glamvalue;//全局变量
//【】第一步:j计算共生矩阵
void getglcm_0(Mat& input, Mat& dst)//0度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)//寻找像素灰度最大值
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;//像素灰度最大值加1即为该矩阵所拥有的灰度级数
if (max_gray_level > 16)//若灰度级数大于16,则将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
else//若灰度级数小于16,则生成相应的灰度共生矩阵
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
void getglcm_45(Mat& input, Mat& dst)//45度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;
if (max_gray_level > 16)
{
for (int i = 0; i < height; i++)//将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
else
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width - 1; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j + 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
void getglcm_90(Mat& input, Mat& dst)//90度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;
if (max_gray_level > 16)
{
for (int i = 0; i < height; i++)//将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j];
dst.ptr<int>(rows)[cols]++;
}
}
}
else
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 0; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
void getglcm_135(Mat& input, Mat& dst)//135度灰度共生矩阵
{
Mat src = input;
CV_Assert(1 == src.channels());
src.convertTo(src, CV_32S);
int height = src.rows;
int width = src.cols;
int max_gray_level = 0;
for (int j = 0; j < height; j++)
{
int* srcdata = src.ptr<int>(j);
for (int i = 0; i < width; i++)
{
if (srcdata[i] > max_gray_level)
{
max_gray_level = srcdata[i];
}
}
}
max_gray_level++;
if (max_gray_level > 16)
{
for (int i = 0; i < height; i++)//将图像的灰度级缩小至16级,减小灰度共生矩阵的大小。
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
srcdata[j] = (int)srcdata[j] / gray_level;
}
}
dst.create(gray_level, gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 1; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j - 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
else
{
dst.create(max_gray_level, max_gray_level, CV_32SC1);
dst = Scalar::all(0);
for (int i = 0; i < height - 1; i++)
{
int*srcdata = src.ptr<int>(i);
int*srcdata1 = src.ptr<int>(i + 1);
for (int j = 1; j < width; j++)
{
int rows = srcdata[j];
int cols = srcdata1[j - 1];
dst.ptr<int>(rows)[cols]++;
}
}
}
}
// 【】第二步:计算纹理特征 // 特征值计算—— double& Asm, double& Con, double& Ent, double& Idm
void feature_computer(Mat&src, float& Asm, float& Con, float& Ent, float& Idm)//计算特征值
{
int height = src.rows;
int width = src.cols;
int total = 0;
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
for (int j = 0; j < width; j++)
{
total += srcdata[j];//求图像所有像素的灰度值的和
}
}
Mat copy;
copy.create(height, width, CV_64FC1);
for (int i = 0; i < height; i++)
{
int*srcdata = src.ptr<int>(i);
float*copydata = copy.ptr<float>(i);
for (int j = 0; j < width; j++)
{
copydata[j] = (float)srcdata[j] / (float)total;//图像每一个像素的的值除以像素总和
}
}
for (int i = 0; i < height; i++)
{
float*srcdata = copy.ptr<float>(i);
for (int j = 0; j < width; j++)
{
Asm += srcdata[j] * srcdata[j]; //能量
if (srcdata[j]>0)
{
Ent -= srcdata[j] * log(srcdata[j]); //熵
}
Con += (float)(i - j)*(float)(i - j)*srcdata[j]; //对比度
Idm += srcdata[j] / (1 + (float)(i - j)*(float)(i - j)); //逆差矩
}
}
}
// 【】融合第一、二步
/*
Mat src_gray;
float data[16] = {0};
*/
void feature_glcm(Mat src_gray, float data[16])
{
Mat dst_0, dst_90, dst_45, dst_135;
getglcm_0(src_gray, dst_0);
float asm_0 = 0, con_0 = 0, ent_0 = 0, idm_0 = 0;
feature_computer(dst_0, asm_0, con_0, ent_0, idm_0);
getglcm_45(src_gray, dst_45);
float asm_45 = 0, con_45 = 0, ent_45 = 0, idm_45 = 0;
feature_computer(dst_45, asm_45, con_45, ent_45, idm_45);
getglcm_90(src_gray, dst_90);
float asm_90 = 0, con_90 = 0, ent_90 = 0, idm_90 = 0;
feature_computer(dst_90, asm_90, con_90, ent_90, idm_90);
getglcm_135(src_gray, dst_135);
float asm_135 = 0, con_135 = 0, ent_135 = 0, idm_135 = 0;
feature_computer(dst_135, asm_135, con_135, ent_135, idm_135);
float AMS[4] = { asm_0, asm_45, asm_90, asm_135 };
float COM[4] = { con_0, con_45, con_90, con_135 };
float ENT[4] = { ent_0, ent_45, ent_90, ent_135 };
float IDM[4] = { idm_0, idm_45, idm_90, idm_135 };
float glcm_data[16] = {
asm_0, asm_45, asm_90, asm_135,
con_0, con_45, con_90, con_135,
ent_0, ent_45, ent_90, ent_135,
idm_0, idm_45, idm_90, idm_135
};
/*std::cout << "特征数据:" << endl;*/
for (size_t i = 0; i < 16; i++)
{
data[i] = glcm_data[i];
//std::cout << data[i] << " ";
}
}
// 读取当前文件夹图片的个数子程序
/*
cv::String pattern = "./save/*.bmp";
int cout = read_images_in_folder(pattern);
*/
size_t read_images_in_folder(cv::String pattern)//读取当前指定目录的图片的个数
{
vector<cv::String> fn;
glob(pattern, fn, false);//OpenCV自带一个函数glob()可以遍历文件
size_t count = fn.size(); //number of png files in images folder
return count;
}
// 【】文件检索
/*
string train_img_dir = "C:\\Users\\Administrator\\Desktop\\样品\\训练";
string train_img_namehead = "test";
string train_img_type = "bmp";
size_t train_img_num = 4;
vector<Mat> train_img = data_search(train_img_dir, train_img_namehead, train_img_type, train_img_num);
for (size_t i = 0; i < train_img_num; i++)
{
namedWindow("vetmat", 0);
imshow("vetmat", train_img[i]);
waitKey(0);
}
*/
vector<Mat> data_search(string &img_dir, string &img_namehead, string &img_type, size_t n)
{
float train_data[4][16] = { 0 };
vector<Mat> src;
for (int i = 0; i < n; i++)
{
string pos;
stringstream ss;
ss << i;
ss >> pos;
string img_name = img_dir + "\\" + img_namehead + pos + "." + img_type; //cout << img_name << endl;
Mat outsrc = imread(img_name);
src.push_back(outsrc);
}
return src;
}
private:
};
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/*遗留问题:两点的确立*/
void Water_Cut(InputArray& src, OutputArray& dst, OutputArray& edge)
{
Mat srcImage;
src.copyTo(srcImage);
//cv::resize(srcImage, srcImage, Size(srcImage.cols / 2, srcImage.rows / 2));
cv::namedWindow("resImage", 0);
cv::imshow("resImage", srcImage);
//waitKey();
// 【mask两点】
//mask的第一点 maskImage
Mat maskImage;
maskImage = Mat(srcImage.size(), CV_8UC1); // 掩模,在上面做标记,然后传给findContours
maskImage = Scalar::all(0);
Point point1(0, 0), point2(100, 10);
//line(maskImage, point1, point2, Scalar::all(255), 5, 8, 0);
circle(maskImage, point1, 10, Scalar::all(255), 100);
//mask的第二点 maskImage
Point point3(srcImage.cols / 2, srcImage.rows / 2), point4(srcImage.cols / 2+200, srcImage.rows / 2);
//line(maskImage, point3, point4, Scalar::all(255), 5, 8, 0);
circle(maskImage, point3, 10, Scalar::all(255), 100);
/*namedWindow("resImage", 0);
imshow("resImage", maskImage);
waitKey();*/
// 【轮廓】
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(maskImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
// 【分水岭】
// 参数二:maskWaterShed(CV_32S)
Mat maskWaterShed; // watershed()函数的参数
maskWaterShed = Mat(maskImage.size(), CV_32S);//空白掩码 maskWaterShed
maskWaterShed = Scalar::all(0);
/* 在maskWaterShed上绘制轮廓 */
for (int index = 0; index < contours.size(); index++)
drawContours(maskWaterShed, contours, index, Scalar::all(index + 1), -1, 8, hierarchy, INT_MAX);
/* 如果imshow这个maskWaterShed,我们会发现它是一片黑,原因是在上面我们只给它赋了1,2,3这样的值,通过代码80行的处理我们才能清楚的看出结果 */
// 参数一:srcImage(CV_8UC3)
watershed(srcImage, maskWaterShed); //int index = maskWaterShed.at<int>(row, col);操作
// 【随机生成几种颜色】
vector<Vec3b> colorTab;
for (int i = 0; i < contours.size(); i++)
{
int b = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int r = theRNG().uniform(0, 255);
colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
Mat dst_ = Mat::zeros(maskWaterShed.size(), CV_8UC3);
Mat dst_edge = Mat::zeros(maskWaterShed.size(), CV_8UC3);
int index = maskWaterShed.at<int>(maskWaterShed.rows / 2, maskWaterShed.cols / 2);
int index_temp = 0;
for (int i = 0; i < maskWaterShed.rows; i++)
{
for (int j = 0; j < maskWaterShed.cols; j++)
{
index_temp = maskWaterShed.at<int>(i, j);
//cout << index_temp << endl;
if (index_temp == index)//取中心的标签区域
{
dst_edge.at<Vec3b>(i, j) = Vec3b((uchar)255, (uchar)255, (uchar)255); //colorTab[index - 1];
dst_.at<Vec3b>(i, j) = srcImage.at<Vec3b>(i, j);
}
}
}
cv::namedWindow("分割结果", 0);
cv::imshow("分割结果", dst_);
imwrite("条纹1.bmp", dst_);
/*Mat dst_add;
addWeighted(dst_edge, 0.3, srcImage, 0.7, 0, dst_add);
namedWindow("加权结果", 0);
imshow("加权结果", dst_add);*/
cv::namedWindow("连通域", 0);
cv::imshow("连通域", dst_edge);
imwrite("条纹1_.bmp", dst_edge);
dst_.copyTo(dst);
dst_edge.copyTo(edge);
}