[iOS OpenCV的使用,灰度和二值化]

时间:2022-04-26 15:27:22

看网上方法很多,但版本都不够新,我看了网上一些知识,总结了下,来个最新版Xcode6.1的.

最近主要想做iOS端的车牌识别,所以开始了解OpenCV。有兴趣的可以跟我交流下哈。

一.Opencv的使用:

  步骤:

  1.从官网下载iOS版本的Opencv2.framework。

  2.拖进工程,选择copy items if needed

  3.进入building settings,设置Framework SearchPath:

     设置成$(PROJECT_DIR)/Newtest,这个Newtest是你的项目名,主要是为了定位到你存放的Opencv2.framework所在位置。

  4.使用Opencv的方式:第(1)种全局pch:(不推荐)新建pch文件,修改成:

              #ifdef __cplusplus

              #import <opencv2/opencv.hpp>

              #endif

                并在building setting里的 Incease Sharing of Precompiled Headers项目处:

               设置成$(PROJECT_DIR)/Newtest,同理,这个Newtest是你的项目名,主要是为了定位到你存放的PCH文件所在位置。

              PCH文件以前建工程默认生成,是全局性质的import。Xcode6不再自动生成。苹果引导开发者在某个类要用时才用。

             第(2)种:在需要的地方#import <opencv2/opencv.hpp>

                这里的重点是:使用opencv的类名一定要改成.mm!!

                        比如你专门写了各一个处理图片的类,Imageprocess。可以在.h里加入。

二:灰度化和二值化的主要实现过程:

  其实过程就是这样:

  UIImage(iOS图像类)-> cv::Mat(OpenCV图像类) -> Opencv灰度或二值处理函数 -> UIImage

三:Opencv类Imageprocess代码参考:

Imageprocess.h

//
// Imageprocess.h
// Chepaishibie
//
// Created by shen on 15/1/28.
// Copyright (c) 2015年 shen. All rights reserved.
// #import <Foundation/Foundation.h>
#import <opencv2/opencv.hpp>
#import <UIKit/UIKit.h> @interface Imageprocess : UIViewController - (cv::Mat)cvMatFromUIImage:(UIImage *)image; - (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat; - (IplImage *)CreateIplImageFromUIImage:(UIImage *)image; - (UIImage *)UIImageFromIplImage:(IplImage *)image; - (UIImage *)Grayimage:(UIImage *)srcimage; - (UIImage *)Erzhiimage:(UIImage *)srcimage; int Otsu(unsigned char* pGrayImg , int iWidth , int iHeight); @end

Imageprocess.mm 里面包含了很多函数:

主要是 UIImage->cv::Mat ,cv::Mat->UIImage,UIImage->IplImage,IplImage->UIImage, 灰度化,二值化等,还有个OSTU计算阈值的方法。

//
// Imageprocess.mm
// Chepaishibie
//
// Created by shen on 15/1/28.
// Copyright (c) 2015年 shen. All rights reserved.
// #import "Imageprocess.h" @implementation Imageprocess #pragma mark - opencv method
// UIImage to cvMat
- (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 CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to data
cols, // Width of bitmap
rows, // Height of bitmap
, // Bits per component
cvMat.step[], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags CGContextDrawImage(contextRef, CGRectMake(, , cols, rows), image.CGImage);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace); return cvMat;
} // CvMat to UIImage
-(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
CGColorSpaceRef colorSpace; if (cvMat.elemSize() == ) {
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
, //bits per component
* cvMat.elemSize(), //bits per pixel
cvMat.step[], //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;
} //由于OpenCV主要针对的是计算机视觉方面的处理,因此在函数库中,最重要的结构体是IplImage结构。
// NOTE you SHOULD cvReleaseImage() for the return value when end of the code.
- (IplImage *)CreateIplImageFromUIImage:(UIImage *)image {
// Getting CGImage from UIImage
CGImageRef imageRef = image.CGImage; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Creating temporal IplImage for drawing
IplImage *iplimage = cvCreateImage(
cvSize(image.size.width,image.size.height), IPL_DEPTH_8U,
);
// Creating CGContext for temporal IplImage
CGContextRef contextRef = CGBitmapContextCreate(
iplimage->imageData, iplimage->width, iplimage->height,
iplimage->depth, iplimage->widthStep,
colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault
);
// Drawing CGImage to CGContext
CGContextDrawImage(
contextRef,
CGRectMake(, , image.size.width, image.size.height),
imageRef
);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace); // Creating result IplImage
IplImage *ret = cvCreateImage(cvGetSize(iplimage), IPL_DEPTH_8U, );
cvCvtColor(iplimage, ret, CV_RGBA2BGR);
cvReleaseImage(&iplimage); return ret;
} // NOTE You should convert color mode as RGB before passing to this function
- (UIImage *)UIImageFromIplImage:(IplImage *)image {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Allocating the buffer for CGImage
NSData *data =
[NSData dataWithBytes:image->imageData length:image->imageSize];
CGDataProviderRef provider =
CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
// Creating CGImage from chunk of IplImage
CGImageRef imageRef = CGImageCreate(
image->width, image->height,
image->depth, image->depth * image->nChannels, image->widthStep,
colorSpace, kCGImageAlphaNone|kCGBitmapByteOrderDefault,
provider, NULL, false, kCGRenderingIntentDefault
);
// Getting UIImage from CGImage
UIImage *ret = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return ret;
} #pragma mark - custom method // OSTU算法求出阈值
int Otsu(unsigned char* pGrayImg , int iWidth , int iHeight)
{
if((pGrayImg==)||(iWidth<=)||(iHeight<=))return -;
int ihist[];
int thresholdValue=; // „–÷µ
int n, n1, n2 ;
double m1, m2, sum, csum, fmax, sb;
int i,j,k;
memset(ihist, , sizeof(ihist));
n=iHeight*iWidth;
sum = csum = 0.0;
fmax = -1.0;
n1 = ;
for(i=; i < iHeight; i++)
{
for(j=; j < iWidth; j++)
{
ihist[*pGrayImg]++;
pGrayImg++;
}
}
pGrayImg -= n;
for (k=; k <= ; k++)
{
sum += (double) k * (double) ihist[k];
}
for (k=; k <=; k++)
{
n1 += ihist[k];
if(n1==)continue;
n2 = n - n1;
if(n2==)break;
csum += (double)k *ihist[k];
m1 = csum/n1;
m2 = (sum-csum)/n2;
sb = (double) n1 *(double) n2 *(m1 - m2) * (m1 - m2);
if (sb > fmax)
{
fmax = sb;
thresholdValue = k;
}
}
return(thresholdValue);
} -(UIImage *)Grayimage:(UIImage *)srcimage{
UIImage *resimage; //openCV二值化过程: /*
//1.Src的UIImage -> Src的IplImage
IplImage* srcImage1 = [self CreateIplImageFromUIImage:srcimage]; //2.设置Src的IplImage的ImageROI
int width = srcImage1->width;
int height = srcImage1->height;
printf("图片大小%d,%d\n",width,height); // 分割矩形区域
int x = 400;
int y = 1100;
int w = 1200;
int h = 600; //cvSetImageROI:基于给定的矩形设置图像的ROI(感兴趣区域,region of interesting)
cvSetImageROI(srcImage1, cvRect(x, y, w , h)); //3.创建新的dstImage1的IplImage,并复制Src的IplImage
IplImage* dstImage1 = cvCreateImage(cvSize(w, h), srcImage1->depth, srcImage1->nChannels);
//cvCopy:如果输入输出数组中的一个是IplImage类型的话,其ROI和COI将被使用。
cvCopy(srcImage1, dstImage1,0);
//cvResetImageROI:释放基于给定的矩形设置图像的ROI(感兴趣区域,region of interesting)
cvResetImageROI(srcImage1); resimage = [self UIImageFromIplImage:dstImage1];
*/ //4.dstImage1的IplImage转换成cvMat形式的matImage
cv::Mat matImage = [self cvMatFromUIImage:srcimage]; cv::Mat matGrey; //5.cvtColor函数对matImage进行灰度处理
//取得IplImage形式的灰度图像
cv::cvtColor(matImage, matGrey, CV_BGR2GRAY);// 转换成灰色 //6.使用灰度后的IplImage形式图像,用OSTU算法算阈值:threshold
//IplImage grey = matGrey; resimage = [self UIImageFromCVMat:matGrey]; /*
unsigned char* dataImage = (unsigned char*)grey.imageData;
int threshold = Otsu(dataImage, grey.width, grey.height);
printf("阈值:%d\n",threshold); //7.利用阈值算得新的cvMat形式的图像
cv::Mat matBinary;
cv::threshold(matGrey, matBinary, threshold, 255, cv::THRESH_BINARY); //8.cvMat形式的图像转UIImage
UIImage* image = [[UIImage alloc ]init];
image = [self UIImageFromCVMat:matBinary]; resimage = image;
*/ return resimage;
} -(UIImage *)Erzhiimage:(UIImage *)srcimage{ UIImage *resimage; //openCV二值化过程: /*
//1.Src的UIImage -> Src的IplImage
IplImage* srcImage1 = [self CreateIplImageFromUIImage:srcimage]; //2.设置Src的IplImage的ImageROI
int width = srcImage1->width;
int height = srcImage1->height;
printf("图片大小%d,%d\n",width,height);
// // 分割矩形区域
int x = 400;
int y = 1100;
int w = 1200;
int h = 600; //cvSetImageROI:基于给定的矩形设置图像的ROI(感兴趣区域,region of interesting)
cvSetImageROI(srcImage1, cvRect(x, y, w , h)); //3.创建新的dstImage1的IplImage,并复制Src的IplImage
IplImage* dstImage1 = cvCreateImage(cvSize(w, h), srcImage1->depth, srcImage1->nChannels);
//cvCopy:如果输入输出数组中的一个是IplImage类型的话,其ROI和COI将被使用。
cvCopy(srcImage1, dstImage1,0);
//cvResetImageROI:释放基于给定的矩形设置图像的ROI(感兴趣区域,region of interesting)
cvResetImageROI(srcImage1); resimage = [self UIImageFromIplImage:dstImage1];
*/ //4.dstImage1的IplImage转换成cvMat形式的matImage
cv::Mat matImage = [self cvMatFromUIImage:srcimage]; cv::Mat matGrey; //5.cvtColor函数对matImage进行灰度处理
//取得IplImage形式的灰度图像
cv::cvtColor(matImage, matGrey, CV_BGR2GRAY);// 转换成灰色 //6.使用灰度后的IplImage形式图像,用OSTU算法算阈值:threshold
IplImage grey = matGrey;
unsigned char* dataImage = (unsigned char*)grey.imageData;
int threshold = Otsu(dataImage, grey.width, grey.height);
printf("阈值:%d\n",threshold); //7.利用阈值算得新的cvMat形式的图像
cv::Mat matBinary;
cv::threshold(matGrey, matBinary, threshold, , cv::THRESH_BINARY); //8.cvMat形式的图像转UIImage
UIImage* image = [[UIImage alloc ]init];
image = [self UIImageFromCVMat:matBinary]; resimage = image; return resimage;
} @end

四:可能问题:

  1.出现'list' file not found:   检查类名是否改成.mm了!还不行的话,在Build Phases 中加入库:libc++.dylib 试试。

  2.arm64不支持的问题:在Building settings里Build Active Architecture Only改为No,然后下面Valid Architectures把arm64删了。

五:样例参考:有两个很好的例子,一个是二值,一个是图像匹配。

1.二值 https://github.com/zltqzj/ios_opencv_divide

2.图像匹配 https://github.com/jimple/OpenCVSample