题目三 第7章,代码7-1,用11x11大小的LoG算子对以下图像进行边缘增强,并结合合适的二值化算法、形态学梯度算法以及联通区域标记算法,将图中车牌区域的字符分割出来。
图形
代码
//head.h
#include<string.h>
#include <stdio.h>
#include <cstdlib>
#include "imgcodecs.hpp"
#include<iostream>
#include<opencv.hpp>
#pragma comment(lib,"opencv_world455.lib")
//#pragma comment(lib, "opencv_world455.lib")
using namespace cv;
using namespace std;
#pragma once
//图像Log算子运算
Mat LoG_Image(const Mat & image,int kervalue =11, double sigma = 1.0f)
{
//首先对图像做高斯平滑
Mat matTemp;
GaussianBlur(image, matTemp, Size(kervalue, kervalue),\
sigma, sigma, BORDER_DEFAULT);
//通过拉斯普斯算子做边缘检测
Mat laplacian = Mat::zeros(image.rows, image.cols, CV_32FC1);
Laplacian(matTemp, laplacian, CV_32FC1, 3);
//求得最大边缘值
double dblMaxVal = 0;
minMaxLoc(laplacian, NULL, &dblMaxVal);
Mat dstImg;
convertScaleAbs(laplacian, dstImg);
imwrite("edge.bmp", dstImg);
imshow("dstImg", dstImg);
imshow("laplacian", laplacian);
Mat result = Mat::zeros(image.rows, image.cols, CV_8UC1);
int sum = 0;
result = image;
//过零点交叉,寻找像素边缘
for (int i = 1; i < result.rows-1; i++)
{
for (int j = 1; j < result.cols-1; j++)
{
//cout << dblMaxVal << endl;
if (laplacian.at<float>(i, j) < 0.1 * dblMaxVal)
{
continue;
}
//水平,垂直,45度方向,135度方向4个方向过零点判定
if (laplacian.at<float>(i - 1, j) \
* laplacian.at<float>(i + 1, j) < 0)
{
//cout << "enter1" << endl;
result.at<uchar>(i, j) = 0;
//sum++;
}
if (laplacian.at<float>(i, j + 1)\
* laplacian.at<float>(i, j - 1) < 0)
{
result.at<uchar>(i, j) = 0;
/* cout << "enter2" << endl;
sum++;*/
}
if (laplacian.at<float>(i + 1, j + 1)\
* laplacian.at<float>(i - 1, j - 1) < 0)
{
result.at<uchar>(i, j) = 0;
/* cout << "enter3" << endl;
sum++;*/
}
if (laplacian.at<float>(i - 1, j + 1) \
* laplacian.at<float>(i + 1, j - 1) < 0)
{
result.at<uchar>(i, j) = 0;
/*cout << "enter4" << endl;
sum++;*/
}
}
}
/*cout << "面积" << result.rows * result.cols << endl;
cout << "sum" << sum << endl;*/
imshow("LoG算子得出", result);
return result;
}
Mat LoG_Image1(const Mat& image, int kervalue = 11, double sigma = 1.0f)
{
//首先对图像做高斯平滑
Mat matTemp;
GaussianBlur(image, matTemp, Size(kervalue, kervalue), \
sigma, sigma, BORDER_DEFAULT);
//通过拉斯普斯算子做边缘检测
Mat laplacian = Mat::zeros(image.rows, image.cols, CV_32FC1);
Laplacian(matTemp, laplacian, CV_32FC1, 3);
//求得最大边缘值
double dblMaxVal = 0;
minMaxLoc(laplacian, NULL, &dblMaxVal);
Mat dstImg;
convertScaleAbs(laplacian, dstImg);
/*imwrite("edge.bmp", dstImg);
imshow("dstImg", dstImg);
imshow("laplacian", laplacian);*/
Mat result = Mat::zeros(image.rows, image.cols, CV_8UC1);
int sum = 0;
//过零点交叉,寻找像素边缘
for (int i = 1; i < result.rows - 1; i++)
{
for (int j = 1; j < result.cols - 1; j++)
{
//cout << dblMaxVal << endl;
if (laplacian.at<float>(i, j) < 0.1 * dblMaxVal)
{
continue;
}
//水平,垂直,45度方向,135度方向4个方向过零点判定
if (laplacian.at<float>(i - 1, j) \
* laplacian.at<float>(i + 1, j) < 0)
{
//cout << "enter1" << endl;
result.at<uchar>(i, j) = 255;
//sum++;
}
if (laplacian.at<float>(i, j + 1)\
* laplacian.at<float>(i, j - 1) < 0)
{
result.at<uchar>(i, j) = 255;
/* cout << "enter2" << endl;
sum++;*/
}
if (laplacian.at<float>(i + 1, j + 1)\
* laplacian.at<float>(i - 1, j - 1) < 0)
{
result.at<uchar>(i, j) = 255;
/* cout << "enter3" << endl;
sum++;*/
}
if (laplacian.at<float>(i - 1, j + 1) \
* laplacian.at<float>(i + 1, j - 1) < 0)
{
result.at<uchar>(i, j) = 255;
/*cout << "enter4" << endl;
sum++;*/
}
}
}
return result;
}
//代码7-6
//OTSU方法对256级灰度图像进行二值化
void Otsu(Mat& src,Mat &result)
{
//Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/rice.tif", IMREAD_GRAYSCALE);
long IPixCnt = src.rows * src.cols;//图像大小
long histogram[256] = { 0 };//histogram 是灰度直方图
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
unsigned char nCurVal = src.at<uchar>(i, j);//访问像素值点
//cout << (int)nCurVal << endl;
histogram[nCurVal]++;
}
}
int nThreshold = 0;
long sum0 = 0, sum1 = 0;//存储前景和背景的灰度总和
long cnt0 = 0, cnt1 = 0;//存储前景和背景的像素总个数
double w0 = 0, w1 = 0;//存储前景和背景所占整幅图像的比例
double u0 = 0, u1 = 0;//存储前景和背景的平均灰度
double variance = 0;//类间方差
double maxVariance = 0;//最大类间方差
for (int i = 0; i < 256; i++)
{
sum0 = 0, cnt0 = 0, w0 = 0;
sum1 = 0, cnt1 = 0, w1 = 0;
for (int j = 0; j < i; j++) {
cnt0 += histogram[j];//前景像素总和
sum0 += j * histogram[j];//前景灰度值总和
}
//前景部分平均灰度
u0 = cnt0 > 0 ? double(sum0) / cnt0 : 0;
w0 = (double)cnt0 / IPixCnt;//前景部分所占的比例
for (int j = i; j <= 255; j++)
{
cnt1 += histogram[j];//背景像素个数
sum1 += j * histogram[j];//背景灰度总和
}
//背景部分平均灰度
u1 = cnt1 > 0 ? double(sum1) / cnt1 : 0;
w1 = 1 - w0;//背景部分所占的比例
//分割阈值为i时的类间方差
variance = w0 * w1 * (u0 - u1) * (u0 - u1);
if (variance > maxVariance) {
maxVariance = variance;
nThreshold = i;
}
}
//遍历每个像素,对图像进行二值化
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC1);
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++) {
if (src.at<uchar>(i, j) > nThreshold)
{
dst.at<uchar>(i, j) = 255;
}
}
}
//imshow("二值化图像", dst);
result = dst;
//waitKey(0);
}
//代码7-7
//三角法二值化原理
void TriangleBinary(const Mat &src,Mat &result)
{
//Mat src = imread("defective_weld.tif", IMREAD_GRAYSCALE);
long lPixCnt = src.rows * src.cols;
long histogram[256] = { 0 };
//histogram是灰度直方图
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
unsigned char nCurval = src.at<uchar>(i, j);
histogram[nCurval]++;
}
}
//左右边界
int left_bound = 0, right_bound = 0;
//直方图最高峰和相应的灰度值
int max_ind = 0, maxPeak = 0;
int temp;
bool isflipped = false;
//找到最左边零的位置
for (int i = 0; i < 256; i++)
{
if (histogram[i] > 0) {
left_bound = i;
break;
}
}
//位置再移动一个步长,节日最左侧零的位置
if (left_bound > 0)
{
left_bound--;
}
//找到最右边零的位置
for (int i = 255; i>0; i--)
{
if (histogram[i] > 0) {
right_bound = i;
break;
}
}
//位置再移动一个步长,节日最右侧零的位置
if (right_bound <255)
{
right_bound++;
}
//在直方图上寻找最亮的点Hmax
for (int i = 0; i < 256; i++)
{
if (histogram[i] > maxPeak)
{
maxPeak = histogram[i];
max_ind = i;
}
}
//如果最大值落在靠左侧这样就无法满足三角法求阈值
//所以要检测最大值是否靠近左侧
//如果靠近左侧要通过翻转到右侧位置
if (max_ind - left_bound < right_bound - max_ind)
{
isflipped = true;
int i = 0;
int j = 255;
//左右交换
while (i < j) {
temp = histogram[i];
histogram[i] = histogram[j];
histogram[j] = temp;
i++;
j--;
}
}
//计算求得阈值
double thresh = left_bound;
double maxDist = 0, tempDist;
double peakIdxBound = left_bound - max_ind;
for (int i = left_bound + 1; i < max_ind; i++)
{
//计算距离
tempDist = maxPeak * i + peakIdxBound * histogram[i];
if (tempDist < tempDist)
{
maxDist = tempDist;
thresh = i;
}
}
thresh--;
if (isflipped)
{
thresh = 255 - thresh;
}
//遍历每一个像素,对图像进行二值化
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC1);
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
if (src.at<uchar>(i, j) > thresh)
{
dst.at<uchar>(i, j) = 255;
}
}
}imshow("二值化", dst);
result = dst;
}
//代码7-8全局阈值化函数threshold的用法
void GLobalBinbary()
{
Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg", \
IMREAD_GRAYSCALE);
Mat binImg;
//采用OTSU方法计算阈值,前景设置为白色
threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
imshow("OTSU二值化", binImg);
//采用三角法计算阈值,前景设置为黑色
threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_TRIANGLE);
imshow("三角法二值化", binImg);
waitKey(0);
}
//代码7-9 局部自适应二值化实例
void AdaptiveBinary(const Mat &src,Mat &result)
{
//Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg",\
IMREAD_GRAYSCALE);
Mat binImg;
//采用OTSU方法计算阈值,前景设置为白色
threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
//imshow("OTSU二值化", binImg);
adaptiveThreshold(src, binImg, 255, ADAPTIVE_THRESH_GAUSSIAN_C, \
THRESH_BINARY_INV, 7, 10);
result = binImg;
//imshow("局部自适应二值化结果", binImg);
}
void AdaptiveBinary_otsu(const Mat& src, Mat& result)
{
//Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg",\
IMREAD_GRAYSCALE);
Mat binImg;
//采用OTSU方法计算阈值,前景设置为白色
threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
//imshow("OTSU二值化", binImg);
/*adaptiveThreshold(src, binImg, 255, ADAPTIVE_THRESH_GAUSSIAN_C, \
THRESH_BINARY_INV, 7, 10);
imshow("局部自适应二值化结果", binImg);*/
result = binImg;
}
//图像放大
void fun1(cv::Mat& src, double kx, double ky, Mat& result)
{
int row = src.rows * kx;
int col = src.cols * ky;
cv::Mat dst(row, col, src.type());
for (int i = 0; i < row; i++)
{
int srx = i / kx;
for (int j = 0; j < col; j++)
{
int sry = j / ky;
dst.at<cv::Vec3b>(i, j) = src.at<cv::Vec3b>(srx, sry);
}
}
result = dst;
}
#include"head.h"
void imgContoursROI()//轮廓roi
{
//1读取图像
Mat src1, src, dst, img_gaussian;
//rgb图像
src1 = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg");
//灰度图像
src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg", \
IMREAD_GRAYSCALE);
//复制一个图像
Mat copyImg = src1.clone();
imshow("src", src);
// 图像预处理
//图片降噪
//GaussianBlur( InputArray src, OutputArray dst, Size ksize,
/*double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );*/
//2高斯平滑
Mat matTemp;
int kervalue = 11; double sigma = 1.0f;
GaussianBlur(src, matTemp, Size(kervalue, kervalue), \
sigma, sigma, BORDER_DEFAULT);
//imshow("Gaussian_Blur2", matTemp);
//3直方图均衡化,增加对比度
Mat out;
equalizeHist(matTemp, out);
/*namedWindow("经过直方图均化后处理");
imshow("经过直方图均化后处理", out);*/
//4二值化
Mat diff;
Otsu(out, diff);
//5画轮廓
//定义轮廓和层次结构
vector<vector<Point> >contours;
vector<Vec4i> hierarchy;
//查找轮廓
Mat result;
findContours(diff, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
//遍历所有的顶层的轮廓,绘制每个连接组件颜色
int index = 0;
for (; index >= 0; index = hierarchy[index][0])
{
//Scalar color(rand() & 255, rand() & 255, rand() & 255);
Scalar color(0, 0, 0);
if (contourArea(contours[index]) > 2000 && contourArea(contours[index]) < 2500) {
drawContours(src1, contours, index, color, FILLED, 8, hierarchy);
Rect rect = boundingRect(contours[index]);
//rectangle(src1, Rect(rect.x, rect.y, rect.width, rect.height), Scalar(0, 255, 0), 2); //将感兴趣区域框出来
imshow("src1", src1);
result = copyImg(Rect((rect.x + 5), (rect.y + 3), (rect.width - 10), (rect.height - 8)));
//result = copyImg(recta);
imshow("result", result);
imwrite("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", result);
fun1(result, 5, 5, result);
imshow("maxresult", result);
imwrite("E:/32/opencv/作业/实验3文件/实验3文件/maxlicense.jpg", result);
src = imread("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", IMREAD_GRAYSCALE);
//局部自适应二值化图像
AdaptiveBinary(src, result);
//imshow("src_bin", src_bin);
//
result = LoG_Image1(src);
}
}
}
void characterSegmentation()
{
Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/maxlicense.jpg", IMREAD_GRAYSCALE);
Mat src1 = imread("E:/32/opencv/作业/实验3文件/实验3文件/maxlicense.jpg");
//imshow("src", src);
//imshow("src1", src1);
Mat dst, src_otsu, src_bin, src_dilate, src_erode, src_LoG, src_label, src_bin2, src_close, src_extract;
//imshow("grayed image", src);
/*cout << "good";
extractImg(src, dst);*/
//局部自适应二值化图像
AdaptiveBinary_otsu(src, src_bin);
src_LoG = src_bin;
bitwise_not(src_bin, src_bin);
//imshow("反色之后", src_bin);
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
//---------【2】进行形态学梯度操作---------
morphologyEx(src_bin, src_bin, MORPH_GRADIENT, element);
//imshow("形态学", src_bin);
//
/*src_LoG = LoG_Image1(src);*/
//定义轮廓和层次结构
vector<vector<Point> >contours;
vector<Vec4i> hierarchy;
//查找轮廓
Mat result;
findContours(src_bin, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
//遍历所有的顶层的轮廓,随机颜色绘制每个连接组件颜色
int index = 0;
for (; index >= 0; index = hierarchy[index][0])
{
//Scalar color(rand() & 255, rand() & 255, rand() & 255);
Scalar color(0, 0, 0);
/*if ((contourArea(contours[index]) > 300 && contourArea(contours[index]) <10000) ) {*/
if ((contourArea(contours[index]) > 350 && contourArea(contours[index]) < 800) || \
(contourArea(contours[index]) > 1000 && contourArea(contours[index]) < 5000)) {
drawContours(src_bin, contours, index, color, FILLED, 8, hierarchy);
Rect rect = boundingRect(contours[index]);
rectangle(src1, Rect(rect.x, rect.y, rect.width, rect.height), Scalar(0, 255, 0), 2); //将感兴趣区域框出来
imshow("src1", src1);
result = src1(rect);
//result = copyImg(recta);
//imshow("result", result);
//imwrite("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", result);
//fun1(result, 5, 5, result);
//imshow("maxresult", result);
//src = imread("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", IMREAD_GRAYSCALE);
//局部自适应二值化图像
//AdaptiveBinary(src, result);
//imshow("src_bin", src_bin);
//
/*result = LoG_Image1(src);*/
cout << index << " ";
cout << contourArea(contours[index]) << endl;
imshow("number" + to_string(index), result);
imwrite("number" + to_string(index) + ".jpg", result);
}
}
}
void main()
{
//选择roi区域region of interest
imgContoursROI();
//字符分割
characterSegmentation();
waitKey(0);
}
结果
参考链接
-
roi选取
-
图像放大
-
字符分割