/* * Copied from http://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/ */ bool straighten(Mat &src, Mat &dst, unsigned int rows, unsigned int cols) { vector<cv::Vec4i> slines; vector<par_line> par_lines; vector<par_line> borders; bool new_corners = false; vector<Point2f> corners = find_corners(src, rows, cols); vector<Point2f> quad_pts; if( corners.size() == 4 ) { // Define the destination image dst = Mat::zeros(cols, rows, CV_8UC3); // Corners of the destination image quad_pts.push_back(Point2f(0, 0)); quad_pts.push_back(Point2f(dst.cols, 0)); quad_pts.push_back(Point2f(dst.cols, dst.rows)); quad_pts.push_back(Point2f(0, dst.rows)); // Get transformation matrix Mat transmtx = getPerspectiveTransform(corners, quad_pts); // Apply perspective transformation warpPerspective(src, dst, transmtx, dst.size()); return true; } else { return false; } }
void reset_sight() { list_node *c; for (c = CORNERS->next; c->next != NULL; c = c->next, free(c->prev)) { if (((line_point *) c->data) != NULL) { free((line_point *) c->data); } } CORNERS = make_list(); memset(RAYS, 0, sizeof(RAYS)); RAY_INDEX = 0; find_corners(); }
int find(cv::Mat src, unsigned thresh,std::vector<cv::Point2f>& corners){ cv::Mat src_gray; cv::cvtColor( src, src_gray, CV_RGB2GRAY ); cv::GaussianBlur(src_gray,src_gray,cv::Size(3,3),0); std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> lines; cv::Mat canny_output; cv::Canny( src_gray, canny_output, thresh, thresh*3, 3 ); cv::findContours( canny_output, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); std::vector<cv::Point> approx; cv::Scalar color(0,0,255); for (int i = 0; i < contours.size(); i++){ // Approximate contour with accuracy proportional // to the contour perimeter cv::approxPolyDP( cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true) * 0.02, true); // Skip small or non-convex objects if (std::fabs(cv::contourArea(contours[i])) < 200 || !cv::isContourConvex(approx)) continue; cv::Mat drawing = cv::Mat::zeros( src.size(), CV_8UC3 ); if (approx.size()==4){ //cv::Rect r = cv::boundingRect(approx); // описанный квадрат //rectangle(drawing,r.tl(),r.br(),color,2,8,0); cv::drawContours(drawing,contours,i,cv::Scalar(255,255,255)); //FIX MEE!!!!!!!!!!!!! return find_corners(drawing, thresh, corners); } } return -1; }
int main(int argc, char* argv[]) { if (argc != 2) { return usage(argv[0]); return 1; } graymap_t* graymap = alloc_graymap_from_pgm(argv[1]); if (!graymap) { fprintf(stderr, "Failed to load %s\n", argv[1]); return 1; } // http://sudokugrab.blogspot.com/2009/07/how-does-it-all-work.html threshold_pixels(graymap); save_graymap_to_pgm("1_thresh.pgm", graymap); // TODO: Maybe run a few open iterations to clean up noise pixels? find_biggest_connected_component(graymap); save_graymap_to_pgm("2_component.pgm", graymap); float projected_corners[4][2]; if (!find_corners(graymap, projected_corners)) { fprintf(stderr, "Failed to find corners\n"); return 1; } for (int i = 0; i < 4; ++i) { const int k = 11; for (int y = projected_corners[i][1] - k/2; y <= projected_corners[i][1] + k/2; ++y) { for (int x = projected_corners[i][0] - k/2; x <= projected_corners[i][0] + k/2; ++x) { if (x >= 0 && x < graymap->w && y >= 0 && y < graymap->h) graymap->data[y*graymap->w + x] = 128 + 30 * i; } } } save_graymap_to_pgm("3_corners.pgm", graymap); free_graymap(graymap); const int kTileSize = 16; const int kSudokuWidth = kTileSize * 9; const int kSudokuHeight = kTileSize * 9; float unprojected_corners[4][2] = { { 0, 0 }, { kSudokuWidth - 1 , 0 }, { 0, kSudokuHeight - 1 }, { kSudokuWidth - 1, kSudokuHeight - 1 }, }; float projmat[3][3]; compute_projection_matrix(projmat, unprojected_corners, projected_corners); graymap_t* orig = alloc_graymap_from_pgm(argv[1]); graymap_t* sudoku = alloc_graymap(kSudokuWidth, kSudokuHeight); for (int y = 0; y < kSudokuHeight; ++y) { for (int x = 0; x < kSudokuWidth; ++x) { float p[3] = { projmat[0][0]*x + projmat[0][1]*y + projmat[0][2], projmat[1][0]*x + projmat[1][1]*y + projmat[1][2], projmat[2][0]*x + projmat[2][1]*y + projmat[2][2], }; float sx = p[0] / p[2]; float sy = p[1] / p[2]; int ix = (int)sx; int iy = (int)sy; // XXX nicer sampling? #if 1 sx -= ix; sy -= iy; int ix2 = ix < orig->w-1 ? ix + 1 : ix; int iy2 = iy < orig->h-1 ? iy + 1 : iy; float p1 = lerp(sx, orig->data[iy*graymap->w + ix], orig->data[iy*graymap->w + ix2]); float p2 = lerp(sx, orig->data[iy2*graymap->w + ix], orig->data[iy2*graymap->w + ix2]); float p3 = lerp(sy, p1, p2); sudoku->data[y * kSudokuWidth + x] = p3 + 0.5; #else sudoku->data[y * kSudokuWidth + x] = orig->data[iy*graymap->w + ix]; #endif } } free_graymap(orig); save_graymap_to_pgm("4_sudoku.pgm", sudoku); threshold_pixels(sudoku); save_graymap_to_pgm("5_thresh.pgm", sudoku); graymap_t* tile = alloc_graymap(kTileSize, kTileSize); for (int r = 0; r < 9; ++r) { for (int c = 0; c < 9; ++c) { // XXX give graymap a stride? Then this copying isn't needed. for (int y = 0; y < tile->h; ++y) for (int x = 0; x < tile->w; ++x) { tile->data[y*tile->w + x] = sudoku->data[(y + r*kTileSize)*sudoku->w + (x + c*kTileSize)]; } // Clean 2px wide border. memset(tile->data, 255, 2*kTileSize); memset(&tile->data[14 * kTileSize], 255, 2*kTileSize); for (int i = 2; i < 14; ++i) { tile->data[i*kTileSize + 0] = 255; tile->data[i*kTileSize + 1] = 255; tile->data[i*kTileSize + 14] = 255; tile->data[i*kTileSize + 15] = 255; } for (int y = 0; y < tile->h; ++y) for (int x = 0; x < tile->w; ++x) { sudoku->data[(y + r*kTileSize)*sudoku->w + (x + c*kTileSize)] = tile->data[y*tile->w + x]; } } } free_graymap(tile); save_graymap_to_pgm("6_comp.pgm", sudoku); free_graymap(sudoku); }