OpenCV - 如何在iOS上对图像应用自适应阈值

13

我在尝试对下面展示的A4纸图像应用自适应阈值处理:

我使用以下代码来进行图像处理:

+ (UIImage *)processImageWithOpenCV:(UIImage*)inputImage {
    cv::Mat cvImage = [inputImage CVMat];
    cv::Mat res;
    cv::cvtColor(cvImage, cvImage, CV_RGB2GRAY);
    cvImage.convertTo(cvImage,CV_32FC1,1.0/255.0);
    CalcBlockMeanVariance(cvImage,res);
    res=1.0-res;
    res=cvImage+res;
    cv::threshold(res,res, 0.85, 1, cv::THRESH_BINARY);
    cv::resize(res, res, cv::Size(res.cols/2,res.rows/2));
    return [UIImage imageWithCVMat:cvImage];
}

void CalcBlockMeanVariance(cv::Mat Img,cv::Mat Res,float blockSide=13) // blockSide - the parameter (set greater for larger font on image)
{
cv::Mat I;
Img.convertTo(I,CV_32FC1);
Res=cv::Mat::zeros(Img.rows/blockSide,Img.cols/blockSide,CV_32FC1);
cv::Mat inpaintmask;
cv::Mat patch;
cv::Mat smallImg;
cv::Scalar m,s;

for(int i=0;i<Img.rows-blockSide;i+=blockSide)
{
    for (int j=0;j<Img.cols-blockSide;j+=blockSide)
    {
        patch=I(cv::Rect(j,i,blockSide,blockSide));
        cv::meanStdDev(patch,m,s);
        if(s[0]>0.01) // Thresholding parameter (set smaller for lower contrast image)
        {
            Res.at<float>(i/blockSide,j/blockSide)=m[0];
        }else
        {
            Res.at<float>(i/blockSide,j/blockSide)=0;
        }
    }
}

cv::resize(I,smallImg,Res.size());

cv::threshold(Res,inpaintmask,0.02,1.0,cv::THRESH_BINARY);

cv::Mat inpainted;
smallImg.convertTo(smallImg,CV_8UC1,255);

inpaintmask.convertTo(inpaintmask,CV_8UC1);
inpaint(smallImg, inpaintmask, inpainted, 5, cv::INPAINT_TELEA);

cv::resize(inpainted,Res,Img.size());
Res.convertTo(Res,CV_8UC3);

}
尽管输入的图像是灰度的,但输出的图像呈现出黄色,如下所示: 我猜测在cv::Mat和UIImage之间的转换中发生了某些问题,导致了彩色图像,但我无法找出如何解决这个问题。
请忽略状态栏,因为这些图片是iOS应用程序的截图。
更新: 我已经尝试使用CV_8UC1而不是CV_8UC3来进行Res.convertTo(),并添加了cvtColor(Res, Res, CV_GRAY2BGR);,但结果仍然非常相似。
这可能是cv::mat和UIImage之间的转换导致的问题吗?
我希望我的图像像下面显示的那样。

你已经将 Res 初始化为 CV_32FC1,然后使用 Res.convertTo(Res,CV_8UC3); 将其转换为 uchar,这似乎有些可疑,请尝试使用 Res.convertTo(Res,CV_8UC1); 并稍后使用 cvtColor - ZdaR
@ZdaR,您能详细说明一下使用cvtColor的含义吗?我尝试了CV_8UC1 后跟着cvtColor(Res, Res, CV_GRAY2BGR);。但是,我仍然得到非常相似的结果。还有其他建议吗? - Taichi Kato
2个回答

5

您可以使用OpenCV框架并实现以下代码

 +(UIImage *)blackandWhite:(UIImage *)processedImage
    {
        cv::Mat original = [MMOpenCVHelper cvMatGrayFromAdjustedUIImage:processedImage];

        cv::Mat new_image = cv::Mat::zeros( original.size(), original.type() );

        original.convertTo(new_image, -1, 1.4, -50);
        original.release();

        UIImage *blackWhiteImage=[MMOpenCVHelper UIImageFromCVMat:new_image];
        new_image.release();

        return blackWhiteImage;
    }

+ (cv::Mat)cvMatGrayFromAdjustedUIImage:(UIImage *)image
{
    cv::Mat cvMat = [self cvMatFromAdjustedUIImage:image];
    cv::Mat grayMat;
    if ( cvMat.channels() == 1 ) {
        grayMat = cvMat;
    }
    else {
        grayMat = cv :: Mat( cvMat.rows,cvMat.cols, CV_8UC1 );
        cv::cvtColor( cvMat, grayMat, cv::COLOR_BGR2GRAY );
    }
    return grayMat;
}

+ (cv::Mat)cvMatFromAdjustedUIImage:(UIImage *)image
{
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;

    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels

    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to backing data
                                                    cols,                       // Width of bitmap
                                                    rows,                       // Height of bitmap
                                                    8,                          // Bits per component
                                                    cvMat.step[0],              // Bytes per row
                                                    colorSpace,                 // Colorspace
                                                    kCGImageAlphaNoneSkipLast |
                                                    kCGBitmapByteOrderDefault);

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
    CGContextRelease(contextRef);
    return cvMat;
}


+ (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];

    CGColorSpaceRef colorSpace;

    if (cvMat.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }

    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

    CGImageRef imageRef = CGImageCreate(cvMat.cols,                                     // Width
                                        cvMat.rows,                                     // Height
                                        8,                                              // Bits per component
                                        8 * cvMat.elemSize(),                           // Bits per pixel
                                        cvMat.step[0],                                  // Bytes per row
                                        colorSpace,                                     // Colorspace
                                        kCGImageAlphaNone | kCGBitmapByteOrderDefault,  // Bitmap info flags
                                        provider,                                       // CGDataProviderRef
                                        NULL,                                           // Decode
                                        false,                                          // Should interpolate
                                        kCGRenderingIntentDefault);                     // Intent

    UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);

    return image;
}

它在我这里运行正常,检查你的文档输出


我已经编写了完整的工作代码..,只需将您的图像传递给+(UIImage *)blackandWhite:(UIImage *)processedImage方法中的processedImage参数。 - Zღk

3

试试这个:

+ (UIImage *)processImageWithOpenCV:(UIImage*)inputImage {
    cv::Mat cvImage = [inputImage CVMat];
    threshold(cvImage, cvImage, 128, 255, cv::THRESH_BINARY);
    return [UIImage imageWithCVMat:cvImage];
}

Result image:


虽然这是一种阈值处理方法并且可以工作,但我想实现自适应阈值处理,因为它会带来更好的结果 - 自适应阈值处理的具体细节可以在这里找到:http://hanzratech.in/2015/01/21/adaptive-thresholding.html。 - Taichi Kato

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接