Skip to content

Commit

Permalink
Got tesseract number recognition working and made work with sudoku so…
Browse files Browse the repository at this point in the history
…lver
  • Loading branch information
petelilley committed Nov 5, 2023
1 parent cd8a835 commit 417a5d6
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 229 deletions.
33 changes: 24 additions & 9 deletions desktop/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,35 +1,50 @@
#include <solver/solver.h>
#include <solver/textRecognition.h>
#include <solver/board_detection.h>

#include <opencv2/highgui/highgui.hpp>

int main(int argc, char** argv) {

#if 0
// Open the camera.
cv::VideoCapture capture(0);
if (!capture.isOpened()) {
std::cout << "Error opening video stream or file" << std::endl;
return -1;
}

cv::Mat frame, output_frame;

cv::Mat frame, preview_frame;
while (1) {
// Capture the frame.
/*
capture >> frame;
if (frame.empty()) {
std::cout << "No captured frame" << std::endl;
break;
}

output_frame = puzzle_solver(frame);
preview_frame = prepare_frame(frame);
std::array<cv::Point2f, 4> corners = find_corners(preview_frame);
cv::line(frame, corners[0], corners[1], cv::Scalar(0, 255, 0), 4);
cv::line(frame, corners[1], corners[2], cv::Scalar(0, 255, 0), 4);
cv::line(frame, corners[2], corners[3], cv::Scalar(0, 255, 0), 4);
cv::line(frame, corners[3], corners[0], cv::Scalar(0, 255, 0), 4);

// Present the frame.
cv::imshow("Frame", output_frame);
*/
// Quit if ESC pressed.
cv::imshow("Frame", frame);

// Quit if ESC pressed.
char c = (char)cv::waitKey(25);
if (c == 27) break;
}
#else

/* cv::imwrite("output.png", frame); */
cv::Mat frame = cv::imread("output.png");
#endif


// Now do processing on the frame!
std::optional<SudokuGrid> grid = puzzle_solver(frame);
(void)grid;

return 0;
}
3 changes: 3 additions & 0 deletions solver/include/solver/board_detection.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
#include <opencv2/opencv.hpp>
#include <array>

cv::Mat prepare_frame(cv::Mat& frame);
std::array<cv::Point2f, 4> find_corners(cv::Mat& frame);

std::array<cv::Mat, 81> detect_board(cv::Mat& frame);
3 changes: 2 additions & 1 deletion solver/include/solver/solver.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <opencv2/opencv.hpp>
#include <solver/sudoku_grid.h>

cv::Mat puzzle_solver(cv::Mat& frame);
std::optional<SudokuGrid> puzzle_solver(cv::Mat& frame);
27 changes: 0 additions & 27 deletions solver/include/solver/sudokuSolver.h

This file was deleted.

7 changes: 7 additions & 0 deletions solver/include/solver/sudoku_grid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include <array>

using SudokuGrid = std::array<std::array<int, 9>, 9>;

void print_grid(const SudokuGrid& grid);
23 changes: 23 additions & 0 deletions solver/include/solver/sudoku_solver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once
#include <vector>
#include <solver/sudoku_grid.h>
#include <optional>

class Cell {
public:
int row;
int col;
std::vector<int> possibleNums;
};

class SudokuSolver {
SudokuGrid grid;
std::vector<Cell> list;

public:
void setGrid(SudokuGrid g);
bool solve();
bool isSafe(int row, int col, int num);
void setInitialPossibleNums();
std::optional<SudokuGrid> doEverything(SudokuGrid initialGrid);
};
7 changes: 0 additions & 7 deletions solver/include/solver/textRecognition.h

This file was deleted.

9 changes: 9 additions & 0 deletions solver/include/solver/text_recognition.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <opencv2/opencv.hpp>

#include <array>

std::array<std::array<int, 9>, 9> recognize_board_numbers(std::array<cv::Mat, 81>& frame);

/* std::string recognize_text(cv::Mat& frame); */
64 changes: 22 additions & 42 deletions solver/src/board_detection.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <solver/board_detection.h>

static cv::Mat prepare_frame(cv::Mat& frame) {
cv::Mat prepare_frame(cv::Mat& frame) {
cv::Mat output;

// Convert to grayscale.
Expand All @@ -24,27 +24,7 @@ static cv::Mat prepare_frame(cv::Mat& frame) {
return output;
}

static void draw_line(cv::Mat& frame, cv::Vec4i& line) {
/* cv::line(frame, cv::Point2i(line[0], line[1]), cv::Point2i(line[2], line[3]), cv::Scalar(0, 255, 0), 2); */
// draw point
cv::circle(frame, cv::Point2i(line[0], line[1]), 2, cv::Scalar(0, 255, 0), 2);
cv::circle(frame, cv::Point2i(line[2], line[3]), 2, cv::Scalar(0, 255, 0), 2);
}

static std::vector<cv::Vec4i> merge_lines(cv::Mat& frame, std::vector<cv::Vec4i>& lines) {
std::vector<cv::Vec4i> merged_lines;

int height = frame.rows,
width = frame.cols;

for (auto& line : lines) {

}

return merged_lines;
}

static cv::Mat crop_to_board(cv::Mat& frame) {
std::array<cv::Point2f, 4> find_corners(cv::Mat& frame) {
cv::Mat output = frame.clone();
// Flood filling to find the biggest blob in the picture.

Expand Down Expand Up @@ -72,8 +52,8 @@ static cv::Mat crop_to_board(cv::Mat& frame) {
cv::floodFill(output, max_point, cv::Scalar(255));

// Erode
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));
cv::erode(output, output, kernel);
/* cv::Mat kernel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)); */
/* cv::erode(output, output, kernel); */

// Flood fill the rest with black.
for (int y = 0; y < height; ++y) {
Expand All @@ -89,12 +69,6 @@ static cv::Mat crop_to_board(cv::Mat& frame) {
std::vector<cv::Vec4i> lines;
cv::HoughLinesP(output, lines, 1, CV_PI / 180, 200);

cv::cvtColor(output, output, cv::COLOR_GRAY2BGR);

/* for (auto& line : lines) { */
/* draw_line(output, line); */
/* } */

// Find outermost corners
cv::Point2f max_x(0, 0),
max_y(0, 0),
Expand Down Expand Up @@ -150,36 +124,37 @@ static cv::Mat crop_to_board(cv::Mat& frame) {
cv::Point2f left[2] = {xsorted[2], xsorted[3]};
cv::Point2f right[2] = {xsorted[0], xsorted[1]};

cv::Point2f corners[4];
std::array<cv::Point2f, 4> corners;
corners[0] = top[0].x < top[1].x ? top[0] : top[1];
corners[1] = top[0].x > top[1].x ? top[0] : top[1];
corners[2] = bottom[0].x > bottom[1].x ? bottom[0] : bottom[1];
corners[3] = bottom[0].x < bottom[1].x ? bottom[0] : bottom[1];

#define CLOSE_PROXIMITY 500

static int counter = 0;
if (cv::norm(top[0] - top[1]) < CLOSE_PROXIMITY ||
cv::norm(bottom[0] - bottom[1]) < CLOSE_PROXIMITY ||
cv::norm(left[0] - left[1]) < CLOSE_PROXIMITY ||
cv::norm(right[0] - right[1]) < CLOSE_PROXIMITY) {
std::cout << "issue? " << counter++ << std::endl;

corners[0] = cv::Point2f(min_x.x, min_y.y);
corners[1] = cv::Point2f(max_x.x, min_y.y);
corners[2] = cv::Point2f(max_x.x, max_y.y);
corners[3] = cv::Point2f(min_x.x, max_y.y);
}

cv::line(output, corners[0], corners[1], cv::Scalar(0, 0, 255), 5);
cv::line(output, corners[1], corners[2], cv::Scalar(0, 0, 255), 5);
cv::line(output, corners[2], corners[3], cv::Scalar(0, 0, 255), 5);
cv::line(output, corners[3], corners[0], cv::Scalar(0, 0, 255), 5);
cv::absdiff(frame, output, frame);

return corners;
}

static cv::Mat crop_to_board(cv::Mat& frame, std::array<cv::Point2f, 4>& _corners) {
cv::Point2f* corners = _corners.data();

cv::Mat output;

// Warp the image to a square using the corners.
cv::Point2f src[4] = {corners[0], corners[1], corners[2], corners[3]};
cv::Point2f dst[4] = {cv::Point2f(0, 0), cv::Point2f(256, 0), cv::Point2f(256, 256), cv::Point2f(0, 256)};
cv::Mat transform = cv::getPerspectiveTransform(src, dst);
cv::Mat transform = cv::getPerspectiveTransform(corners, dst);
cv::warpPerspective(frame, output, transform, cv::Size(256, 256));

return output;
Expand All @@ -188,15 +163,20 @@ static cv::Mat crop_to_board(cv::Mat& frame) {
std::array<cv::Mat, 81> detect_board(cv::Mat& frame) {
cv::Mat output = prepare_frame(frame);

cv::Mat grid = crop_to_board(output);
std::array<cv::Point2f, 4> corners = find_corners(output);

frame = crop_to_board(output, corners);

// split grid into 9x9 grid.
std::array<cv::Mat, 81> grid_pieces;
for (int y = 0; y < 9; ++y) {
for (int x = 0; x < 9; ++x) {

cv::Rect roi(x * 28, y * 28, 28, 28);
grid_pieces[y * 9 + x] = grid(roi);
frame(roi).copyTo(grid_pieces[y * 9 + x]);

cv::Mat kernel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(1, 1));
cv::erode(grid_pieces[y * 9 + x], grid_pieces[y * 9 + x], kernel);
}
}

Expand Down
25 changes: 23 additions & 2 deletions solver/src/solver.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
#include <solver/solver.h>
#include <solver/board_detection.h>
#include <solver/text_recognition.h>
#include <solver/sudoku_solver.h>

cv::Mat puzzle_solver(cv::Mat& frame) {
std::optional<SudokuGrid> puzzle_solver(cv::Mat& frame) {
// Solve stuff here!
return frame;
std::array<cv::Mat, 81> cells = detect_board(frame);

SudokuGrid grid = recognize_board_numbers(cells);

std::cout << "Original puzzle:" << std::endl;
print_grid(grid);

SudokuSolver solver;
std::optional<SudokuGrid> solved_grid = solver.doEverything(grid);

if (solved_grid) {
std::cout << "Solved puzzle:" << std::endl;
print_grid(*solved_grid);
}
else {
std::cout << "Could not solve puzzle!!" << std::endl;
}

return solved_grid;
}
Loading

0 comments on commit 417a5d6

Please sign in to comment.