本文欢迎转载,转载请注明出处:
https://my.oschina.net/227/blog/796485
前言
在开发项目中,有的时候需要用户上传照片到后台服务器审核,如果照片不清晰的话,那就得用户重新上传。有没有办法在上传之前就判断照片是否清晰呢?为了解决这个问题,我们可以用到一个图像处理框架“openCV”。"openCV"非常强大,我们这里只是用了它的很小一点点。
openCV官网:http://opencv.org/
思路:
如果读者了解信号处理,就会知道最直接的方法就是计算图片的快速傅里叶变换,然后查看高低频的分布。如果图片有少量的高频成分,那么该图片就可以被认为是模糊的。然而,区分高频量多少的具体阈值却是十分困难的,不恰当的阈值将会导致极差的结果。
查阅了相关资料,了解到了目前比较好的方法是“拉普拉斯方差算法”。
简单来说就是只需要将图片中的某一通道(一般用灰度值)用的拉普拉斯掩模做卷积运算,得到的像素矩阵,在把矩阵中的数值求方差。如果图像越清晰,边缘越锐利,像素之间的差别越大,则方差越大。反之图像模糊,边缘之间像素差别小,那方差越小。
步骤一:
去openCV官网下载给iOS用的“opencv2.framework”包,并导入xcode工程中
地址: (输入浏览器回车后过几秒钟就会自动下载)
https://sourceforge.net/projects/opencvlibrary/files/opencv-ios/2.4.13/opencv2.framework.zip/download
步骤二:
在需要使用的地方添加头文件“#import”,并且把当前的“.m”的objective-c文件改成“.mm”的objc/c++文件,或者在PCH里添加头文件,注意在PCH里添加的时候要如下所示#ifdef __cplusplus #import#endif
这样做的原因是openCV的库是c++文件,加入这段表明这个库只有在c++文件导入,作用和“#ifdef __OBJC__”的效果类似。上面说改成“.mm”也是这个原因。
步骤三:
把UIImage图像转换成cvMat类型,让openCV可以处理
+ (cv::Mat)cvMatFromUIImage:(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 (color channels + alpha) CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to data cols, // Width of bitmap rows, // Height of bitmap 8, // Bits per component cvMat.step[0], // Bytes per row colorSpace, // Colorspace kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault); // Bitmap info flags 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); // Creating CGImage from cv::Mat CGImageRef imageRef = CGImageCreate(cvMat.cols, //width cvMat.rows, //height 8, //bits per component 8 * cvMat.elemSize(), //bits per pixel cvMat.step[0], //bytesPerRow colorSpace, //colorspace kCGImageAlphaNone|kCGBitmapByteOrderDefault,// bitmap info provider, //CGDataProviderRef NULL, //decode false, //should interpolate kCGRenderingIntentDefault //intent ); // Getting UIImage from CGImage UIImage *finalImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); CGDataProviderRelease(provider); CGColorSpaceRelease(colorSpace); return finalImage; }
步骤四:
进行计算,代码如下
//方差参考值 const double delta = 100; + (BOOL) whetherTheImageBlurry:(UIImage*)image{ unsigned char *data; int height,width,step; int Iij; double Iave = 0, Idelta = 0; cv::Mat mat = [OpenCVExtension cvMatFromUIImage:image]; if(!mat.empty()){ cv::Mat gray; cv::Mat outGray; // 将图像转换为灰度显示 cv::cvtColor(mat,gray,CV_RGB2GRAY); // 用openCV的Laplacian函数做灰度图做拉普拉斯计算,得到outGray cv::Laplacian(gray, outGray, gray.depth()); //将得到的outGray转化为图片信息类IplImage IplImage ipl_image(outGray); data = (uchar*)ipl_image.imageData; //图片像素信息 height = ipl_image.height; //图片像素高度 width = ipl_image.width; //图片像素宽度 step = ipl_image.widthStep; //排列的图像行大小,以字节为单位 for(int i=0;i