OpenCV

来源:互联网 发布:机器人软件编程 编辑:程序博客网 时间:2024/06/04 20:07

利用OpenCV的功能可以方便快捷的将图片里面的文字纠正过来:

文章是参考国外帖子来写的:http://felix.abecassis.me/2011/09/opencv-detect-skew-angle/

这里有我的一些iOS, Mac代码和测试结果,可以方便的自动旋转文字方向,旋转图片。


图片有一些前提,或者处理缺陷:

1、图片没有外边框干扰

2、倒立或者竖着的图片,纠正后还是倒立,或者竖着的。

比如以下这2个图片,纠正后结果都不理想:


会旋转,但是纠正后会竖立,需要进一步判断。


计算的纠正角度为0,纠正失败。

方法一、利用线条横线计算角度,然后再纠正。

// 参考 Félix Abecassis - OpenCV - Detect skew angle// http://felix.abecassis.me/2011/09/opencv-detect-skew-angle/+ (double)cvMatImageSkewAngleUsingSHT:(cv::Mat)gray;{    // Load in grayscale.    //cv::Mat src = cv::imread(filename, cv::IMREAD_GRAYSCALE);        if(gray.data == NULL){        fprintf(stderr, "error: src.data == NULL\n");        return 0.0f;    }    if(gray.channels() != 1){        fprintf(stderr, "error: src.channels() != 1\n");        return 0.0f;    }    cv::bitwise_not(gray, gray);        cv::Size size = gray.size();        // 需要的是黑色背景    std::vector<cv::Vec4i> lines;    cv::HoughLinesP(gray, lines, 1, CV_PI/180, 100, size.width / 2.f, 20);            cv::Mat disp_lines(size, CV_8UC1, cv::Scalar(0, 0, 0));    double angle = 0.;    unsigned nb_lines = lines.size();    for (unsigned i = 0; i < nb_lines; ++i)    {        cv::line(disp_lines, cv::Point(lines[i][0], lines[i][1]), cv::Point(lines[i][2], lines[i][3]), cv::Scalar(255, 0 ,0));        angle += atan2((double)lines[i][3] - lines[i][1], (double)lines[i][2] - lines[i][0]);    }    angle /= nb_lines; // mean angle, in radians.    angle = angle * 180 / CV_PI;    cv::imwrite("/Users/James/Desktop/skew-lines.bmp", disp_lines);        fprintf(stderr, "ImageSkewAngle result: %f\n", angle);        return angle;}


方法二、利用文字最小区域获取矩形计算角度,然后再纠正。

// 参考 Félix Abecassis - OpenCV - Bounding Box & Skew Angle// http://felix.abecassis.me/2011/10/opencv-bounding-box-skew-angle/+ (double)cvMatImageSkewAngleWithImageBin:(cv::Mat)image_bin{    // 输入的是黑底白字图片    cv::Mat image = image_bin.clone();    if(image.data == NULL){        fprintf(stderr, "error: src.data == NULL\n");        return 0.0f;    }    if(image.channels() != 1){        fprintf(stderr, "error: src.channels() != 1\n");        return 0.0f;    }    cv::imwrite("/Users/James/Desktop/skew-bin.bmp", image);        // Size(23, 3)); Size(5, 3));,使用Size(3, 3)最好    cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));    // 最基本的形态学操作有二:腐蚀与膨胀(Erosion 与 Dilation)    cv::erode(image, image, element);        cv::imwrite("/Users/James/Desktop/skew-erode.bmp", image);        std::vector<cv::Point> points;    cv::Mat_<uchar>::iterator it = image.begin<uchar>();    cv::Mat_<uchar>::iterator end = image.end<uchar>();    for (; it != end; ++it)        if (*it)            points.push_back(it.pos());        cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));            double angle = box.angle;    if (angle < -45.)        angle += 90.;        if(1){        cv::Point2f vertices[4];        box.points(vertices);        for(int i = 0; i < 4; ++i)            cv::line(image, vertices[i], vertices[(i + 1) % 4], cv::Scalar(255, 0, 0), 1, CV_AA);        cv::imwrite("/Users/James/Desktop/skew-rect.bmp", image);            }            fprintf(stderr, "ImageSkewAngle result: %f\n", angle);        return angle;}+ (double)cvMatImageSkewAngleWithImageGray:(cv::Mat)image_gray{    // 输入的是灰度图片    // Load in grayscale.    //cv::Mat img = cv::imread(filename, cv::IMREAD_GRAYSCALE);        cv::Mat image = image_gray.clone();    if(image.data == NULL){        fprintf(stderr, "error: src.data == NULL\n");        return 0.0f;    }    if(image.channels() != 1){        fprintf(stderr, "error: src.channels() != 1\n");        return 0.0f;    }        //中值滤波器是一种非线性滤波器,常用于消除图像中的椒盐噪声。与低通滤波不同的是,中值滤波有利于保留边缘的尖锐度,但它会洗去均匀介质区域中的纹理。    //cv::medianBlur(img, img, 3);        //cv::GaussianBlur(img, img, cv::Size(3, 3), 0);    //GaussianBlur(img, img, cvSize(11,11), 0);//change from median blur to gaussian for more accuracy of square detection        // Binarize    //cv::threshold(img, img, 225, 255, CV_THRESH_BINARY_INV);    cv::threshold(image, image, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); // 二值化效果好        return [RecognitionManager cvMatImageSkewAngleWithImageBin:image];}

最后是纠正图片的方法:

// 参考 Félix Abecassis - OpenCV - Rotation (Deskewing)// http://felix.abecassis.me/2011/10/opencv-rotation-deskewing/+ (cv::Mat)cvMatImageDeskew:(cv::Mat)src angle:(double)angle;{    cv::Mat img = src.clone();    cv::imwrite("/Users/James/Desktop/deskew-src.bmp", img);        cv::Mat cropped;    cv::Mat rotated;    // Load in grayscale.    //cv::Mat img = cv::imread(filename, cv::IMREAD_GRAYSCALE);    if(img.data == NULL){        fprintf(stderr, "error: src.data == NULL");        return rotated;    }    if(img.channels() != 1){        fprintf(stderr, "error: src.channels() != 1");        return rotated;    }        std::vector<cv::Point> points;    cv::Mat_<uchar>::iterator it = img.begin<uchar>();    cv::Mat_<uchar>::iterator end = img.end<uchar>();    for (; it != end; ++it)        if (*it)            points.push_back(it.pos());        cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));        cv::Mat rot_mat = cv::getRotationMatrix2D(box.center, angle, 1);        // 旋转    cv::warpAffine(img, rotated, rot_mat, img.size(), cv::INTER_CUBIC); // 默认是黑色背景    //cv::warpAffine(img, rotated, rot_mat, img.size(), CV_INTER_CUBIC, cv::BORDER_CONSTANT, cvScalarAll(0)); // 黑色背景    //cv::warpAffine(img, rotated, rot_mat, img.size(), cv::INTER_CUBIC, cv::BORDER_CONSTANT, cvScalar(255, 255, 255)); // 白色背景        cv::imwrite("/Users/James/Desktop/deskew-rotated.bmp", rotated);    if(0){        // 切割        cv::Size box_size = box.size;        if (box.angle < -45.)            std::swap(box_size.width, box_size.height);        cv::getRectSubPix(rotated, box_size, box.center, cropped);        cv::imwrite("/Users/James/Desktop/deskew-cropped.bmp", cropped);    }        return rotated;    }


测试代码:


- (void)deskew{       if(1){        cv::Mat gray_image = [RecognitionManager cvMatImageFromCGImageRef_Grayscale:[[imageView image] CGImage_FPLib]];                //double angle = [RecognitionManager cvMatImageSkewAngleWithImageGray:gray_image];        double angle = [RecognitionManager cvMatImageSkewAngleUsingSHT:gray_image];                cv::Mat img_deskew;        img_deskew = [RecognitionManager cvMatImageDeskew:gray_image angle:angle];                imageView.image = [NSImage imageWithCGImage_FPLib:[RecognitionManager CGImageRefFromCVMat:img_deskew]];            }    }


测试结果:

原图:


方法一、




方法二:






更多测试:

原图:


方法二结果:

结果是误旋转了:-(。




可以看出,方法一、对于边框有干扰,无法正确纠正。

方法二、要比方法一适应性强,但是也有边框干扰,都会有一些误操作。

其实还有一种方法就是利用findContours查找最大边框,然后利用图片裁剪函数纠正。

https://stackoverflow.com/questions/8667818/opencv-c-obj-c-detecting-a-sheet-of-paper-square-detection?noredirect=1&lq=1




原创粉丝点击