长按图片识别二维码在移动端是很常见的操作,长按后需要对图片进行识别,并且将二维码中所包含的数据解码出来。在我们的业务场景中,是通过点击图片进入大图预览页面。长按大图预览的图片,会识别图片中的二维码,并且显示有跳转按钮,提示用户可以跳转二维码对应的页面。
但是,在现有业务场景中,要求图片中二维码不能在视觉上占据太大的位置,所以只能以很小的尺寸显示在下面。为了更好的配合公司现有业务,保证对图片中二维码的识别率,所以需要对二维码识别进行优化。
方案总体分为探测和识别两个核心流程,探测流程主要由图像处理算法,以及
OpenCV
来实现,识别流程主要由系统AVFoundation
库的CIDetector
来实现。先将二维码所在区域探测出来,随后对这个区域进行识别增强的处理,以实现模糊、较小的二维码的识别。
因为不是每一张图片上都有二维码,探测的意义在于,查找图片中是否有二维码,以及二维码在图片中的位置。从而进行后续的针对性处理。以下,任何一步探测有结果,都将进入识别流程中,并且将探测到的位置传给识别方法。
OpenCV
的cvtColor
函数,将四通道的RGBA
图片,转换为单通道的灰度图。20%
的右半部分,clone
到一个新的Mat
对象中,并且进行3.5
倍的resize
。将得到后的灰度大图调用detect
进行探测。20%
的左半部分,clone
到一个新的Mat
对象中,并且进行3.5
倍的resize
。将得到后的灰度大图调用detect
进行探测。需要注意的是,在探测方案中,为什么选取下面左右两边的20%
着重进行探测。是因为根据对公司实际业务的调研,绝大多数的二维码都是在图片的右下角位置,其次是左下角。以公司业务为例,目前没见过将二维码放在图片中间的场景。
resize
后的大图,对这些大图识别率会更高。15
的外边距,以保证二维码Quiet Zone
的特性。AVFoundation
的CIDetector
进行识别,并获取识别后的字符串。为什么不把探测和识别都交给OpenCV
来做。这是因为经过我的测试,我发现OpenCV
的识别率较低,远不如CIDetector
的识别率。所以,只将探测部分交给OpenCV
,但不让OpenCV
去识别二维码。
根据OpenCV
的detect
函数的源码进行查看,发现OpenCV
的探测是基于定位符号进行探测的,分为横向和纵向两个方向进行探测,使用OpenCV
进行探测是不错的选择。但是OpenCV
识别率并不高,所以用OpenCV
进行探测,结合AVFoundation
进行识别的方案,是一个比较不错的策略。
方案总体代码量较大,这里列出了一些主要方法的代码实现。探测方法内部实现,会进行不同程度的增强扫描和识别。方案中使用了一些OpenCV
的API
,可以通过OpenCV官方文档了解下API
的定义和调用。
把传入的图片转为OpenCV
可以识别的Mat
的灰度图,灰度图色彩通道只有一个,在进行后续计算上,会节省很多性能。随后进入后续的探测部分,探测到二维码后,会将二维码拼接业务参数,并在主线程中返回给调用方。
方法的核心逻辑有两部分,一是将UIImage
转为OpenCV
类型的Mat
对象,二是将四个颜色通道的彩色图,转为单个颜色通道的灰度图。二值图和灰色图是不同的,灰色图是单个通道,每个单位占8
位,表示范围是0~255
。二值图只有0~1
的展示,所以识别速度会相对快一些。
这里简要讲一下颜色通道的概念,CV_8UC4
表示RGBA
四颜色通道,占用32
位空间。同样的,也有三颜色通道RGB
,以及两个通道和单通道,两个通道的CV_8UC2
我没用过,不知道什么场景下会需要。cvtColor
函数是OpenCV
中进行颜色空间转换的函数,可以将彩色图修改为灰度图。
CGFloat rows = sourceImage.size.height;
CGFloat cols = sourceImage.size.width;
cv::Mat cvMat(rows, cols, CV_8UC4);
CGColorSpaceRef colorSpace = CGImageGetColorSpace(sourceImage.CGImage);
CGContextRef context = CGBitmapContextCreate(cvMat.data,
cols,
rows,
8,
cvMat.step[0],
colorSpace,
kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault);
if (context == NULL) {
return cvMat;
}
CGContextDrawImage(context, CGRectMake(0, 0, cols, rows), sourceImage.CGImage);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
cv::Mat grayMat;
cv::cvtColor(cvMat, grayMat, cv::COLOR_BGR2GRAY);
return grayMat;
下面代码是对左下和右下两个部分进行探测的逻辑,代码中scale
代表局部放大的倍数。根据公司业务,先从右下角进行放大及探测。放大需要获取到像素的行数和列数,这些Mat
提供了对应的API
。
下面是核心逻辑的梳理。
rowRange
和colRange
获取到需要增强扫描的像素,例如右下角区域的横排和竖排的像素。clone
将像素取出,并赋值给Mat
。resize
函数扩大到对应的倍数。需要注意的是,在下面方法的第二段,对右下角进行了强制识别。是一个容错处理,属于“闭眼识别”。第一步无论是否有探测的结果,都会对右下角进行二维码的识别。因为在测试中,对于非常小的二维码,会出现对于resize
后的灰度图,探测不到但是能识别到的情况。所以,增加了右下角没有探测到,但依然进行强制识别的逻辑。
CGFloat scale = 3.5f;
cv::Mat copyMat = grayMat.rowRange(grayMat.rows * 0.8, grayMat.rows).colRange(grayMat.cols * 0.5, grayMat.cols).clone();
cv::Mat resizeMat;
cv::resize(copyMat, resizeMat, cv::Size(grayMat.cols * scale * 0.5, grayMat.rows * 0.2 * scale));
NSString *result = [self qrcodeQRCodeForGrayMat:resizeMat];
/// 闭眼识别
if (!result.length) {
UIImage *image = [self UIImageFromCVMat:resizeMat];
result = [self readQRCodeWithImage:image
detectorAccuracy:CIDetectorAccuracyHigh];
}
if (!result.length) {
copyMat = grayMat.rowRange(grayMat.rows * 0.8, grayMat.rows).colRange(grayMat.cols * 0.f, grayMat.cols * 0.5).clone();
cv::resize(copyMat, resizeMat, cv::Size(grayMat.cols * scale * 0.5, grayMat.rows * 0.2 * scale));
result = [self qrcodeQRCodeForGrayMat:resizeMat];
}
return result;
识别方法会接收传入的二维码坐标系,裁剪出二维码的位置并做相应的处理,随后交给AVFoundation
去识别。当探测不到时,不会进入识别的环节。
当探测到二维码时,可以通过output
获取二维码的四个点,顺序是左上、右上、右下、左下,顺时针存储。在原有的四个点外面,加上15
个单位的Quiet Zone
,随后会将这部分图片裁剪出来,并交给AVFoundation
去识别。加入Quiet Zone
的原因在于,有Quiet Zone
的二维码,识别率会比没有的好。
vector<cv::Point> output;
if (output.empty()) {
return nil;
}
/// 左上
cv::Point point0 = output[0];
/// 右下
cv::Point point2 = output[2];
CGRect rect = CGRectMake(point0.x, point0.y, point2.x - point0.x, point2.y - point0.y);
CGRect insetRect = CGRectInset(rect, -15, -15);
/// rect合法性检查
if (insetRect.origin.x > 0 && insetRect.origin.y > 0 && insetRect.size.width > 0 && insetRect.size.height > 0) {
rect = insetRect;
}
UIImage *image = [self UIImageFromCVMat:grayMat];
image = [self drawInRect:rect sourceImage:image];
NSString *result = [self readQRCodeWithImage:image
detectorAccuracy:CIDetectorAccuracyLow];
return result;
在iOS
中进行OpenCV
开发的过程中,UIImage
和Mat
的相互转换是经常需要的。UIImage
转Mat
在生成灰度图的过程中已经涉及,下面的方法是将Mat
转为UIImage
。核心逻辑就是将Mat
的data
数据,通过CGImageCreate
函数创建CGImage
完成的。
CGColorSpaceRef colorSpace;
CGBitmapInfo bitmapInfo;
size_t elemsize = cvMat.elemSize();
if (elemsize == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;
}
else {
colorSpace = CGColorSpaceCreateDeviceRGB();
bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= (elemsize == 4) ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNone;
}
NSData *data = [NSData dataWithBytes:cvMat.data length:elemsize * cvMat.total()];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
/// 根据Mat创建CGImage
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
bitmapInfo, // bitmap info
provider, // CGDataProviderRef
NULL, // decode
false, // should interpolate
kCGRenderingIntentDefault // intent
);
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return finalImage;
二维码识别率的统计比较困难,因为场景是在长按图片识别二维码,但图片中是否有二维码,我们并不知道。并且,用户长按操作可能是为了保存图片,并非识别二维码。
所以,数据分析通过A/B
测的方式进行,通过分桶方式实现,两个桶分别为50%
的用户量。通过这种方式,可以得出一个基本准确的识别数据。
数据从两个维度来统计,识别速度和识别率,数据如下。
6.8%
12%
识别速度OpenCV
表现并不是很明显,因为只是通过灰度图的方式提升了识别速度,但如果到了探测的阶段,对速度还是有一定影响的,收益为正数就很不错了。
识别率提升还是不错的,因为是A/B
方案的对比,我们并不知道单个方案的真实识别率。但经过我们自测,运营常用的几个存在复杂二维码的账号中,挨个试二维码的识别,自测识别率为100%
。
除了灰度图,也考虑过通过OTSU
算法将灰度图转为二值图,但是发现二值图在图片清晰度不够的情况下,定位符号比较模糊的位置会被转换为白色。这样,定位符号就会缺一个角,不能构成特定比例,导致识别率出现明显下降。既然二值图的方案都不可行,那就别考虑识别轮廓了,所以就乖乖的用灰度图做识别了。
后续计划加入中值滤波后,再进行OTSU
算法做二值化,这样转换后的二值图效果会好很多。
6月5日,一张券商降薪截图在社交媒体疯传。截图提到,当日上午,某中字头头部券商召开大会,除了MD外全员降薪,且降薪不只是降奖金,而是直接降底薪。按照职级不同,SA1降6K,SA3降8K,VP降8K—10K。据了解,降薪大概率整体属实,但具体幅度有所差异,且不同区域、不同业务条线目前掌握的降薪情况也不尽相同。
今日,蔚来 CEO 李斌在 2023 高通汽车技术与合作峰会上爆料,蔚来第二代技术平台的全系车型已标配第三代骁龙座舱平台。
Meta公司周一(5月22日)推出了一个开源AI语言模型——大规模多语言语音(Massively Multilingual Speech, MMS)模型,可以识别和产生1000多种语言的语音——比目前可用的模型增加了10倍。研究人员表示,他们的模型可以转换1000多种语言,但能识别4000多种语言。
歌手孙燕姿在更新动态中回应了近日引发争议的“顶流AI歌手孙燕姿”,笑称粉丝已经接受她是“冷门”歌手,而AI成为了目前的顶流。
5月31日晚,荣耀方面对澎湃新闻记者表示,上海荣耀智能科技开发有限公司是荣耀位于上海的研究所,是荣耀在中国的5个研究中心之一,重点方向在终端侧核心软件、图形算法、通信、拍照等方面研究开发工作。荣耀强调,坚持以用户为中心,开放创新,与全球合作伙伴一起为用户提供最佳产品解决方案。
据北京市市场监督管理局公示信息,5月24日,苹果电子产品商贸(北京)有限公司因发布虚假广告被北京市东城区市场监督管理局处以20万元的行政处罚。
据外媒5月24日消息,全球最大的个人电脑制造商联想表示,在2023年1-3月期间,该公司裁员了约5%,这是由于PC市场不景气导致的。
日前,有网络博主号称拍摄到了小米首款汽车MS11的高清视频。从视频中可以看出,新车依旧包裹大面积的伪装,据该博主称,他之所以确定这是小米汽车,是因为靠近观察之后,发现它的三角形大灯轮廓和其最初手绘的小米汽车假想图几乎一模一样。
超过 350 名从事人工智能工作的高管、研究人员和工程师签署了这份由非盈利组织人工智能安全中心发布的公开信,认为人工智能具备可能导致人类灭绝的风险,应当将其视为与流行病和核战争同等的社会风险。
日前,以押注“颠覆性创新”著称的ARK Invest创始人Cathie Wood在接受媒体采访时表示,软件提供商将是人工智能狂潮的下一个受益板块。英伟达每卖出1美元的硬件,软件供应商SaaS供应商就会产生8美元的收入。
据报道,阿里巴巴研究员吴翰清已于近期离职,钉钉显示其离职时间是5月19日。在阿里内部,研究员的职级为P10。据消息人士透露,吴翰清离职后,选择AI短视频赛道创业,已经close一轮融资。对于上述消息,截至发稿,阿里尚未回应。
阿里巴巴集团官微宣布,2023年六大业务集团总计需新招15000人,其中校招超过3000人。同时表示,“近日,关于淘宝天猫、阿里云、菜鸟、本地生活各个业务裁员谣言传得很厉害,但谣言就是谣言。我们的招聘正在紧锣密鼓的进行。”
“现今每一个存在的应用都将被AI 2.0重构,我觉得整个AI大模型带来的机遇和技术浪潮,会比过去Windows和安卓大10倍。”李开复表示。
苹果发布Vision Pro头显,正式宣布开启空间计算时代;苹果还发布新款MacBook Air,新款Mac Studio,并展示了iOS17、iPadOS 17、macOS Sonoma和watchOS10等新系统;Vision Pro头显售价3499美元,将于2024年初正式在美国市场发售;华尔街并不看好Vision Pro,苹果股价周一创历史新高后由涨转跌。
5月25日,长城汽车就比亚迪秦PLUS DM-i、宋PLUS DM-i采用常压油箱,涉嫌整车蒸发污染物排放不达标的问题进行举报。
近日,一个名为“贾跃亭”的抖音账号悄然出现,带有“FF创始人、合伙人、首席产品及用户生态官, LeEco 乐视创始人”等标签,IP 地址显示为美国。
5月29日消息,继上周远超预期的财报业绩预测引得股价和市值史诗级暴涨后,今日,英伟达(NVIDIA)创始人兼CEO黄仁勋穿着标志性的皮衣,意气风发地出现在台北电脑展COMPUTEX 2023上,在主题演讲期间先是现场给自家显卡带货,然后一连公布涉及加速计算和人工智能(AI)的多项进展。
近日,苹果位于天猫的Apple Store官方旗舰店挂出直播预告,表示将在5月31日晚19时开启官方直播,这也是苹果官方在电商平台的全球首次直播。
前京东集团副总裁、京东探索研究院副院长梅涛自今年初离职后,确认在 AI 领域创业,成立生成式 AI 公司 HiDream.ai。