과제
아래와 같이 영수증을 모아 스캔한 이미지가 있고 영수증 영역을 각각 분리하고자 한다.
아이디어
1. 위에 4장은 세로로 길고 아래 한장은 옆으로 눕혀있다.
2. 위의 4장은 세로이므로 흑백 이미지로 변환한 후 세로로 길게 (3 x 40) 모펄러지 필터를 적용하자
3. 2의 필터로 문자들간에 거리가 40픽셀 이하는 하나로 묶여지게 되었고 큰 덩어리로 서로 분리 되었다.
큰 틀안에 독립된 덩어리들도 존재하지만 5번에서 처리.
4. findContours 실시하여 먼저 덩어리의 면적 순으로 내림정렬를 시킨다.
5. 작은 덩어리들은 큰 덩어리 내 혹은 겹치지 않는지 조사하여 합체 시키고, 외에 존재하는 덩어리들은 한 묶음으로 하기 위해 시작점과 끝점을 벡터에 등록한다.
6. 큰 덩어리는 빨간색으로 작은 덩어리들의 좌표들은 boundingRect 으로 바운더리를 구한 후 녹색으로 영역을 표시
흑백 이미지로 변환 ( 문자는 백, 배경은 흑)
cv::Mat img = cv::imread("d:\\image.jpg"); cv::Mat gray; cv::cvtColor(img, gray, CV_BGR2GRAY); cv::GaussianBlur(gray, gray, cv::Size(3, 3), 0.0); cv::Mat thresh; //cv::adaptiveThreshold(gray, thresh, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 11, 2); cv::threshold(gray, thresh, 0, 255, cv::THRESH_OTSU | cv::THRESH_BINARY_INV); |
모펄러지 필터 적용
cv::Mat kern = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 40 + 1)); cv::morphologyEx(thresh, thresh, cv::MORPH_DILATE, kern); |
findContours 실시 후 면적으로 내림 차순 정렬
std::vector< std::vector< cv::Point > > contours; std::vector< cv::Vec4i > hierarchy; cv::findContours(thresh, contours, hierarchy, cv::RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); int receiptMinSize = 80; std::vector receiptRects; std::vector blobs; sort(contours.begin(), contours.end(), [](const std::vector& c1, const std::vector& c2) { return cv::contourArea(c1, false) > cv::contourArea(c2, false); }); |
영수증의 최소 사이즈를 정의하고 큰 덩어리는 receiptRects 벡터에 넣고
작은 덩어리들은 기존 receiptRects 에 등록된 Rect와 곂치면 합치고 그 외에는 따로 blobs 벡터에 포이터를 등록
int receiptMinSize = 80; std::vector< cv::Rect > receiptRects; std::vector< cv::Point > blobs; for (size_t i = 0; i < contours.size(); i++) { cv::Rect rect = cv::boundingRect(contours[i]); if (std::min(rect.width, rect.height) > receiptMinSize) { receiptRects.push_back(rect); } else { bool isInside = false; for (cv::Rect& rt : receiptRects) { if ((rt & rect).area() > 0) { rt = rt | rect; isInside = true; break; } } if (isInside) continue; blobs.push_back(cv::Point(rect.x, rect.y)); blobs.push_back(cv::Point(rect.x + rect.width, rect.y + rect.height)); } } |
큰 덩어리들은 청색으로 작은 덩어리들은 하나로 합친 후 녹색으로 표시
for (cv::Rect& rt : receiptRects) { cv::rectangle(img, rt, cv::Scalar(255, 0, 0), 2); } cv::Rect rect = cv::boundingRect(blobs); cv::rectangle(img, rect, cv::Scalar(0, 255, 0), 2); cv::imwrite("d:\\result.png", img); return 0; |
'opencv' 카테고리의 다른 글
close contours (convexHull 사용) (0) | 2025.02.16 |
---|---|
자동차 번호판 추출하기 (1) | 2025.02.15 |
Color를 Grayscale 로 변환 (커스텀 비율) (0) | 2025.02.14 |
C# 와 C++/CLI Dll 사이의 이미지 전달 (0) | 2025.02.10 |
(opencv) 이미지위의 직선상의 픽셀위치 구하기 (0) | 2025.02.08 |