二维码的生成与识别
刚刚写了一篇扫描二维码的文章,就想着怎么样才能实现像微信那样的识别图片中的二维码呢?百度一下,发现基本上都是利用ZXing或ZBar来实现的,难道系统就没有方法吗?于是再继续找,终于发现了使用CoreImage类来实现二维码的识别。
CoreImage简介
iOS提供的CoreImage框架非常强大,简直可以用来完全实现自己的”PhotoShop”,当然我本人是只会点皮毛。现在就利用CoreImage中的CIFilter和CIDetector来实现二维码的生成和识别。
生成二维码
生成滤镜
生成二维码,主要是使用CIFilter类,该类代表了各种滤镜,利用该类的方法,可生成对应的滤镜:
+ (nullable CIFilter *) filterWithName:(NSString *) name;
查询滤镜信息
那怎么知道有哪些滤镜可以使用呢?该类还提供了另外一个方法,供我们查询可以使用哪些滤镜:
+ (CI_ARRAY(NSString*) *)filterNamesInCategory:(nullable NSString *)category;
该方法会返回一个包含指定分类的可用滤镜集合,那么问题又来了,我们又如何知道有哪些分类呢?滤镜名称只是一个字符串,在头文件中并没有任何相关的属性定义,但是分类是有的,对应请查看CIFilter.h
/* Categories */
CORE_IMAGE_EXPORT NSString * const kCICategoryDistortionEffect;
CORE_IMAGE_EXPORT NSString * const kCICategoryGeometryAdjustment;
CORE_IMAGE_EXPORT NSString * const kCICategoryCompositeOperation;
CORE_IMAGE_EXPORT NSString * const kCICategoryHalftoneEffect;
CORE_IMAGE_EXPORT NSString * const kCICategoryColorAdjustment;
CORE_IMAGE_EXPORT NSString * const kCICategoryColorEffect;
CORE_IMAGE_EXPORT NSString * const kCICategoryTransition;
CORE_IMAGE_EXPORT NSString * const kCICategoryTileEffect;
CORE_IMAGE_EXPORT NSString * const kCICategoryGenerator;
CORE_IMAGE_EXPORT NSString * const kCICategoryReduction NS_AVAILABLE(10_5, 5_0);
CORE_IMAGE_EXPORT NSString * const kCICategoryGradient;
CORE_IMAGE_EXPORT NSString * const kCICategoryStylize;
CORE_IMAGE_EXPORT NSString * const kCICategorySharpen;
CORE_IMAGE_EXPORT NSString * const kCICategoryBlur;
CORE_IMAGE_EXPORT NSString * const kCICategoryVideo;
CORE_IMAGE_EXPORT NSString * const kCICategoryStillImage;
CORE_IMAGE_EXPORT NSString * const kCICategoryInterlaced;
CORE_IMAGE_EXPORT NSString * const kCICategoryNonSquarePixels;
CORE_IMAGE_EXPORT NSString * const kCICategoryHighDynamicRange;
CORE_IMAGE_EXPORT NSString * const kCICategoryBuiltIn;
CORE_IMAGE_EXPORT NSString * const kCICategoryFilterGenerator NS_AVAILABLE(10_5, 9_0);
我们要生成二维码,可以使用kCICategoryGenerator,查看到该分类下面有一个CIQRCodeGenerator,别看这家伙的名字长的像一个类,其实就是一个字符串,所以在传入的时候只需以字符串的形式传入就行了。 个人感觉苹果这种做法有点操蛋,简单的找一个滤镜的名字,都这么麻烦。代码如下所示:
_QRCodeFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
如果说这就觉得操蛋了,那接下来还有更加麻烦的事。要想使用CIFilter生成二维码,需要设置一些属性值,方法是通过KVC,setValue:forKey,我们需要设置两个属性,那么同名字一样,我哪知道要设置哪些属性?其实方法都是一样的,CIFilter还提供了inputKeys,outputKeys,attributes属性供我们查询,我们可以将attributes的内容输出来,如下所示:
{
"CIAttributeFilterAvailable_Mac" = "10.9";
"CIAttributeFilterAvailable_iOS" = 7;
CIAttributeFilterCategories = (
CICategoryGenerator,
CICategoryStillImage,
CICategoryBuiltIn
);
CIAttributeFilterDisplayName = "QRCode Generator";
CIAttributeFilterName = CIQRCodeGenerator;
CIAttributeReferenceDocumentation = "http://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIQRCodeGenerator";
inputCorrectionLevel = {
CIAttributeClass = NSString;
CIAttributeDefault = M;
CIAttributeDescription = "QRCode correction level L, M, Q, or H.";
CIAttributeDisplayName = CorrectionLevel;
};
inputMessage = {
CIAttributeClass = NSData;
CIAttributeDisplayName = Message;
};
}
在这些属性里面,我们可以看到,上面一些都是苹果设置的属性,我们需要设置的就是最后两个属性inputCorrectionLevel和inputMessage,
inputCorrectionLevel:该参数应该是代表生成二维码的精度(自己猜的)
inputMessage:该参数代表要生成的二维码中所包含的信息
从CIAttributeClass字段中,我们可以见到,inputCorrectionLevel的值是NSString类型,并且根据CIAttributeDescription知道可选的值有L,M,Q,H等。
而inputMessage的类型则是NSData。
获取二维码图像
设置这两个属性之后,我们就可以通过以下属性来获取处理后的图片:
@property (readonly, nonatomic, nullable) CIImage *outputImage NS_AVAILABLE(10_10, 5_0);
该方法返回一个CIImage,可以转换成UIImage并设置在UIImageView上。代码如下所示:
[_QRCodeFilter setValue:@"H" forKey:@"inputCorrectionLevel"];
[_QRCodeFilter setValue:[_messageTextField.text dataUsingEncoding:NSUTF8StringEncoding] forKey:@"inputMessage"];
[_QRCodeImageView setImage:[UIImage imageWithCIImage:_QRCodeFilter.outputImage]];
至此,就能成功生成一个二维码。接下来就是利用CIDetector来识别二维码。
识图二维码
相对起使用CIFilter生成二维码来说,识别二维码可要简单太多了,我们只需要实例化CIDetector类,并采用以下方法,便可以获取:
- (CI_ARRAY(CIFeature*) *)featuresInImage:(CIImage *)image
NS_AVAILABLE(10_7, 5_0);
该方法返回包含CIFeature类的数组,对于二维码来说,该数组中的类型应该是CIQRCodeFeature类的实例,我们只需要通过其中的messageString属性即可以获取到二维码中的内容。
-(void)longPressed:(UILongPressGestureRecognizer*)recognizer {
CIImage* image = _QRCodeFilter.outputImage;
NSArray* array =[_QRCodeDector featuresInImage:image];
for (CIFeature* feature in array) {
if([feature isKindOfClass:[CIQRCodeFeature class]]) {
CIQRCodeFeature* QRCodeFeature = (CIQRCodeFeature*)feature;
[[[UIAlertView alloc] initWithTitle:@"二维码" message:QRCodeFeature.messageString delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil] show];
return;
}
}
return ;
}
效果演示
最终的效果演示如下图所示: