1 | initial version |
This should extracts and straighten the small images
and so on
Enjoy
void Main_ExtractStamps()
{
Mat img,src, dst, gray, bw;
img = imread("../img/stamp.png");
//remove some unwanted cols on the left
int colsToSkip = 80;
src = img(Rect(colsToSkip, 0, img.cols - colsToSkip, img.rows));
//keep a copy to show selection
src.copyTo(dst);
//convert to gray scale
cvtColor(src, gray, CV_BGR2GRAY);
threshold(gray, bw, 0, 255, THRESH_BINARY | THRESH_OTSU);
// clear a bit
Mat kernel = Mat::ones(Size(3,3), CV_8UC1);
dilate(bw, bw, kernel);
erode(bw, bw, kernel);
Mat copyMask(bw.size(), bw.type());
//detect outer contour of the stamps
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
vector<Rect> stampsRect;
vector<RotatedRect> stampsRectRotated;
findContours(bw, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// iterate through all the top-level contours,
int idx = 0;
for (; idx >= 0; idx = hierarchy[idx][0])
{
//ignore small contour ... may be is noise
if (contours[idx].size() < 20) continue;
//store contour boundingRect
stampsRect.push_back(boundingRect(contours[idx]));
stampsRectRotated.push_back(minAreaRect(contours[idx]));
//prepare the copy mask
drawContours(copyMask, contours, idx, 255, FILLED, 8, hierarchy);
//show selected contours
drawContours(dst, contours, idx, Scalar(0, 0, 255), 2, 8, hierarchy);
}
Mat rot_mat(2, 3, CV_32FC1);
RotatedRect rr;
for (idx = 0; idx < stampsRect.size(); idx++)
{
RotatedRect rr = stampsRectRotated[idx];
Rect rect = stampsRect[idx];
Point center(rect.width / 2, rect.height / 2);
//create an empty image with a default background
Scalar bkg(0, 0, 0);
Mat stamp(rect.height, rect.width, src.type(), bkg);
//copy the stamp from source using a mask
src(rect).copyTo(stamp, copyMask(rect));
//straighten
if (rr.angle != 0)
{
rot_mat=getRotationMatrix2D(center, 90+rr.angle, 1);
warpAffine(stamp, stamp, rot_mat, stamp.size());
}
imwrite("../img/Stamp" + to_string(idx) + ".jpg", stamp);
imshow("Stamp" + to_string(idx), stamp);
}
imshow("Bin", bw);
imwrite("../img/StampDST..jpg", dst);
imshow("DST", dst);
waitKey();
}
2 | No.2 Revision |
This should extracts and straighten the small images
and so on
If the contour isn't a rectangle however its the Bounding Rect will be extracted like this
Enjoy
void Main_ExtractStamps()
{
Mat img,src, dst, gray, bw;
img = imread("../img/stamp.png");
//remove some unwanted cols on the left
int colsToSkip = 80;
src = img(Rect(colsToSkip, 0, img.cols - colsToSkip, img.rows));
//keep a copy to show selection
src.copyTo(dst);
//convert to gray scale
cvtColor(src, gray, CV_BGR2GRAY);
threshold(gray, bw, 0, 255, THRESH_BINARY | THRESH_OTSU);
// clear a bit
Mat kernel = Mat::ones(Size(3,3), CV_8UC1);
dilate(bw, bw, kernel);
erode(bw, bw, kernel);
Mat copyMask(bw.size(), bw.type());
//detect outer contour of the stamps
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
vector<Rect> stampsRect;
vector<RotatedRect> stampsRectRotated;
findContours(bw, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// iterate through all the top-level contours,
int idx = 0;
for (; idx >= 0; idx = hierarchy[idx][0])
{
//ignore small contour ... may be is noise
if (contours[idx].size() < 20) continue;
//store contour boundingRect
stampsRect.push_back(boundingRect(contours[idx]));
stampsRectRotated.push_back(minAreaRect(contours[idx]));
//prepare the copy mask
drawContours(copyMask, contours, idx, 255, FILLED, 8, hierarchy);
//show selected contours
drawContours(dst, contours, idx, Scalar(0, 0, 255), 2, 8, hierarchy);
}
Mat rot_mat(2, 3, CV_32FC1);
RotatedRect rr;
for (idx = 0; idx < stampsRect.size(); idx++)
{
RotatedRect rr = stampsRectRotated[idx];
Rect rect = stampsRect[idx];
Point center(rect.width / 2, rect.height / 2);
//create an empty image with a default background
Scalar bkg(0, 0, 0);
Mat stamp(rect.height, rect.width, src.type(), bkg);
//copy the stamp from source using a mask
src(rect).copyTo(stamp, copyMask(rect));
//straighten
if (rr.angle != 0)
{
rot_mat=getRotationMatrix2D(center, 90+rr.angle, 1);
warpAffine(stamp, stamp, rot_mat, stamp.size());
}
imwrite("../img/Stamp" + to_string(idx) + ".jpg", stamp);
imshow("Stamp" + to_string(idx), stamp);
}
imshow("Bin", bw);
imwrite("../img/StampDST..jpg", dst);
imshow("DST", dst);
waitKey();
}
3 | No.3 Revision |
This should extracts and straighten the small images
and so on
If the contour isn't a rectangle however its the Bounding Rect will be extracted like this
EnjoyEDIT: Ok now you can play with trackbar to understand parameters
namespace ExtractStamps {
#define USE_COPY_MASK 0
int useEqualize, useNormalize, threshold, blurSize;
int extract;
Mat src, src_gray;
const std::string winName = "Extract Stamp";
void Main_ExtractStamps()
FindStamps(Mat &bw, Mat &dst)
{
Mat img,src, dst, gray, bw;
img = imread("../img/stamp.png");
//remove some unwanted cols on // find external contours ignores holes in the left
int colsToSkip = 80;
src = img(Rect(colsToSkip, 0, img.cols - colsToSkip, img.rows));
//keep a copy to show selection
src.copyTo(dst);
//convert to gray scale
cvtColor(src, gray, CV_BGR2GRAY);
threshold(gray, bw, 0, 255, THRESH_BINARY | THRESH_OTSU);
// clear a bit
Mat kernel = Mat::ones(Size(3,3), CV_8UC1);
dilate(bw, bw, kernel);
erode(bw, bw, kernel);
Mat copyMask(bw.size(), bw.type());
//detect outer contour of the stamps
vector<vector<Point> fish
vector<vector<cv::Point> > contours;
vector<Vec4i> vector<cv::Vec4i> hierarchy;
vector<Rect> stampsRect;
vector<RotatedRect> stampsRectRotated;
findContours(bw, cv::findContours(bw, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//draw all contours
vector<Rect> stampsRect;
vector<RotatedRect> stampsRectRotated;
#if USE_COPY_MASK==1
Mat copyMask(bw.rows, bw.cols, bw.type(), Scalar(0));
#endif
int minContourSize = cvRound(0.05*src.cols);
for (int i = 0; i< contours.size(); i++) {
Rect rect = boundingRect(contours[i]);
if (rect.width < minContourSize) {
if (rect.width > minContourSize / 2.0)
// iterate through all the top-level contours,
int idx = 0;
for (; idx >= 0; idx = hierarchy[idx][0])
{
//ignore draw not so small contour ... may be is noise
if (contours[idx].size() < 20) contours in RED
drawContours(dst, contours, i, cv::Scalar(0, 0, 255), 1);
continue;
}
// selected contours in GREEN
drawContours(dst, contours, i, cv::Scalar(0, 255, 0), 1);
//store contour boundingRect
stampsRect.push_back(boundingRect(contours[idx]));
stampsRectRotated.push_back(minAreaRect(contours[idx]));
//prepare cv::rectangle(dst, rect, Scalar(255, 0, 0), 4); //draw it in BLUE
stampsRect.push_back(rect);
stampsRectRotated.push_back(minAreaRect(contours[i]));
#if USE_COPY_MASK==1
// prepare the copy mask
drawContours(copyMask, //drawContours(copyMask, contours, idx, i, 255, FILLED, 8, hierarchy);
//show selected contours
drawContours(dst, contours, idx, Scalar(0, 0, 255), 2, 8, hierarchy);
#endif
}
if (extract) {
Mat rot_mat(2, 3, CV_32FC1);
RotatedRect rr;
double angle;
for (idx (int i = 0; idx < i< stampsRect.size(); idx++)
i++) {
RotatedRect rr = stampsRectRotated[idx];
stampsRectRotated[i];
Rect rect = stampsRect[idx];
stampsRect[i];
Point center(rect.width / 2, rect.height / 2);
//create // create an empty image with a default background
Scalar bkg(0, 0, 255, 0);
Mat stamp(rect.height, rect.width, src.type(), bkg);
//copy the // copy stamp from source
#if USE_COPY_MASK==1
// using a contour mask
src(rect).copyTo(stamp, copyMask(rect));
#else
src(rect).copyTo(stamp);
#endif
//straighten
if (rr.angle != 0) {
if (rr.angle < 0)
angle = rr.angle;
if (rr.angle < -45)
angle = 90 + rr.angle;
if (abs(angle) > 0.5) {
rot_mat=getRotationMatrix2D(center, 90+rr.angle, rot_mat = getRotationMatrix2D(center, angle, 1);
warpAffine(stamp, stamp, rot_mat, stamp.size());
stamp.size(), 1, 0, bkg);
}
imwrite("../img/Stamp" }
//imwrite("../img/Stamp" + to_string(idx) + ".jpg", stamp);
imshow("Stamp" + to_string(idx), to_string(i), stamp);
}
imshow("Bin", }
}
void UseThreshold(const Mat &gray,Mat &bw)
{
std::string win;
// threshold to select bright
cv::threshold(gray, bw, threshold, 255, cv::THRESH_BINARY);
win = "threshold"; namedWindow(win, WINDOW_NORMAL);
imshow(win, bw);
imwrite("../img/StampDST..jpg", }
void Preprocessing(const Mat &imgIn, Mat &imgOut)
{
if (blurSize >= 3) {
blurSize += (1 - blurSize % 2);
GaussianBlur(imgIn, imgOut, cv::Size(blurSize, blurSize), 0);
}
else
imgIn.copyTo(imgOut);
if (useEqualize)
equalizeHist(imgOut, imgOut);
if (useNormalize)
normalize(imgOut, imgOut,0, 255, NORM_MINMAX);
//BrightnessAndContrastAuto(img, img, 1);
}
void onTrackbar(int, void*)
{
Mat tmp,bw, dst;
Preprocessing(src_gray,tmp);
UseThreshold(tmp,bw);
src.copyTo(dst); // keep a copy to show the result
FindStamps(bw, dst);
imshow("DST", imshow(winName, dst);
waitKey();
}
void Main()
{
Mat img;
//img = imread("../img/stamp-big.png");
img = imread("../img/stamp-rot.png");
// remove some unwanted cols and rows
int skipRowTop = 0.01*img.rows;
int skipRowBottom = 0.01*img.rows;
int skipColLeft = 0.08*img.cols;
int skipColRight = 0.01*img.cols;
src = img.colRange(skipColLeft, img.cols - skipColRight).rowRange(skipRowTop, img.rows - skipRowBottom);
namedWindow(winName, WINDOW_NORMAL);
// convert to gray scale
cvtColor(src, src_gray, CV_BGR2GRAY);
// prepare defaults
useEqualize = 0; useNormalize = 1;
blurSize = 5; threshold = 70;
extract = 0;
createTrackbar("Equalize", winName, &useEqualize, 1, onTrackbar, 0);
createTrackbar("Normalize", winName, &useNormalize, 1, onTrackbar, 0);
createTrackbar("Blur Sigma", winName, &blurSize, 50, onTrackbar, 0);
createTrackbar("Threshold", winName, &threshold, 255, onTrackbar, 0);
createTrackbar("Extract", winName, &extract, 1, onTrackbar, 0);
onTrackbar(0, 0);
cv::waitKey(0);
}//main
}//namespace