表格线识别通用库文档
载入中...
搜索中...
未找到
image.cpp
浏览该文件的文档.
1/*
2 * @Description: 图像类 实现
3 * @Version:
4 * @Autor: dreamy-xay
5 * @date: 2023-12-17
6 * @LastEditors: dreamy-xay
7 * @LastEditTime: 2024-05-28
8 */
9
11
12namespace cm {
13
21Image::Image() : cv::Mat() {}
22
32Image::Image(const cv::Mat& image) : cv::Mat(image) {}
33
45Image::Image(const Image& image) : cv::Mat(image){};
46
55
64cv::Size Image::Size() const {
65 return cv::Size(this->cols, this->rows);
66}
67
85Image& Image::Resize(size_t width, size_t height, int interpolation) {
86 // 如果宽高不变则不缩放
87 if (cols == width && rows == height)
88 return *this;
89
90 if (cols > width && rows > height) // 缩小
91 cv::resize(*this, *this, cv::Size(width, height), 0, 0, cv::INTER_AREA);
92 else // 不确定
93 cv::resize(*this, *this, cv::Size(width, height), 0, 0, interpolation);
94
95 return *this;
96}
97
114 if ((resize_type == RT_AUTO) || (resize_type == RT_ZOOM_IN && (rows < max_size && cols < max_size)) || (resize_type == RT_ZOOM_OUT && (rows > max_size || cols > max_size))) {
115 size_t width; // 新图像宽度
116 size_t height; // 新图像高度
117
118 if (rows > cols) { // 高大于宽
119 width = cols * max_size / rows;
120 height = max_size;
121 } else { // 其他情况
122 width = max_size;
123 height = rows * max_size / cols;
124 }
125
126 // resize
127 this->Resize(width, height);
128 }
129
130 return *this;
131}
132
146 return this->Resize(factor * cols, factor * rows, interpolation);
147}
148
165 Cm_Assert(this->channels() == 3, "this image is a three-channel image!!!");
166
167 if (this->empty())
168 return *this;
169
170 std::vector<cv::Mat> channels;
171 cv::split(*this, channels);
172
173 cv::Mat& image_red_channel = channels[2];
174 cv::Mat& image_green_channel = channels[1];
175 cv::Mat& image_blue_channel = channels[0];
176
177 // 删除红色印章
181
182 // 删除蓝色印章
186
187 // 删除印章残留的绿色
191
192 // 将三通道合并为一根通道并且返回给image
193 cv::merge(channels, *this); // 合并通道
194
195 return *this;
196}
197
212 Cm_Assert(this->channels() == 3, "this image is a three-channel image!!!");
213
214 if (this->empty())
215 return *this;
216
217 std::vector<cv::Mat> channels;
218
219 cv::split(*this, channels);
220
222
223 channels[0] = ~((~channels[0]) & (~channels[2]));
224 channels[1] = ~((~channels[1]) & (~channels[2]));
225
226 // 合并三个通道得到最终的图像
227 cv::merge(channels, *this);
228
229 return *this;
230}
231
246double Image::AdjustImageByGrayImage(cv::Mat& image, const cv::Mat& gray_image, uchar bg_gray_value, const std::vector<uchar>& thresholds) {
247 if (image.empty() || gray_image.empty())
248 return 0;
249
250 Cm_Assert(image.channels() == 1, "the image is a single channel image!!!");
251 Cm_Assert(gray_image.channels() == 1, "the grayscale image is a single channel image!!!");
252 Cm_Assert(image.rows == gray_image.rows && image.cols == gray_image.cols, "the image and the grayscale map must be the same size!!!");
253
254 int num_nonzero = 0;
255
257
258 cv::Mat compare_image(image.size(), image.type(), thresholds[0]);
259 cv::compare(image - gray_image, compare_image, result_image1, cv::CMP_GT);
260
262 cv::compare(image, compare_image, result_image2, cv::CMP_GE);
263
265 cv::compare(gray_image, compare_image, result_image3, cv::CMP_GE);
266
268
271
272 cv::multiply(image, result_image2, result_image2);
273 num_nonzero = cv::countNonZero(result_image2);
274
275 cv::multiply(image, 1 - result_image2, image);
277
278 // 计算平均灰度
279 if (num_nonzero != 0)
280 return (sum(result_image2)[0] / num_nonzero);
281 else
282 return cv::mean(image).val[0]; // 返回图像均值
283}
284
297 Cm_Assert(this->channels() == 1, "the image is a single channel image!!!");
298
299 if (this->empty())
300 return *this;
301
302 // 图像本身
303 auto& this_image = *this;
304
305 // 先归一化
306 this_image /= 255;
307
308 // 遍历所有行
309 for (size_t row = 0; row < rows; ++row) {
310 uchar* row_ptr = this->ptr<uchar>(row); // 二值图行指针(加速索引)
311
312 size_t cursor_start = 0; // 游程开始
313 size_t cursor_end = 0; // 游程结束
314
315 // 遍历每一行的所有像素
316 while (cursor_end < cols) {
317 // 如果当前像素为黑色
318 if (row_ptr[cursor_start] == 0) {
320
321 // 查找连续的非纯白色像素,计算游程长度
322 while (cursor_end < cols && row_ptr[cursor_end] != 1)
323 ++cursor_end;
324
325 // 如果游程长度小于等于阈值,则将这些黑色像素全部变成白色
327 while (cursor_start < cursor_end) {
329 ++cursor_start;
330 }
331
332 cursor_start = cursor_end; // 更新游程开始坐标
333 } else {
334 // 游程开始和结束自增
335 ++cursor_start;
336 ++cursor_end;
337 }
338 }
339 }
340
341 // 再重归一化状态恢复
342 this_image *= 255;
343
344 return this_image;
345}
346
360 Cm_Assert(this->channels() == 1, "the image is a single channel image!!!");
361
362 // 当图像为空时返回空列表
363 if (this->empty())
364 return {};
365
366 // 文本区域矩形轮廓列表
368
369 // 膨胀图像
370 cv::Mat dilate_img;
371
372 // Dilate to connect text together
373 if (dilate_size >= 1) {
374 cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(dilate_size, 1));
375 cv::dilate(*this, dilate_img, kernel);
376 } else
377 dilate_img = *this;
378
379 // Contours detect
380 std::vector<std::vector<cv::Point>> contours;
381 cv::findContours(dilate_img, contours, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
382
383 for (auto& contour : contours) {
384 // Obtain minimal bounding rectangle of contour
385 cv::Rect rect = boundingRect(contour);
386
387 double aspect_ratio = double(rect.width) / rect.height; // Ratio of width to height
388
390 text_areas.emplace_back(rect);
391 }
392
393 return std::move(text_areas);
394}
395
416Lines Image::GetLines(LineType line_type, const ImagePreprocessor& imp, int threshold, double min_line_length, double max_line_gap) const {
417 if (this->empty()) // 如果图像为空,则返回空
418 return {};
419
420 // 二值图
421 cv::Mat binary_image = std::move(imp.Preprocess(*this));
422
423 // 线列表
424 std::vector<cv::Vec4i> lines;
425
426 // 通过霍夫变换获取线列表
427 cv::HoughLinesP(binary_image, lines, 1, CV_PI / 360, threshold, min_line_length, max_line_gap);
428
429 // 过滤指定类型线并返回
430 return std::move(cm::Lines(lines).Filter([line_type](const cm::Line& line) { return line.Type() == line_type && !line.IsPoint(); }));
431}
432
453 if (this->empty() || lines.empty())
454 return *this;
455
456 Cm_Assert(this->channels() == 1, "the image is a single channel image!!!");
457
458 switch (line_type) {
459 case HLINE: {
460 // 断言查看所有线是否都是指定线类型
461 Cm_Assert(lines.Filter([line_type](const cm::Line& line) { return line.Type() == line_type; }).size() == lines.size(), Sprintf("The line type in the line list must all be \"%s\"!!!", line_type == HLINE ? "cm::HLINE" : "cm::VLINE"));
462
463 Interval row_range(0, rows);
465 cv::Point point;
466
467 for (auto& line : lines) {
468 cv::LineIterator line_iter(*this, line.pt1, line.pt2); // 迭代整条横线
469
470 for (int i = 0; i != line_iter.count; ++i, ++line_iter) {
471 point = line_iter.pos(); // 获取线当前点坐标
472
474
475 for (int y = iter_interval.start; y < iter_interval.end; ++y) // 横线上下两边像素也修改值
476 this->at<uchar>(y, point.x) = pixel_value;
477 }
478 }
479
480 break;
481 }
482 case VLINE: {
483 // 断言查看所有线是否都是指定线类型
484 Cm_Assert(lines.Filter([line_type](const cm::Line& line) { return line.Type() == line_type; }).size() == lines.size(), Sprintf("The line type in the line list must all be \"%s\"!!!", line_type == HLINE ? "cm::HLINE" : "cm::VLINE"));
485
486 Interval col_range(0, cols);
488 cv::Point point;
489
490 for (auto& line : lines) {
491 cv::LineIterator line_iter(*this, line.pt1, line.pt2); // 迭代整条竖线
492
493 for (int i = 0; i != line_iter.count; ++i, ++line_iter) {
494 point = line_iter.pos(); // 获取线当前点坐标
495
497
498 for (int x = iter_interval.start; x < iter_interval.end; ++x) // 竖线左右两边像素也修改值
499 this->at<uchar>(point.y, x) = pixel_value;
500 }
501 }
502
503 break;
504 }
505 case ULINE: {
506 // 横线列表和竖线列表
508
509 // 分类横竖线
511
512 // 横线列表和竖线列表分别修改值
515
516 break;
517 }
518 default:
519 throw Exception("The type(LineType) of param \"line_type\" is incorrect!");
520 }
521
522 return *this;
523}
524
537 Image result_image = this->clone();
538
539 if (this->empty())
540 return std::move(result_image);
541
542 cv::Mat binary_image;
543
544 // 获取灰度图
545 cv::cvtColor(result_image, binary_image, cv::COLOR_BGR2GRAY);
546
547 // 通过OTSU算法获取二值图
548 cv::threshold(binary_image, binary_image, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
549
550 // 找到所有白色块轮廓
551 std::vector<std::vector<cv::Point>> contours;
552 cv::findContours(binary_image, contours, cv::RETR_TREE, cv::CHAIN_APPROX_NONE);
553
554 // 在新的图像上绘制轮廓所组成的封闭区域
555 cv::Mat mask(result_image.size(), CV_8UC1, cv::Scalar(0, 0, 0));
556 int effective_area_count = 0;
557 for (int i = 0; i < contours.size(); ++i) {
558 // 计算轮廓面积
559 double area = cv::contourArea(contours[i]);
560 // 有较大白色块的区域
561 if (area >= min_area) {
563 cv::drawContours(mask, contours, i, cv::Scalar(255, 255, 255), cv::FILLED);
564 }
565 }
566
567 // 如果存在有效区域图像
569 // 原图像掩膜区域重置为黑底
570 result_image.setTo(cv::Scalar(0, 0, 0), mask);
571 // 将二值图像转换为三通道 RGB 彩色图像
572 cv::Mat rgb_image;
573 cv::cvtColor(binary_image, rgb_image, cv::COLOR_GRAY2RGB);
574 // 掩膜区域反转色
575 cv::bitwise_or(result_image, rgb_image, result_image, mask);
576 }
577
578 return std::move(result_image);
579}
580
593 Cm_Assert(this->channels() == 3, "the image must be an RGB three-channel image!!!");
594
595 // hsv图
596 cv::Mat hsv_image;
597
598 // bgr 转 hsv
599 cv::cvtColor(*this, hsv_image, CV_BGR2HSV);
600
601 cv::Mat result_image; // 结果图像
602 cv::Mat temp_image; // 临时图像
603
604 switch (type) {
605 case SCT_BLUE:
606 cv::inRange(hsv_image, cv::Scalar(100, 43, 46), cv::Scalar(124, 255, 255), result_image); // 阈值分割
607 break;
608 case SCT_RED:
609 cv::inRange(hsv_image, cv::Scalar(0, 43, 46), cv::Scalar(10, 255, 255), result_image); // 阈值分割
610 cv::inRange(hsv_image, cv::Scalar(156, 43, 46), cv::Scalar(180, 255, 255), temp_image);
612 break;
613 case SCT_BLACK:
614 cv::inRange(hsv_image, cv::Scalar(0, 0, 0), cv::Scalar(180, 255, 180), result_image); // 阈值分割
615 break;
616 default:
617 break;
618 }
619
620 return std::move(result_image);
621}
622
635 Cm_Assert(this->channels() == 1, "the image is a single channel image!!!");
636
637 Image histogram; // 直方图
638
639 if (this->empty())
640 return histogram;
641
642 cv::Mat this_image = *this; // 图像本身
643
644 int hist_size = 256; // 直方图的大小
645 float range[] = {0, 256};
646 const float* hist_range = {range};
647 // 计算灰度直方图
648 cv::calcHist(&this_image, 1, nullptr, cv::Mat(), histogram, 1, &hist_size, &hist_range);
649
650 return std::move(histogram);
651}
652
653} // namespace cm
通用异常类
Definition type.h:39
图像类
Definition image.h:38
Image SeparateColor(SeparatedColorType type) const
提取指定颜色像素图像
Definition image.cpp:592
Image & Scale(double factor, int interpolation=cv::INTER_LINEAR)
缩放图像
Definition image.cpp:145
Lines GetLines(LineType line_type, const ImagePreprocessor &imp, int threshold, double min_line_length, double max_line_gap=10) const
通过霍夫变换获取线
Definition image.cpp:416
Image InvertColorRegion(double min_area=7000) const
反转图像中的彩色块
Definition image.cpp:536
Image & ModifyLinesAreaValue(LineType line_type, const Lines &lines, int pixel_margin=2, uchar pixel_value=0)
修改图像中线所在区域的灰度值
Definition image.cpp:452
Image GetGrayHistogram() const
计算图像的灰度直方图
Definition image.cpp:634
Image & Resize(size_t height, size_t width, int interpolation=cv::INTER_LINEAR)
重新设置图像大小
Definition image.cpp:85
Rects GetTextAreaOutline(size_t dilate_size, double max_aspect_ratio=30) const
获取文本区域轮廓
Definition image.cpp:359
Image & RmRedBlueStamp(uchar bg_gray_value=255)
删除图像中的红色和蓝色印章
Definition image.cpp:164
Image & RmRedStamp(uchar bg_gray_value=255)
删除图像中的红色印章
Definition image.cpp:211
static double AdjustImageByGrayImage(cv::Mat &image, const cv::Mat &gray_image, uchar bg_gray_value, const std::vector< uchar > &thresholds)
通过灰度图像调整输入图像
Definition image.cpp:246
~Image()
图像类的析构函数
Definition image.cpp:54
Image()
图像类的默认构造函数
Definition image.cpp:21
cv::Size Size() const
获取图像的大小
Definition image.cpp:64
Image & HorizontalLengthSmooth(int threshold)
水平游程平滑
Definition image.cpp:296
图像预处理器类
virtual cv::Mat Preprocess(const cv::Mat &image) const
预处理图像
区间类
Definition interval.hpp:29
线类
Definition line.hpp:31
线列表类
Definition lines.hpp:34
const Lines & ClassifyLines(Lines &hlines, Lines &vlines) const
对线段进行分类
Definition lines.hpp:258
List< T > Filter(const FUNC &func) const
过滤列表项
Definition list.hpp:270
点类
Definition point.hpp:52
T y
点的 y 坐标
Definition point.hpp:57
T x
点的 x 坐标
Definition point.hpp:55
矩形列表类
Definition rects.hpp:28
#define Cm_Assert(expr, message)
断言宏
Definition macro.h:109
std::string Sprintf(const char *format,...)
格式化输出字符串
Definition string.hpp:47
LineType
线类型枚举
Definition enum.h:20
@ HLINE
横线 (horizontal line)
Definition enum.h:24
@ ULINE
未知线类型 (unknown line)
Definition enum.h:22
@ VLINE
竖线 (vertical line)
Definition enum.h:26
ResizeType
图片调整大小类型枚举
Definition enum.h:178
@ RT_ZOOM_IN
仅支持放大图片
Definition enum.h:182
@ RT_ZOOM_OUT
仅支持缩小图片
Definition enum.h:184
@ RT_AUTO
自动调整大小,支持放大和缩小图片
Definition enum.h:180
unsigned char uchar
无符号字符类型
Definition type.h:26
SeparatedColorType
支持分离的颜色类型枚举
Definition enum.h:164
@ SCT_BLACK
黑色
Definition enum.h:170
@ SCT_BLUE
蓝色
Definition enum.h:166
@ SCT_RED
红色
Definition enum.h:168