如何在 openCV/C++ 中将 png 文件中的单词转换为字符串?

How to convert words from png file to string in openCV/C++?

所以我的项目是创建一个程序,根据用户点击图像的位置检测拼字板上的单词。我的问题如下:

  1. 如何将检测到的字母存入strings/words? (即依次检查每个轮廓,并根据它是否接近另一个轮廓然后将其添加为字符串的下一个元素)

程序根据力矩和轮廓区域绘制每个字母周围的轮廓。 当前代码中的任何更正将不胜感激。它有效,但显然它可以写得更好。以下是获得的图片:

二进制:

颜色:

#include <opencv2\core.hpp> //cv::Mat etc
#include <opencv2\imgproc.hpp> // algorithms 
#include <opencv2\highgui.hpp> // input, output 
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;

Mat image, labimg, perspective, gaussian, binary, gray, erodedimg, dilatedimg, newmat, newmat2, transformed, transformed2;
Mat_<Point> capturePoint; // point coordinates, global variable;

vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
int largest_contour_index = 0;
double largest_area = 0;

void on_mouse(int e, int x, int y, int d, void *ptr)
{
    if (e == EVENT_LBUTTONDOWN)
    {
        Point*p = (Point*)ptr;
        p->x = x;
        p->y = y;

        //Point center = *p;
        cout << *p << endl;

        //destroyWindow("My Window");
    }
}

void myperspective()
{
    Mat dst = image.clone(); //(image.rows, image.cols, CV_8UC1, Scalar::all(0)); //create destination image
    findContours(binary.clone(), contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0,0)); // Find the contours in the image
    for (int i = 0; i < contours.size(); i++) 
    {
        double a = contourArea(contours[i], false);  //  Find the area of contour
        if (a > largest_area) 
        {
            largest_area = a;
            largest_contour_index = i;                //Store the index of largest contour
        }
    }

    drawContours(dst, contours, largest_contour_index, Scalar(0, 255, 0), 2, 8, hierarchy);
    //imshow("Display window", dst);
    //waitKey(0);

    vector<vector<Point> > contours_poly(1);
    approxPolyDP(Mat(contours[largest_contour_index]), contours_poly[0], 10, true);
    Point2f output_points[4];
    Point2f input_points[4];

    cout << contours_poly[0].size();

    int min_x = image.cols;
    int min_y = image.rows;
    int max_x = 0;
    int max_y = 0;
    int myxmin = image.cols;
    int myxmin2 = image.cols;
    int myxmax = 0;
    int myymin = image.rows;

    for (int k = 0; k < contours_poly[0].size(); k++)
    {
        if (contours_poly[0][k].x < min_x && contours_poly[0][k].y < min_y)
        {
            min_x = contours_poly[0][k].x;
            min_y = contours_poly[0][k].y;
        }
        else if (contours_poly[0][k].x <= min_x && contours_poly[0][k].y > max_y)
        {
            //min_x = contours_poly[0][k].x;
            max_y = contours_poly[0][k].y;
            myxmin2 = contours_poly[0][k].x;
        }
        else if (contours_poly[0][k].x > max_x && contours_poly[0][k].y > max_y)
        {
            max_x = contours_poly[0][k].x;
            //max_y = contours_poly[0][k].y;
        }
        else if (contours_poly[0][k].x < max_x && contours_poly[0][k].y <= (min_y+20) && contours_poly[0][k].x > myxmax)
        {
            myxmax = contours_poly[0][k].x;
        }
        else if (contours_poly[0][k].x > min_x && contours_poly[0][k].y <= min_y && contours_poly[0][k].x < myxmin)
        {
            myxmin = contours_poly[0][k].x;
            myymin = contours_poly[0][k].y;
        }
        else
        {
            cout << "NA" << endl;
        }
    }

    input_points[0] = Point(myxmin, myymin);
    input_points[1] = Point(myxmax, min_y);
    input_points[2] = Point(myxmin2, max_y);
    input_points[3] = Point(max_x-30, max_y+5);

    output_points[0] = Point(0, 0);
    output_points[1] = Point(image.cols, 0);
    output_points[2] = Point(0, image.rows);
    output_points[3] = Point(image.cols, image.rows);

    Mat transmtx = getPerspectiveTransform(input_points, output_points);
    transformed = Mat::zeros(image.rows, image.cols, CV_8UC3);
    transformed2 = Mat::zeros(image.rows, image.cols, CV_8UC3);
    warpPerspective(binary, transformed, transmtx, image.size());
    warpPerspective(image, transformed2, transmtx, image.size());
}

int main(int argc, char** argv)
{
    image = cv::imread("ideal.png"); // Read image from file
    namedWindow("Display window", WINDOW_NORMAL);

    cv::GaussianBlur(image, gaussian, Size(3, 3), 0);
    cvtColor(gaussian, gray, CV_BGR2GRAY);

    cv::adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 7, 2);
    //createTrackbar("Threshold:", "Display window2", &thresh_elem, max_thresh_size, Threshold);
    //createTrackbar("Kernel:", "Display window2", &kernel_size, max_kernel_size, Threshold);
    //imshow("Display window", binary);
    //waitKey(0);

    myperspective();

    vector<vector<Point>>lettercontours;
    vector<Vec4i> hierarchyoflet;

    findContours(transformed, lettercontours, hierarchyoflet, CV_RETR_LIST, CV_CHAIN_APPROX_TC89_L1);
    int idx = 0;
    vector<Point>contour;

    //double table[100][7];
    fstream humom("humombase.txt", ios::out);

    double hu[7];

    for (int i = 0; idx >= 0; idx = hierarchyoflet[idx][0], i++)
    {
        double area = contourArea(lettercontours[i]);
        Moments lettermoments = moments(lettercontours[i]);
        HuMoments(lettermoments, hu);
        humom << hu[0] << " " << hu[1] << " " << hu[2] << " " << hu[3] << " " << hu[4] << " " << hu[5] << " " << hu[6] << "\n";
        if (area > 500 && area < 1800 && hu[0] > 0.16292 && hu[0] < 0.18292 && hu[1]> 0.000534 && hu[1] < 0.000665 && hu[2]> 0.000162 && hu[2] < 0.000339)
        { 
            cout << "D"; 
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.257 && hu[0] < 0.283 && hu[1]> 0.0423 && hu[1] < 0.0473 && hu[2]> 1.044e-05 && hu[2] < 8.475e-05)
        {
            cout << "I";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
         else if (area > 500 && area < 1800 && hu[0] > 0.243 && hu[0] < 0.263 && hu[1]> 0.000837 && hu[1] < 0.000857 && hu[2]> 0.000136 && hu[2] < 0.000156)
        {
            cout << "S";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.287 && hu[0] < 0.3 && hu[1]> 0.009 && hu[1] < 0.015 && hu[2]> 0.019 && hu[2] < 0.021)
        {
            cout << "T";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.28 && hu[0] < 0.3 && hu[1]> 0.002 && hu[1] < 0.003 && hu[2]> 0.0005 && hu[2] < 0.0007)
        {
            cout << "U";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.191 && hu[0] < 0.197 && hu[1]> 0.0001 && hu[1] < 0.005 && hu[2]> 4.39e-05 && hu[2] < 0.0003)
        {
            cout << "R";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.156 && hu[0] < 0.176 && hu[1]> 4.606e-05 && hu[1] < 4.806e-05 && hu[2]> 4.89e-05 && hu[2] < 5e-05)
        {
            cout << "B";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.24 && hu[0] < 0.28 && hu[1]> 0.001 && hu[1] < 0.004 && hu[2]> 0 && hu[2] < 0.0002)
        {
            cout << "E";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.15 && hu[0] < 0.25 && hu[1]> 0.0004 && hu[1] < 0.0006 && hu[2]> 0.004 && hu[2] < 0.006)
        {
            cout << "A";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.33 && hu[0] < 0.35 && hu[1]> 0.02 && hu[1] < 0.04 && hu[2]> 0.008 && hu[2] < 0.01)
        {
            cout << "M";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.29 && hu[0] < 0.32 && hu[1]> 0.027 && hu[1] < 0.034 && hu[2]> 0.009 && hu[2] < 0.015)
        {
            cout << "L";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.23 && hu[0] < 0.25 && hu[1]> 0.0009 && hu[1] < 0.0015 && hu[2]> 0.0005 && hu[2] < 0.0007)
        {
            cout << "K";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.27 && hu[0] < 0.29 && hu[1]> 0.0002 && hu[1] < 0.0003 && hu[2]> 0.019 && hu[2] < 0.021)
        {
            cout << "Y";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.15 && hu[0] < 0.17 && hu[1]> 0.001 && hu[1] < 0.002 && hu[2]> 2.2e-07 && hu[2] < 2.5e-07)
        {
            cout << "O";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else if (area > 500 && area < 1800 && hu[0] > 0.28 && hu[0] < 0.30 && hu[1]> 0.022 && hu[1] < 0.024 && hu[2]> 0.002 && hu[2] < 0.0025)
        {
            cout << "W";
            drawContours(transformed2, lettercontours, idx, Scalar(0, 255, 255), 2, 8, hierarchyoflet, 3);
        }
        else
        {
            drawContours(transformed, lettercontours, idx, Scalar(0,0,0), CV_FILLED, 8, hierarchyoflet);
        }
    }

    Mat element = getStructuringElement(MORPH_CROSS, Size(3, 3), Point(1, 1));
    for (int o = 0; o < 1; o++)
    {
        erode(transformed, erodedimg, element);
        dilate(erodedimg, dilatedimg, element);
        transformed = dilatedimg;
    }

    imshow("Display window", transformed);
    waitKey(0);
    imshow("Display window", transformed2);

    namedWindow("Display window2", WINDOW_NORMAL);
    Point p;
    setMouseCallback("Display window2", on_mouse, &p); //set the callback function for any mouse event
    imshow("Display window2", transformed2);
    waitKey(0); //wait till any key is pressed 
    return 0;
}

我将从检测网格开始。

  1. 找到 4 个完整的角单元格

    所以让 (x1,y1)...(x4,y4) 成为找到的完整边形成矩形的中心点。 nx=12 , ny=12 是单元格中这些点之间的距离。假设 (x1,y1) 有网格位置 (1,1)(x2,y2) (1+nx,1) 所以:

    screen(x,y) grid(i,j)
    x1,y1        1, 1
    x2,y2       13, 1
    x3,y3        1,13
    x4,y4       13,13
    
  2. interpolate/fit网格线

    所以对于单元格 (i,j) 到屏幕 (x,y) 的转换,我们可以这样写:

    a=x1+(x3-x1)*(j-j1); // x position of (i1,j)
    b=x2+(x4-x2)*(j-j2); // x position of (i2,j)
    x=a +(b - a)*(i-i1)/(i2-i1) // x position of (i,j)
    a=y1+(y2-y1)*(i-i1); // y position of (i,j1)
    b=y3+(y4-y3)*(i-i3); // y position of (i,j3)
    y=a +(b - a)*(j-j1)/(j3-j1) // x position of (i,j)
    

    希望我没有犯任何错误(因为我是直接在 SO 编辑器中得出的)...现在您知道每个单元格的中心位置。单元格 (i,j) 的角是使用 (i+/-0.5,j+/-0.5) 坐标通过此转换计算的...

    这是简单的bi-linear插值,如果失真太大,您也可以使用bi-cubic或任何其他插值。

    如果您没有完整的单元格形成矩形的角,您可以使用 4 条线覆盖尽可能大的区域。等式会有点变化,但它是可行的。

  3. 创建在地图和屏幕位置之间转换的函数

    注意空单元格与在地面板上偏移的填充单元格不同。如果您还需要反向转换 (x,y)->(i,j) 那么您可以按照与 #2 相同的方式进行,只需交换 i,jx,y ...

您可以使用它仅在所需的单元格而不是整个图像中查找轮廓,从而消除轮廓分组的问题。

您还可以剪切每个单元格的右下部分,以忽略轮廓中的数字分数。我不使用 OpenCV 所以如果你在将它包含到 OpenCV 代码中时遇到问题你总是可以生成 ROI 从这个应该很容易在 OpenCV 中使用到 select 任何操作的特定区域的映射。

对于粗略的字符检测,您需要某种 OCR,它不是 OpenCV 的一部分(至少据我所知).很少有像 Tesseract 这样的开源 OCR。您也可以尝试更简单的方法,例如相关 QA:

  • OCR and character similarity