文章目录

思路方法

  1. 通过边缘检测 + 轮廓发现或者直线检测最大外接矩形实现
  2. 通过二值分割 + 形态学方法 + Hough直线 ,然后再执行相应的处理
  3. 通过二值分割 + 形态学方法 + 直线拟合,求交点获取轮廓,仿射变换。

一、图像旋转与切边

头文件 ​​image_feature_all.h​​:声明类与公共函数

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


class ImageFeature {
public:
void cut_edges(Mat& image);
void detect_line(Mat& image);

};

主函数​​main.cpp​​调用该类的公共成员函数

#include "image_feature_all.h"



int main(int argc, char** argv) {
const char* img_path = "D:\\Desktop\\match_dst.jpg";
Mat image = imread(img_path);
if (image.empty()) {
cout << "图像数据为空,读取文件失败!" << endl;
}
ImageFeature imgfeature;
imgfeature.cut_edges(image);
imgfeature.detect_line(image);

waitKey(0);
destroyAllWindows();
return 0;
}

效果演示

源文件 ​​feature_extract.cpp​​:实现类与公共函数

static void on_thresh(int thresh, void* userdata) {
Mat image = *((Mat*)userdata);
int img_width = image.cols;
int img_height = image.rows;
Mat gray_src, edges;
cvtColor(image, gray_src, COLOR_BGR2GRAY);
Canny(gray_src, edges, thresh, 2 * thresh);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(edges, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);

//获取最小包围框的宽高
float max_w = 0;
float max_h = 0;
double degree = 0;
for (size_t i = 0; i < contours.size(); i++) {
RotatedRect rect_min = cv::minAreaRect(contours[i]);
if (abs(rect_min.angle) > 0) {
max_h = rect_min.size.height > max_h ? rect_min.size.height : max_h;
max_w = rect_min.size.width > max_w ? rect_min.size.width : max_w;
}
}

//绘制最小包围框的轮廓,可视化
RNG rng(12345);
Mat drawImg = Mat::zeros(gray_src.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++){
RotatedRect rect_min = cv::minAreaRect(contours[i]);
if (rect_min.size.height == max_h && rect_min.size.width == max_w) {
degree = rect_min.angle;
Point2f pts[4];
rect_min.points(pts);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (size_t j = 0; j < 4; j++){
line(drawImg, pts[j], pts[(j + 1) % 4], color, 1, 8, 0);
}
}
}
cout << "[max_w,max_h,degree] = " << max_w<< " , " << max_h << " , " << degree << endl;
imshow("FindContours", drawImg);

// 旋正图像(同时旋转edges)
Mat dst;
Point2f center(image.rows / 2, image.cols / 2);
Mat rotate_mat = getRotationMatrix2D(center, -degree, 1);
warpAffine(image, dst, rotate_mat, image.size(), 1, 0, Scalar(255,255,255));
warpAffine(edges, edges, rotate_mat, image.size(), 1, 0);
imshow("dst", dst);
imshow("edges", edges);


// 旋转图像后切边(查找轮廓->筛选掉小框->切边)
vector<vector<Point>> contours_cut;
vector<Vec4i> hireachy_cut;
findContours(edges, contours_cut, hireachy_cut, RETR_TREE, CHAIN_APPROX_SIMPLE);

double minH = image.rows * 0.5;
double minW = image.cols * 0.5;
Mat goodImage = Mat::zeros(image.size(), CV_8UC3);
Rect box;
for (size_t i = 0; i < contours_cut.size(); i++){
RotatedRect min_rect = minAreaRect(contours_cut[i]);
if (min_rect.size.height > minH && min_rect.size.width ) {
Point2f pts[4];
min_rect.points(pts);
box = min_rect.boundingRect();
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (size_t j = 0; j < 4; j++){
line(goodImage, pts[j], pts[(j + 1) % 4], color, 1, 8, 0);
}
}
}
imshow("goodImage", goodImage);

if (box.width > 0 && box.height > 0 && box.width < img_width && box.height < img_height) {
Mat roiImg = dst(box);
imshow("roiImg", roiImg);
}
return;
}


void ImageFeature::cut_edges(Mat& image) {
namedWindow("FindContours", WINDOW_AUTOSIZE);
int thresh = 200;
int max_thresh = 400;
createTrackbar("thresh", "FindContours", &thresh, max_thresh, on_thresh, (void*)(&image));
on_thresh(thresh, &image);
}

OpenCV + CPP 系列(卌五)图像旋转、切边、直线检测_opencv


OpenCV + CPP 系列(卌五)图像旋转、切边、直线检测_i++_02


OpenCV + CPP 系列(卌五)图像旋转、切边、直线检测_i++_03


形状复杂使用形态学预处理:

二、直线检测

思路:通过图像形态学操作来寻找直线,(​​提取表格​​ 简单示例)霍夫获取位置信息与显示。

处理流程:

  1. 截取ROI区域
  2. 大津阈值二值化
  3. 形态学检测直线
  4. 腐蚀(加粗直线)
  5. 霍夫直线检测
  6. 可视化(筛选掉短直线)
void ImageFeature::detect_line(Mat& image) {
Mat roiImg, binaryImg, morphImg;
cvtColor(image, image, COLOR_BGR2GRAY);
Rect rect = Rect(10, 10, image.cols - 10, image.rows - 20);
roiImg = image(rect);
threshold(roiImg, binaryImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
imshow("binaryImg", binaryImg);


Mat kernel = cv::getStructuringElement(MORPH_RECT, Size(20, 1));
morphologyEx(binaryImg, morphImg, MORPH_OPEN, kernel);
kernel = cv::getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(morphImg, morphImg, kernel);
imshow("morphImg", morphImg);

vector<Vec4i> lines;
HoughLinesP(morphImg, lines, 1, CV_PI / 180.0, 30, 20.0, 0);
Mat resultImg = roiImg.clone();
cvtColor(resultImg, resultImg, COLOR_GRAY2BGR);
for (size_t i = 0; i < lines.size(); i++){
Vec4i ln = lines[i];
cout << "x1,y1, x2,y2 = " << ln[0] << " , " <<ln[1] << "\t" << ln[2] << " , " << ln[3] << endl;
if ((ln[2] - ln[0]) > 80 && (ln[3] - ln[1]) < 2) {
line(resultImg, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0);
}
}
imwrite("D:\\Desktop\\resultImg.png", resultImg);
imshow("resultImg", resultImg);
}

效果演示

OpenCV + CPP 系列(卌五)图像旋转、切边、直线检测_i++_04


关于检测直线并进行仿射变化