I have a picture with a table in it and I need to extract the boxes with the handwriting in it then crop them,because I need to recognise the handwriting in those boxes one by one but the problem is the findSquare function gives me an output of too many squares with the same coordinates(+-5 pixels) beacuse of the several threshold levels.I already reduced the number of squares from 300 to 90 by searching in only one channel.The teacher told me to filter the extra squares by their coordinates,if the difference between the square coordinates is less then 10 pixels it means it's the same box. The picture with the table is here https://ibb.co/Ms8YP8n And instead of 20 boxes it crops me around 90 and most of them are the same. So I was trying to go through the squares vector in a for loop and eliminate the squares that are seen twice.
Here is the code that I was trying with
vector<vector<Point> > same;
same.clear();
same = squares;
int sq = squares.size();
for (int i = 0; i < sq; i++) {
for (int j = 1; j < sq-1; j++) {
if (-5 < (same[i][0].x - same[j][0].x) < 5 && -5 < (same[i][0].y - same[j][0].y) < 5 &&
-5 < (same[i][1].x - same[j][1].x) < 5 && -5 < (same[i][1].y - same[j][1].y) < 5 &&
-5 < (same[i][2].x - same[j][2].x) < 5 && -5 < (same[i][2].y - same[j][2].y) < 5 &&
-5 < (same[i][3].x - same[j][3].x) < 5 && -5 < (same[i][3].y - same[j][3].y) < 5) {
squares.erase(same.begin() + j);
}
}
}
The "same" vector is just a copy of "squares".
The error that gives me is "Vector erase iterator outside range". You can find this code in the drawSquare function bellow where you can see the full code too.
I need the pictures for future proccessing, I need to recognise the handwritten numbers.
Can anybody please help me?With another method or ideeas or some fixes in this code...
Thank you !
static void findSquares(const Mat& image, vector<vector<Point> >& squares)
{
squares.clear();
Mat pyr, timg, timb, gray0(image.size(), CV_8UC1), gray;
pyrDown(image, pyr, Size(image.cols / 2, image.rows / 2));
pyrUp(pyr, timg, image.size());
vector<vector<Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 1; c++)
{
int ch[] = { c, 0 };
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// try several threshold levels
for (int l = 0; l < N; l++)
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if (l == 0)
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
Canny(gray0, gray, 0, thresh, 5);
// dilate canny output to remove potential
// holes between edge segments
dilate(gray, gray, Mat(), Point(-1, -1));
}
else
{
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l + 1) * 255 / N;
// imshow("graaay0", gray);
}
// find contours and store them all as a list
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
vector<Point> approx;
// test each contour
for (int i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
//// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() >= 4 && approx.size() <= 6 &&
fabs(contourArea(Mat(approx))) > 5000 && fabs(contourArea(Mat(approx))) < 8000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
// find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = MAX(maxCosine, cosine);
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if (maxCosine >= 0 && maxCosine < 0.2)
squares.push_back(approx);
}
}
}
}
}
// the function draws all the squares in the image
static void drawSquares(Mat& image, vector<vector<Point> > &squares)
{
vector<vector<Point> > same;
same.clear();
same = squares;
int sq = squares.size();
for (int i = 0; i < sq; i++) {
for (int j = 1; j < sq; j++) {
if (-5 < (same[i][0].x - same[j][0].x) < 5 && -5 < (same[i][0].y - same[j][0].y) < 5 &&
-5 < (same[i][1].x - same[j][1].x) < 5 && -5 < (same[i][1].y - same[j][1].y) < 5 &&
-5 < (same[i][2].x - same[j][2].x) < 5 && -5 < (same[i][2].y - same[j][2].y) < 5 &&
-5 < (same[i][3].x - same[j][3].x) < 5 && -5 < (same[i][3].y - same[j][3].y) < 5) {
squares.erase(same.begin() + j);
}
}
}
for (int i = 0; i < squares.size(); i++)
{
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
polylines(image, &p, &n, 1, true, Scalar(0, 255, 0), 3, LINE_AA);
}
imshow("Plate with green", image);
}
Mat ImageExtract(Mat& image, vector<vector<Point> >& squares)
{
char file_name[100];
int x = squares.size();
printf("squaree %d", x);
Mat roi;
for (int i = 0; i < squares.size(); i++)
{
sprintf(file_name, "cropped%d.jpg", i + 1);
Rect r = boundingRect(squares[i]);
roi = Mat(image, r);
imwrite(file_name, roi);
}
return roi;
}
void Preprocessing() {
char fname[MAX_PATH];
openFileDlg(fname);
Mat img = imread(fname, 1);
std::vector<std::vector<Point> > squares;
findSquares(img, squares);
ImageExtract(img, squares);
drawSquares(img, squares);
waitKey(0);
}
int main() {
Preprocessing();
return 0;
}
[1]: https://i.stack.imgur.com/mwvxX.png