年关集福的号角一吹响,日常社交用语里就多了一句:你有敬业福嘛?今年是集五福的第6年,这场搭载手机的年味仪式,让古早的“福”文化又登上新时代的C位。不难发现,我们拿着手机,不管面对的是笔端纤细的手写福,还是电子屏幕上五花八门的花体福,“扫福”功能都能轻易识别。那么,是什么技术实现扫福,又让扫福得以如此简单易操作,本文试着对扫福进行一次技术详解,看完这篇,没准你的app也能来一次“万物皆可扫”的有趣活动。

<span><span><strong><span><span style="color:black">“AR扫福”技术解读</span></span></strong></span></span>-LMLPHP

“AR扫福”技术解读

1、OCR原理

“AR扫福”加持的第一个技术是光学字符识别技术(Optical Character Recognition, OCR),手机等电子设备扫描任意载体上较为清晰的字符,通过对字符明暗的检测确定其形状,再通过字符形状识别翻译输出对应的文字。

常见的OCR应用

如今OCR技术已经深入生活的诸多方面。例如,身份证、护照、车牌号等证件识别,纸质文档扫描识别,时下一些在线答题的学习软件也凭借OCR实现了拍照搜题功能。

OCR的技术路线 

<span><span><strong><span><span style="color:black">“AR扫福”技术解读</span></span></strong></span></span>-LMLPHP

预处理环节的五大“门神”
(1)灰度化
字符输入设备后,在预处理环节,首先经过“灰度化”处理,降低复杂背景环境的干扰,只保留敏感度信息的图片。灰度化满足的公式一般为:Gray=0.299R+0.587G+0.114B
 
(2)二值化
图片灰度化后进行“二值化”做进一步背景简化,环节将图片简化为非黑即白的前景信息和背景信息,前景信息是需要读取的信息为为黑色,背景信息为白色。
 
(3)降噪
要完成更精确的识别还需要对图片进行降噪处理。图像噪声是指存在于图像数据中的不必要的或多余的干扰信息,图像噪声的来源在图像获取和传递过程中收到传感器材质、工作环境以及传输介质等的各种影响,都会产生。二值化后的字符周围小黑点越多图片噪声越大,影响字符的精确切割。所以二值化后再进行图片降噪成为必要。
 
(4)倾斜矫正
降噪处理后,为了方便后续的字符切割,还要进行一道倾斜校正的工序,即将歪斜的图片以字符的公正排布进行图片矫正。霍夫变换时最常用的矫正方法,基于二值图片进行直线检测,基本原理在于利用点与线的对偶性,将断续的字符连城一条直线,在进行直线水平矫正。
 
(5)字符切割
经过重重筛选,最后筛出具有明确清晰字符的图片,就可进行行切分和字符切分。为下一步字符识别和翻译输出做准备。
 
字符识别和翻译
以上一顿操作,提取出真实需要的内容,处理清晰化的字符再与模板库进行匹配,输出匹配结果。

2、用ML机器学习实现“扫福”功能

华为机器学习服务(HUAWEI ML Kit)提供文本识别能力,利用OCR技术,将拍摄到的文字提取出来,转换为文本信息。除了简体中文,该能力还支持在手机端识别日文、韩文、英文、西班牙文、葡萄牙文、意大利文、德文、法文和俄文,下面我们就来看看如何简单地实现这一强大功能。

在开始开发前,需要先配置HMS Core的Maven仓地址,具体可以参考:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides-V5/config-maven-0000001050040031-V5
之后将文本识别的基础SDK包和语言包添加到依赖中。

dependencies{
……
  // 引入基础SDK
  implementation 'com.huawei.hms:ml-computer-vision-ocr:2.0.5.300'
  // 引入中英文文字识别模型包
  implementation 'com.huawei.hms:ml-computer-vision-ocr-cn-model:2.0.5.300'
……
}

为了实现实时根据拍摄到的画面进行文字识别,我们选用ML Kit的端侧视频流文本识别能力,首先创建并初始化一个LensEngine对象

private LensEngine lensEngine = null;
private void createLensEngine() {
        if (this.lensEngine == null) {
            this.lensEngine = new LensEngine(this, this.cameraConfiguration, this.graphicOverlay);
        }
        try {
            this.lensEngine.setMachineLearningFrameTransactor(this.localTextTransactor);
            isInitialization = true;
        } catch (Exception e) {
            Toast.makeText(
                    this,
                    "Can not create image transactor: " + e.getMessage(),
                    Toast.LENGTH_LONG)
                    .show();
        }
}

接下来创建文本识别的处理类“LocalTextTransactor”,继承BaseTransactor<MLText>接口

public class LocalTextTransactor extends BaseTransactor<MLText>

在其中的LocalTextTransactor方法中,创建一个MLTextAnalyzer文本分析器,并且将识别的语种设置为中文“zh”

MLLocalTextSetting options = new MLLocalTextSetting.Factory()
                .setOCRMode(MLLocalTextSetting.OCR_TRACKING_MODE)
                .setLanguage(language)
                .create();
MLTextAnalyzer analyzer = MLAnalyzerFactory.getInstance().getLocalTextAnalyzer(options);

当获取到文本的识别结果之后,将识别到的字符打印显示到对应的位置

protected void onSuccess(
Bitmap originalCameraImage,
MLText results,
FrameMetadata frameMetadata,
GraphicOverlay graphicOverlay) {
        this.mlText =results;
        this.latestImageMetaData = frameMetadata;
        graphicOverlay.clear();
        List<MLText.Block> blocks = results.getBlocks();
        if ((Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) && originalCameraImage != null) {
            CameraImageGraphic imageGraphic = new CameraImageGraphic(graphicOverlay, originalCameraImage);
            graphicOverlay.addGraphic(imageGraphic);
        }
        if (blocks.size() > 0) {
            this.mCount = 0;
            this.mHandler.sendEmptyMessage(Constant.SHOW_TAKE_PHOTO_BUTTON);
        } else {
            this.mCount++;
            if (this.mCount > 1) {
                this.mHandler.sendEmptyMessage(Constant.HIDE_TAKE_PHOTO_BUTTON);
            }
        }
        for (int i = 0; i < blocks.size(); i++) {
            List<MLText.TextLine> lines = blocks.get(i).getContents();
            for (int j = 0; j < lines.size(); j++) {
                // Display by line, without displaying empty lines.
                if (lines.get(j).getStringValue() != null && lines.get(j).getStringValue().trim().length() != 0) {
                    BaseGraphic textGraphic = new LocalTextGraphic(graphicOverlay,
                            lines.get(j));
                    graphicOverlay.addGraphic(textGraphic);
                }
            }
        }
        graphicOverlay.postInvalidate();
}

调用LensEnginerun方法,启动相机读取视频流,即可进行识别

SurfaceView mSurfaceView = findViewById(R.id.surface_view);
try {
    lensEngine.run(mSurfaceView.getHolder());
} catch (IOException e) {
    // 异常处理逻辑。
}

识别完成之后,停止分析器,释放对应的视频流资源

if (analyzer != null) {
    try {
        analyzer.stop();
    } catch (IOException e) {
         // 异常处理。
    }
}
if (lensEngine != null) {
    lensEngine.release();

完成以上步骤,简单的“扫五福”功能就实现了

<span><span><strong><span><span style="color:black">“AR扫福”技术解读</span></span></strong></span></span>-LMLPHP

总结与思考

凭借OCR技术和华为机器学习能力,能轻易实现扫福,强大的功能带来的识别范围不止于字,万物皆可扫也成为可能,如何更好地利用功能为运营加分,为增长蓄能,扫福已经做了一个不错的示范,更好的范例和成果等你去大胆实践。

03-23 07:58