[C++ ๋ก OpenCV ๊ตฌํํ๊ธฐ] (10) Project2 - Document Scanner
<๋ณธ ๋ธ๋ก๊ทธ๋ Murtaza's Workshop ์ ์ ํ๋ธ๋ฅผ ์ฐธ๊ณ ํด์ ๊ณต๋ถํ๋ฉฐ ์์ฑํ์์ต๋๋ค :-)>
=> LEARN OPENCV C++ in 4 HOURS | Including 3x Projects | Computer Vision
๐ Project2 - Document Scanner
๐ง ๋ฌธ์ ์ด๋ฏธ์ง์ dilate ์ ์ฉ์ผ๋ก ์ค์บ ํ๊ธฐ
dilate : ํฐ์ ํฝ์ ์ ์ค์ด๋ ์ญํ
#include <opencv2/opencv.hpp> // OpenCV์์ ์ง์ํ๋ ๋ชจ๋ ๊ธฐ๋ฅ
#include <opencv2/videoio.hpp> // ๋น๋์ค ์ถ์ ๋ฐ ๋ฐฐ๊ฒฝ segmentation๊ณผ ๊ด๋ จ๋ ๋ฃจํด
#include <opencv2/imgcodecs.hpp> // ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์
์ด ์ ์ธ (Mat ์ด๋ Point๊ฐ ์ ์ธ, ํ๋ ฌ ์ฐ์ฐ ํน์ ๋ฒกํฐ ์ฐ์ฐ)
#include <opencv2/highgui.hpp> // ์๋์ฐ ํ๋ฉด, UI์ฒ๋ฆฌ(์ฌ๋ผ์ด๋, ๋ฒํผ ๋ฑ) ๋ฐ ๋ง์ฐ์ค ์ ์ด ๊ฐ๋ฅ
#include <opencv2/objdetect.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
// #10. Document Scanner
Mat imgOriginal, imgGray, imgCanny, imgThre, imgBlur, imgErode, imgDil;
Mat preProcessing(Mat img) {
// cvtColor( input Array, output Array, flag)
// : input Array๋ฅผ ์
๋ ฅ๋ฐ์ flag ์ ๋ํ ์ต์
์ผ๋ก ์ด๋ฏธ์ง ์์ฑ๋์ ๋ณ๊ฒฝ
cvtColor(img, imgGray, COLOR_BGR2GRAY);
// GaussianBlur( src, dst, kernel_size, sigma_x, sigma_y, borderType)
// : ์ค์๊ฐ์ ๊ฐ์ค์น๋ฅผ ๋ ์ฃผ๊ณ ์ฃผ๋ณ์ ๋ ํ๋ฆฌ๊ฒ
GaussianBlur(img, imgBlur, Size(3, 3), 3, 0);
// Canny( src, dst, threshold1, threshold2)
// : ๊ฒฝ๊ณ์ ๊ฒ์ถ
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
// Morphology
// For Erosion : erode( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ๋๋ฆฌ๋ ์ญํ
//erode(imgCanny, imgErode, kernel);
// For Dilation : dilate( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ์ค์ด๋ ์ญํ
dilate(imgCanny, imgDil, kernel);
return imgDil;
}
int main() {
string path = "Resources/paper.jpg";
imgOriginal = imread(path);
resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);
// Preprocessing
imgThre = preProcessing(imgOriginal);
imshow("image", imgOriginal);
imshow("image Dial", imgDil);
waitKey(0);
}
๐ง ๋ฌธ์์ ํ ๋๋ฆฌ์ ๊ผญ์ง์ ๊ฒ์ถํด์ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ธฐ
#include <opencv2/opencv.hpp> // OpenCV์์ ์ง์ํ๋ ๋ชจ๋ ๊ธฐ๋ฅ
#include <opencv2/videoio.hpp> // ๋น๋์ค ์ถ์ ๋ฐ ๋ฐฐ๊ฒฝ segmentation๊ณผ ๊ด๋ จ๋ ๋ฃจํด
#include <opencv2/imgcodecs.hpp> // ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์
์ด ์ ์ธ (Mat ์ด๋ Point๊ฐ ์ ์ธ, ํ๋ ฌ ์ฐ์ฐ ํน์ ๋ฒกํฐ ์ฐ์ฐ)
#include <opencv2/highgui.hpp> // ์๋์ฐ ํ๋ฉด, UI์ฒ๋ฆฌ(์ฌ๋ผ์ด๋, ๋ฒํผ ๋ฑ) ๋ฐ ๋ง์ฐ์ค ์ ์ด ๊ฐ๋ฅ
#include <opencv2/objdetect.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
// #10. Document Scanner
Mat imgOriginal, imgGray, imgCanny, imgThre, imgBlur, imgErode, imgDil;
vector<Point> initialPoints;
Mat preProcessing(Mat img) {
// cvtColor( input Array, output Array, flag)
// : input Array๋ฅผ ์
๋ ฅ๋ฐ์ flag ์ ๋ํ ์ต์
์ผ๋ก ์ด๋ฏธ์ง ์์ฑ๋์ ๋ณ๊ฒฝ
cvtColor(img, imgGray, COLOR_BGR2GRAY);
// GaussianBlur( src, dst, kernel_size, sigma_x, sigma_y, borderType)
// : ์ค์๊ฐ์ ๊ฐ์ค์น๋ฅผ ๋ ์ฃผ๊ณ ์ฃผ๋ณ์ ๋ ํ๋ฆฌ๊ฒ
GaussianBlur(img, imgBlur, Size(3, 3), 3, 0);
// Canny( src, dst, threshold1, threshold2)
// : ๊ฒฝ๊ณ์ ๊ฒ์ถ
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
// Morphology
// For Erosion : erode( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ๋๋ฆฌ๋ ์ญํ
//erode(imgCanny, imgErode, kernel);
// For Dilation : dilate( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ์ค์ด๋ ์ญํ
dilate(imgCanny, imgDil, kernel);
return imgDil;
}
vector<Point> getContours(Mat imgDil) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
// findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
// : ์ธ๊ณฝ์ ๊ฒ์ถ์ด๋ ๊ฐ์ฒด์ ์ธ๊ณฝ์ ์ขํ๋ฅผ ๋ชจ๋ ์ถ์ถํ๋ ์์
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point> biggest;
int maxArea = 0;
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
cout << area << endl;
string objectType;
if (area > 1000) {
float peri = arcLength(contours[i], true);
// approxPolyDP(์ค๊ณฝ์ , ๊ทผ์ฌ์น ์ ํ๋, ํ๊ณก์ )
// : ์ค๊ณฝ์ ๋ค์ ์ค๊ณฝ์ ๋ค๋ก ๊ทผ์ฌํด ๊ทผ์ฌ ๋ค๊ฐํ์ผ๋ก ๋ฐํ
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
if (area > maxArea && conPoly[i].size() == 4) {
drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 5);
biggest = { conPoly[i][0], conPoly[i][1], conPoly[i][2], conPoly[i][3] };
maxArea = area;
}
// drawContours(image, contours, contourIdx, color, thickness=None, lineType=No)
// : ๊ฒ์ถํ ์ธ๊ณฝ์ ์ ํ์ธํ๊ธฐ ์ํด ์ด ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ธ๊ณฝ์ ์ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ธฐ
//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 2);
//rectangle(imgOriginal, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
}
}
return biggest;
}
void drawPoints(vector<Point> points, Scalar color) {
for (int i = 0; i < points.size(); i++) {
circle(imgOriginal, points[i], 30, color, FILLED);
putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN, 2, color, 2);
}
}
int main() {
string path = "Resources/paper.jpg";
imgOriginal = imread(path);
resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);
// Preprocessing
imgThre = preProcessing(imgOriginal);
// Get contours - Biggest
initialPoints = getContours(imgThre);
drawPoints(initialPoints, Scalar(0, 0, 255));
// Warp
imshow("image", imgOriginal);
imshow("image Dial", imgDil);
waitKey(0);
}
๐ง ์ค์บ๋ ๋ฌธ์์ ๊ผญ์ง์ ์ ํ ์คํธ ์ถ๊ฐํ๊ธฐ
#include <opencv2/opencv.hpp> // OpenCV์์ ์ง์ํ๋ ๋ชจ๋ ๊ธฐ๋ฅ
#include <opencv2/videoio.hpp> // ๋น๋์ค ์ถ์ ๋ฐ ๋ฐฐ๊ฒฝ segmentation๊ณผ ๊ด๋ จ๋ ๋ฃจํด
#include <opencv2/imgcodecs.hpp> // ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์
์ด ์ ์ธ (Mat ์ด๋ Point๊ฐ ์ ์ธ, ํ๋ ฌ ์ฐ์ฐ ํน์ ๋ฒกํฐ ์ฐ์ฐ)
#include <opencv2/highgui.hpp> // ์๋์ฐ ํ๋ฉด, UI์ฒ๋ฆฌ(์ฌ๋ผ์ด๋, ๋ฒํผ ๋ฑ) ๋ฐ ๋ง์ฐ์ค ์ ์ด ๊ฐ๋ฅ
#include <opencv2/objdetect.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
// #10. Document Scanner
Mat imgOriginal, imgGray, imgCanny, imgThre, imgBlur, imgErode, imgDil;
vector<Point> initialPoints;
Mat preProcessing(Mat img) {
// cvtColor( input Array, output Array, flag)
// : input Array๋ฅผ ์
๋ ฅ๋ฐ์ flag ์ ๋ํ ์ต์
์ผ๋ก ์ด๋ฏธ์ง ์์ฑ๋์ ๋ณ๊ฒฝ
cvtColor(img, imgGray, COLOR_BGR2GRAY);
// GaussianBlur( src, dst, kernel_size, sigma_x, sigma_y, borderType)
// : ์ค์๊ฐ์ ๊ฐ์ค์น๋ฅผ ๋ ์ฃผ๊ณ ์ฃผ๋ณ์ ๋ ํ๋ฆฌ๊ฒ
GaussianBlur(img, imgBlur, Size(3, 3), 3, 0);
// Canny( src, dst, threshold1, threshold2)
// : ๊ฒฝ๊ณ์ ๊ฒ์ถ
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
// Morphology
// For Erosion : erode( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ๋๋ฆฌ๋ ์ญํ
//erode(imgCanny, imgErode, kernel);
// For Dilation : dilate( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ์ค์ด๋ ์ญํ
dilate(imgCanny, imgDil, kernel);
return imgDil;
}
vector<Point> getContours(Mat imgDil) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
// findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
// : ์ธ๊ณฝ์ ๊ฒ์ถ์ด๋ ๊ฐ์ฒด์ ์ธ๊ณฝ์ ์ขํ๋ฅผ ๋ชจ๋ ์ถ์ถํ๋ ์์
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point> biggest;
int maxArea = 0;
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
cout << area << endl;
string objectType;
if (area > 1000) {
float peri = arcLength(contours[i], true);
// approxPolyDP(์ค๊ณฝ์ , ๊ทผ์ฌ์น ์ ํ๋, ํ๊ณก์ )
// : ์ค๊ณฝ์ ๋ค์ ์ค๊ณฝ์ ๋ค๋ก ๊ทผ์ฌํด ๊ทผ์ฌ ๋ค๊ฐํ์ผ๋ก ๋ฐํ
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
if (area > maxArea && conPoly[i].size() == 4) {
drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 5);
biggest = { conPoly[i][0], conPoly[i][1], conPoly[i][2], conPoly[i][3] };
maxArea = area;
}
// drawContours(image, contours, contourIdx, color, thickness=None, lineType=No)
// : ๊ฒ์ถํ ์ธ๊ณฝ์ ์ ํ์ธํ๊ธฐ ์ํด ์ด ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ธ๊ณฝ์ ์ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ธฐ
//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 2);
//rectangle(imgOriginal, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
}
}
return biggest;
}
void drawPoints(vector<Point> points, Scalar color) {
for (int i = 0; i < points.size(); i++) {
circle(imgOriginal, points[i], 10, color, FILLED);
putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN, 4, color, 4);
}
}
int main() {
string path = "Resources/paper.jpg";
imgOriginal = imread(path);
resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);
// Preprocessing
imgThre = preProcessing(imgOriginal);
// Get contours - Biggest
initialPoints = getContours(imgThre);
drawPoints(initialPoints, Scalar(0, 0, 255));
// Warp
imshow("image", imgOriginal);
imshow("image Dial", imgDil);
waitKey(0);
}
๐ง proprecesing ์ ํตํด ๊ฐ์ฅ ํฐ ๊ฒ์ถ๊ฐ์ ํ ์คํธ ์ถ๊ฐํ๊ธฐ
#include <opencv2/opencv.hpp> // OpenCV์์ ์ง์ํ๋ ๋ชจ๋ ๊ธฐ๋ฅ
#include <opencv2/videoio.hpp> // ๋น๋์ค ์ถ์ ๋ฐ ๋ฐฐ๊ฒฝ segmentation๊ณผ ๊ด๋ จ๋ ๋ฃจํด
#include <opencv2/imgcodecs.hpp> // ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์
์ด ์ ์ธ (Mat ์ด๋ Point๊ฐ ์ ์ธ, ํ๋ ฌ ์ฐ์ฐ ํน์ ๋ฒกํฐ ์ฐ์ฐ)
#include <opencv2/highgui.hpp> // ์๋์ฐ ํ๋ฉด, UI์ฒ๋ฆฌ(์ฌ๋ผ์ด๋, ๋ฒํผ ๋ฑ) ๋ฐ ๋ง์ฐ์ค ์ ์ด ๊ฐ๋ฅ
#include <opencv2/objdetect.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
// #10. Document Scanner
Mat imgOriginal, imgGray, imgCanny, imgThre, imgBlur, imgErode, imgDil;
vector<Point> initialPoints, docPoints;
Mat preProcessing(Mat img) {
// cvtColor( input Array, output Array, flag)
// : input Array๋ฅผ ์
๋ ฅ๋ฐ์ flag ์ ๋ํ ์ต์
์ผ๋ก ์ด๋ฏธ์ง ์์ฑ๋์ ๋ณ๊ฒฝ
cvtColor(img, imgGray, COLOR_BGR2GRAY);
// GaussianBlur( src, dst, kernel_size, sigma_x, sigma_y, borderType)
// : ์ค์๊ฐ์ ๊ฐ์ค์น๋ฅผ ๋ ์ฃผ๊ณ ์ฃผ๋ณ์ ๋ ํ๋ฆฌ๊ฒ
GaussianBlur(img, imgBlur, Size(3, 3), 3, 0);
// Canny( src, dst, threshold1, threshold2)
// : ๊ฒฝ๊ณ์ ๊ฒ์ถ
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
// Morphology
// For Erosion : erode( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ๋๋ฆฌ๋ ์ญํ
//erode(imgCanny, imgErode, kernel);
// For Dilation : dilate( src, dst, kernel, anchor, iteration, borderType, borderValue)
// : ํฐ์ ํฝ์
์ ์ค์ด๋ ์ญํ
dilate(imgCanny, imgDil, kernel);
return imgDil;
}
vector<Point> getContours(Mat imgDil) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
// findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
// : ์ธ๊ณฝ์ ๊ฒ์ถ์ด๋ ๊ฐ์ฒด์ ์ธ๊ณฝ์ ์ขํ๋ฅผ ๋ชจ๋ ์ถ์ถํ๋ ์์
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point> biggest;
int maxArea = 0;
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
cout << area << endl;
string objectType;
if (area > 1000) {
float peri = arcLength(contours[i], true);
// approxPolyDP(์ค๊ณฝ์ , ๊ทผ์ฌ์น ์ ํ๋, ํ๊ณก์ )
// : ์ค๊ณฝ์ ๋ค์ ์ค๊ณฝ์ ๋ค๋ก ๊ทผ์ฌํด ๊ทผ์ฌ ๋ค๊ฐํ์ผ๋ก ๋ฐํ
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
if (area > maxArea && conPoly[i].size() == 4) {
drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 5);
biggest = { conPoly[i][0], conPoly[i][1], conPoly[i][2], conPoly[i][3] };
maxArea = area;
}
// drawContours(image, contours, contourIdx, color, thickness=None, lineType=No)
// : ๊ฒ์ถํ ์ธ๊ณฝ์ ์ ํ์ธํ๊ธฐ ์ํด ์ด ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ธ๊ณฝ์ ์ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ธฐ
//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 2);
//rectangle(imgOriginal, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
}
}
return biggest;
}
void drawPoints(vector<Point> points, Scalar color) {
for (int i = 0; i < points.size(); i++) {
circle(imgOriginal, points[i], 10, color, FILLED);
putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN, 4, color, 4);
}
}
vector<Point> reorder(vector<Point> points) {
vector<Point> newPoints;
vector<int> sumPoints, subPoints;
for (int i = 0; i < 4; i++) {
sumPoints.push_back(points[0].x + points[0].y);
subPoints.push_back(points[0].x - points[0].y);
}
newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); // 0
newPoints.push_back(points[max_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); // 1
newPoints.push_back(points[min_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); // 2
newPoints.push_back(points[max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); // 3
return newPoints;
}
int main() {
string path = "Resources/paper.jpg";
imgOriginal = imread(path);
resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);
// Preprocessing
imgThre = preProcessing(imgOriginal);
// Get contours - Biggest
initialPoints = getContours(imgThre);
drawPoints(initialPoints, Scalar(0, 0, 255));
docPoints = reorder(initialPoints);
drawPoints(docPoints, Scalar(0, 255, 0));
// Warp
imshow("image", imgOriginal);
imshow("image Dial", imgDil);
waitKey(0);
}
๐ง Perspective(์๊ทผ๋ฒ) ๋ณํ ์ผ๋ก ํจ๊ณผ ๋ด๊ธฐ
์ง์ ์ ์ฑ์ง๋ง ์ ์ง๊ฐ ๋๊ณ , ์ ์ ํํ์ฑ์ ์ ์ง๊ฐ ๋์ง ์๋ ๋ณํ
์๊ทผ๋ณํ์ ๊ฑฐ์น๋ฉด ํํ์ฑ์ ์ ์ง ๋์ง ๋ชปํ๊ณ ํ๋์ ์ ์์ ๋ง๋๋ ๊ฒ
4๊ฐ์ Point์ Input๊ฐ๊ณผ์ด๋ํ output Point ๊ฐ ํ์
getPerspectiveTransform(src, dst, solveMethod=None)
๋ณํ ํ๋ ฌ์ ๊ตฌํ๊ธฐ ์ํด์๋ cv2.getPerspectiveTransform() ํจ์๊ฐ ํ
cv2.warpPerspective() ํจ์์ ๋ณํํ๋ ฌ๊ฐ์ ์ ์ฉํ์ฌ ์ต์ข ๊ฒฐ๊ณผ ์ด๋ฏธ์ง๋ฅผ ์ป์ ์ ์์
src: 4๊ฐ์ ์๋ณธ ์ขํ์
dst: 4๊ฐ์ ๊ฒฐ๊ณผ ์ขํ์
retval: 3x3 ํฌ์ ๋ณํ ํ๋ ฌ
#include <opencv2/opencv.hpp> // OpenCV์์ ์ง์ํ๋ ๋ชจ๋ ๊ธฐ๋ฅ
#include <opencv2/videoio.hpp> // ๋น๋์ค ์ถ์ ๋ฐ ๋ฐฐ๊ฒฝ segmentation๊ณผ ๊ด๋ จ๋ ๋ฃจํด
#include <opencv2/imgcodecs.hpp> // ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์
์ด ์ ์ธ (Mat ์ด๋ Point๊ฐ ์ ์ธ, ํ๋ ฌ ์ฐ์ฐ ํน์ ๋ฒกํฐ ์ฐ์ฐ)
#include <opencv2/highgui.hpp> // ์๋์ฐ ํ๋ฉด, UI์ฒ๋ฆฌ(์ฌ๋ผ์ด๋, ๋ฒํผ ๋ฑ) ๋ฐ ๋ง์ฐ์ค ์ ์ด ๊ฐ๋ฅ
#include <opencv2/objdetect.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
// #10. Document Scanner
Mat imgOriginal, imgGray, imgBlur, imgCanny, imgThre, imgDil, imgErode, imgWarp, imgCrop;
vector<Point> initialPoints, docPoints;
float w = 420, h = 596;
Mat preProcessing(Mat img)
{
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDil, kernel);
//erode(imgDil, imgErode, kernel);
return imgDil;
}
vector<Point> getContours(Mat image) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2);
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point> biggest;
int maxArea = 0;
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
//cout << area << endl;
string objectType;
if (area > 1000) {
float peri = arcLength(contours[i], true);
// approxPolyDP(์ค๊ณฝ์ , ๊ทผ์ฌ์น ์ ํ๋, ํ๊ณก์ )
// : ์ค๊ณฝ์ ๋ค์ ์ค๊ณฝ์ ๋ค๋ก ๊ทผ์ฌํด ๊ทผ์ฌ ๋ค๊ฐํ์ผ๋ก ๋ฐํ
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
if (area > maxArea && conPoly[i].size() == 4) {
//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 5);
biggest = { conPoly[i][0], conPoly[i][1], conPoly[i][2], conPoly[i][3] };
maxArea = area;
}
// drawContours(image, contours, contourIdx, color, thickness=None, lineType=No)
// : ๊ฒ์ถํ ์ธ๊ณฝ์ ์ ํ์ธํ๊ธฐ ์ํด ์ด ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ธ๊ณฝ์ ์ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ธฐ
//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 2);
//rectangle(imgOriginal, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
}
}
return biggest;
}
void drawPoints(vector<Point> points, Scalar color)
{
for (int i = 0; i < points.size(); i++)
{
circle(imgOriginal, points[i], 10, color, FILLED);
putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN, 4, color, 4);
}
}
vector<Point> reorder(vector<Point> points)
{
vector<Point> newPoints;
vector<int> sumPoints, subPoints;
for (int i = 0; i < 4; i++)
{
sumPoints.push_back(points[i].x + points[i].y);
subPoints.push_back(points[i].x - points[i].y);
}
newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); // 0
newPoints.push_back(points[max_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); //1
newPoints.push_back(points[min_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); //2
newPoints.push_back(points[max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); //3
return newPoints;
}
Mat getWarp(Mat img, vector<Point> points, float w, float h)
{
Point2f src[4] = { points[0],points[1],points[2],points[3] };
Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };
// ์ 4๊ฐ์ ์ด๋ ์ , ์ด๋ ํ ์ขํ๋ฅผ ์
๋ ฅํ๋ฉด ํฌ์ ๋ณํ ํ๋ ฌ์ ๋ฐํํ๋ ํจ์
Mat matrix = getPerspectiveTransform(src, dst);
warpPerspective(img, imgWarp, matrix, Point(w, h));
return imgWarp;
}
int main() {
string path = "Resources/paper.jpg";
imgOriginal = imread(path);
resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);
// Preprocessing
imgThre = preProcessing(imgOriginal);
// Get contours - Biggest
initialPoints = getContours(imgThre);
docPoints = reorder(initialPoints);
//drawPoints(docPoints, Scalar(0, 255, 0));
// Warp
imgWarp = getWarp(imgOriginal, docPoints, w, h);
imshow("image", imgOriginal);
imshow("image Dial", imgThre);
imshow("image Dial", imgWarp);
waitKey(0);
}
๐ง crop ์ผ๋ก ๋ฌธ์์ ์ค์บ๋ ์ถ์ถํ๊ธฐ
#include <opencv2/opencv.hpp> // OpenCV์์ ์ง์ํ๋ ๋ชจ๋ ๊ธฐ๋ฅ
#include <opencv2/videoio.hpp> // ๋น๋์ค ์ถ์ ๋ฐ ๋ฐฐ๊ฒฝ segmentation๊ณผ ๊ด๋ จ๋ ๋ฃจํด
#include <opencv2/imgcodecs.hpp> // ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์
์ด ์ ์ธ (Mat ์ด๋ Point๊ฐ ์ ์ธ, ํ๋ ฌ ์ฐ์ฐ ํน์ ๋ฒกํฐ ์ฐ์ฐ)
#include <opencv2/highgui.hpp> // ์๋์ฐ ํ๋ฉด, UI์ฒ๋ฆฌ(์ฌ๋ผ์ด๋, ๋ฒํผ ๋ฑ) ๋ฐ ๋ง์ฐ์ค ์ ์ด ๊ฐ๋ฅ
#include <opencv2/objdetect.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
// #10. Document Scanner
Mat imgOriginal, imgGray, imgBlur, imgCanny, imgThre, imgDil, imgErode, imgWarp, imgCrop;
vector<Point> initialPoints, docPoints;
float w = 420, h = 596;
Mat preProcessing(Mat img)
{
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDil, kernel);
//erode(imgDil, imgErode, kernel);
return imgDil;
}
vector<Point> getContours(Mat image) {
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2);
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point> biggest;
int maxArea = 0;
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
//cout << area << endl;
string objectType;
if (area > 1000) {
float peri = arcLength(contours[i], true);
// approxPolyDP(์ค๊ณฝ์ , ๊ทผ์ฌ์น ์ ํ๋, ํ๊ณก์ )
// : ์ค๊ณฝ์ ๋ค์ ์ค๊ณฝ์ ๋ค๋ก ๊ทผ์ฌํด ๊ทผ์ฌ ๋ค๊ฐํ์ผ๋ก ๋ฐํ
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
if (area > maxArea && conPoly[i].size() == 4) {
//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 5);
biggest = { conPoly[i][0], conPoly[i][1], conPoly[i][2], conPoly[i][3] };
maxArea = area;
}
// drawContours(image, contours, contourIdx, color, thickness=None, lineType=No)
// : ๊ฒ์ถํ ์ธ๊ณฝ์ ์ ํ์ธํ๊ธฐ ์ํด ์ด ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ธ๊ณฝ์ ์ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ธฐ
//drawContours(imgOriginal, conPoly, i, Scalar(255, 0, 255), 2);
//rectangle(imgOriginal, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
}
}
return biggest;
}
void drawPoints(vector<Point> points, Scalar color)
{
for (int i = 0; i < points.size(); i++)
{
circle(imgOriginal, points[i], 10, color, FILLED);
putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN, 4, color, 4);
}
}
vector<Point> reorder(vector<Point> points)
{
vector<Point> newPoints;
vector<int> sumPoints, subPoints;
for (int i = 0; i < 4; i++)
{
sumPoints.push_back(points[i].x + points[i].y);
subPoints.push_back(points[i].x - points[i].y);
}
newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); // 0
newPoints.push_back(points[max_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); //1
newPoints.push_back(points[min_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); //2
newPoints.push_back(points[max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); //3
return newPoints;
}
Mat getWarp(Mat img, vector<Point> points, float w, float h)
{
Point2f src[4] = { points[0],points[1],points[2],points[3] };
Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };
// ์ 4๊ฐ์ ์ด๋ ์ , ์ด๋ ํ ์ขํ๋ฅผ ์
๋ ฅํ๋ฉด ํฌ์ ๋ณํ ํ๋ ฌ์ ๋ฐํํ๋ ํจ์
Mat matrix = getPerspectiveTransform(src, dst);
warpPerspective(img, imgWarp, matrix, Point(w, h));
return imgWarp;
}
int main() {
string path = "Resources/paper.jpg";
imgOriginal = imread(path);
resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);
// Preprpcessing - Step 1
imgThre = preProcessing(imgOriginal);
// Get Contours - Biggest - Step 2
initialPoints = getContours(imgThre);
docPoints = reorder(initialPoints);
//drawPoints(docPoints, Scalar(0, 255, 0));
// Warp - Step 3
imgWarp = getWarp(imgOriginal, docPoints, w, h);
//Crop - Step 4
int cropVal = 5;
Rect roi(cropVal, cropVal, w - (2 * cropVal), h - (2 * cropVal));
imgCrop = imgWarp(roi);
imshow("image", imgOriginal);
//imshow("image Dial", imgThre);
//imshow("image Dial", imgWarp);
imshow("Image Crop", imgCrop);
waitKey(0);
}