【AI文字识别】HarmonyOS Codelab挑战赛记录
# 文字识别## 项目启动
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/Untitled.png](https://i.loli.net/2021/04/22/iZhFtXCl4kDSxTs.png)
## 布局文件
base/graphic/background_ability_main.xml
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<solid
ohos:color="#FFFFFF"/>
</shape>
```
base/graphic/background_ability_page.xml
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<solid
ohos:color="#FFFAF0"/>
</shape>
```
base/graphic/button_element.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="100"/>
<solid
ohos:color="#FF007DFE"/>
</shape>
```
base/layout/ability_main.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical"
ohos:background_element="$graphic:background_ability_page"
>
<Text
ohos:id="$+id:text_helloworld"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="关键词搜索图片"
ohos:text_size="30fp"
ohos:top_margin="5vp"
/>
<Text
ohos:id="$+id:picture_list"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="图片列表"
ohos:text_size="20fp"
ohos:top_margin="15vp"
/>
<ListContainer
ohos:id="$+id:picture_list_show"
ohos:height="200vp"
ohos:width="match_parent"
ohos:orientation="horizontal"
ohos:left_margin="5vp"
ohos:right_margin="5vp"
/>
<Text
ohos:id="$+id:word_seg_title"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:left_margin="5vp"
ohos:text="请输入关键词:"
ohos:text_size="25fp"
ohos:top_margin="10vp"
/>
<TextField
ohos:id="$+id:word_seg_text"
ohos:height="match_content"
ohos:width="match_parent"
ohos:background_element="$graphic:background_ability_main"
ohos:hint="Enter a statement."
ohos:left_padding="5vp"
ohos:right_padding="5vp"
ohos:text_alignment="vertical_center"
ohos:text_size="20fp"
ohos:top_margin="5vp"/>
<Button
ohos:id="$+id:button_search"
ohos:width="match_content"
ohos:height="match_content"
ohos:text_size="20fp"
ohos:text="开始通用文字识别"
ohos:layout_alignment="horizontal_center"
ohos:top_margin="10vp"
ohos:top_padding="1vp"
ohos:bottom_padding="1vp"
ohos:right_padding="20vp"
ohos:left_padding="20vp"
ohos:text_color="white"
ohos:background_element="$graphic:button_element"
ohos:center_in_parent="true"
ohos:align_parent_bottom="true"
ohos:bottom_margin="5vp"/>
<Text
ohos:id="$+id:picture_list_result"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_ability_main"
ohos:layout_alignment="horizontal_center"
ohos:text="搜索结果"
ohos:text_size="20fp"
ohos:top_margin="5vp"
/>
<ListContainer
ohos:id="$+id:picture_list_match"
ohos:height="200vp"
ohos:width="match_parent"
ohos:orientation="horizontal"
ohos:left_margin="5vp"
ohos:right_margin="5vp"
/>
</DirectionalLayout>
```
base/layout/item_image_layout.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="200vp"
ohos:width="205vp">
<Image
ohos:id="$+id:select_picture_list"
ohos:height="200vp"
ohos:width="200vp"
ohos:layout_alignment="horizontal_center"
ohos:top_margin="1vp"
ohos:scale_mode="stretch"
/>
</DirectionalLayout>
```
在"resources\base\media"目录下添加8张jpg图片(分别命名为1-8.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/1.jpg](https://i.loli.net/2021/04/22/c1JXAdaEwYorICF.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/2.jpg](https://i.loli.net/2021/04/22/FICBAYGQUmxrys1.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/3.jpg](https://i.loli.net/2021/04/22/mXjdWRP1c7ULO4t.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/4.jpg](https://i.loli.net/2021/04/22/9y8ZUEnNL1bR2XM.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/5.jpg](https://i.loli.net/2021/04/22/uOp68FH7AmxBUWi.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/6.jpg](https://i.loli.net/2021/04/22/438kMtfa2zBEcUG.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/7.jpg](https://i.loli.net/2021/04/22/IjXRqaDAiF2ncUE.jpg)
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/8.jpg](https://i.loli.net/2021/04/22/odDxjcikMfBlZqF.jpg)
## provider 目录
PictureProvider.java
```java
package com.example.ocrbaseonai.provider;
import com.example.ocrbaseonai.ResourceTable;
import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.LayoutScatter;
import ohos.app.Context;
import java.util.Optional;
public class PictureProvider extends BaseItemProvider {
private int[] pictureLists;
private Context context;
/**
*picture provider
*
* @Param pictureLists pictureLists
* @param context context
*/
public PictureProvider(int[] pictureLists, Context context) {
this.pictureLists = pictureLists;
this.context = context;
}
@Override
public int getCount() {
return pictureLists == null ? 0 : pictureLists.length;
}
@Override
public Object getItem(int position) {
return Optional.of(this.pictureLists);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public Component getComponent(int var1, Component var2, ComponentContainer var3) {
ViewHolder viewHolder = null;
Component component = var2;
if (component == null) {
component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_image_layout,
null, false);
viewHolder = new ViewHolder();
Component componentImage = component.findComponentById(ResourceTable.Id_select_picture_list);
if (componentImage instanceof Image) {
viewHolder.image = (Image) componentImage;
}
component.setTag(viewHolder);
} else {
if (component.getTag() instanceof ViewHolder) {
viewHolder = (ViewHolder) component.getTag();
}
}
if (viewHolder != null) {
viewHolder.image.setPixelMap(pictureLists);
}
return component;
}
private static class ViewHolder {
Image image;
}
}
```
## slice 目录
MainAbilitySlice.java
```java
package com.example.ocrbaseonai.slice;
import com.example.ocrbaseonai.ResourceTable;
import com.example.ocrbaseonai.provider.PictureProvider;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.components.TextField;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class MainAbilitySlice extends AbilitySlice {
private static final int LIST_CONTAINER_ID_SHOW = ResourceTable.Id_picture_list_show;
private static final int LIST_CONTAINER_ID_MATCH = ResourceTable.Id_picture_list_match;
private static final int NEG_ONE = -1;
private static final int ZERO = 0;
private static final int ONE = 1;
private static final int TWO = 2;
private Context slice;
private EventRunner runner;
private MyEventHandle myEventHandle;
private int[] pictureLists = new int[]{ResourceTable.Media_1, ResourceTable.Media_2,
ResourceTable.Media_3, ResourceTable.Media_4, ResourceTable.Media_5,
ResourceTable.Media_6, ResourceTable.Media_7, ResourceTable.Media_8};
private Component selectComponent;
private int selectPosition;
private Button button;
private TextField textField;
private Map<Integer, String> imageInfos;
private int[] matchPictures;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
slice = MainAbilitySlice.this;
// 展示图片列表
setSelectPicture(pictureLists, LIST_CONTAINER_ID_SHOW);
// 所有图片通用文字识别
wordRecognition();
// 设置需要分词的语句
Component componentText = findComponentById(ResourceTable.Id_word_seg_text);
if (componentText instanceof TextField) {
textField = (TextField) componentText;
}
// 点击按钮进行文字识别
Component componentSearch = findComponentById(ResourceTable.Id_button_search);
if (componentSearch instanceof Button) {
button = (Button) componentSearch;
button.setClickedListener(listener -> wordSegment());
}
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
// 设置图片选择区域
private void setSelectPicture(int[] pictures, int id) {
// 获取图片
PictureProvider newsTypeAdapter = new PictureProvider(pictures, this);
Component componentById = findComponentById(id);
if (componentById instanceof ListContainer) {
ListContainer listContainer = (ListContainer) componentById;
listContainer.setItemProvider(newsTypeAdapter);
}
}
// 通用文字识别
private void wordRecognition() {
initHandler();
WordRecognition wordRecognition = new WordRecognition();
wordRecognition.setParams(slice, pictureLists, myEventHandle);
wordRecognition.sendResult(null);
}
// 分词
private void wordSegment() {
// 组装关键词,作为分词对象
String requestData = "{\"text\":" + textField.getText() + ",\"type\":0}";
initHandler();
new WordSegment().wordSegment(slice, requestData, myEventHandle);
}
// 匹配图片
private void matchImage(List<String> list) {
Set<Integer> matchSets = new HashSet<>();
for (String str: list) {
for (Integer key : imageInfos.keySet()) {
if (imageInfos.get(key).indexOf(str) != NEG_ONE) {
matchSets.add(key);
}
}
}
// 获得匹配的图片
matchPictures = new int;
int i = 0;
for (int match: matchSets) {
matchPictures = match;
i++;
}
// 展示图片
setSelectPicture(matchPictures, LIST_CONTAINER_ID_MATCH);
}
private void initHandler() {
runner = EventRunner.getMainEventRunner();
if (runner == null) {
return;
}
myEventHandle = new MyEventHandle(runner);
}
public class MyEventHandle extends EventHandler {
MyEventHandle(EventRunner runner) throws IllegalArgumentException {
super(runner);
}
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
int eventId = event.eventId;
if (eventId == ONE) {
// 通用文字识别
if (event.object instanceof Map) {
imageInfos = (Map) event.object;
}
}
if (eventId == TWO) {
// 分词
if (event.object instanceof List) {
List<String> lists = (List) event.object;
if ((lists.size() > ZERO) && (!"no keywords".equals(lists.get(ZERO)))) {
// 根据输入关键词 匹配图片
matchImage(lists);
}
}
}
}
}
}
```
LogUtil.java
```java
package com.example.ocrbaseonai.slice;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
public class LogUtil {
private static final String TAG_LOG = "LogUtil";
private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, LogUtil.TAG_LOG);
private static final String LOG_FORMAT = "%{public}s: %{public}s";
private LogUtil() {
}
public static void info(String tag, String msg) {
HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg);
}
public static void error(String tag, String msg) {
HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg);
}
}
```
WordRecognition.java
```java
package com.example.ocrbaseonai.slice;
import ohos.ai.cv.common.ConnectionCallback;
import ohos.ai.cv.common.VisionCallback;
import ohos.ai.cv.common.VisionConfiguration;
import ohos.ai.cv.common.VisionImage;
import ohos.ai.cv.common.VisionManager;
import ohos.ai.cv.text.ITextDetector;
import ohos.ai.cv.text.Text;
import ohos.ai.cv.text.TextConfiguration;
import ohos.ai.cv.text.TextDetectType;
import ohos.app.Context;
import ohos.eventhandler.InnerEvent;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;
import ohos.global.resource.ResourceManager;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class WordRecognition {
private static final boolean IS_ASYNC = false;
private static final int IS_ASYNC_CODE = 700;
private Context slice;
private ITextDetector textDetector;
private PixelMap pixelMap;
private MainAbilitySlice.MyEventHandle handle;
private int[] pictureLists;
private int mediaId;
private Map maps = new HashMap<>();
private int index;
private int result;
public void setParams(Context context, int[] pictureIds, MainAbilitySlice.MyEventHandle myEventHandle) {
slice = context;
pictureLists = pictureIds;
handle = myEventHandle;
}
public void wordRecognition(Context context, int resId, MainAbilitySlice.MyEventHandle myEventHandle) {
mediaId = resId;
// 实例化ITextDetector接口
textDetector = VisionManager.getTextDetector(context);
// 实例化VisionImage对象image,并传入待检测图片pixelMap
pixelMap = getPixelMap(resId);
VisionImage image = VisionImage.fromPixelMap(pixelMap);
// 定义VisionCallback<Text>回调,异步模式下用到
VisionCallback<Text> visionCallback = getVisionCallback();
// 定义ConnectionCallback回调,实现连接能力引擎成功与否后的操作
ConnectionCallback connectionCallback = getConnectionCallback(image, visionCallback);
// 建立与能力引擎的连接
VisionManager.init(context, connectionCallback);
}
private VisionCallback getVisionCallback() {
return new VisionCallback<Text>() {
@Override
public void onResult(Text text) {
sendResult(text.getValue());
}
@Override
public void onError(int i) {
}
@Override
public void onProcessing(float v) {
}
};
}
private ConnectionCallback getConnectionCallback(VisionImage image, VisionCallback<Text> visionCallback) {
return new ConnectionCallback() {
@Override
public void onServiceConnect() {
// 实例化Text对象text
Text text = new Text();
// 通过TextConfiguration配置textDetector()方法的运行参数
TextConfiguration.Builder builder = new TextConfiguration.Builder();
builder.setProcessMode(VisionConfiguration.MODE_IN);
builder.setDetectType(TextDetectType.TYPE_TEXT_DETECT_FOCUS_SHOOT);
builder.setLanguage(TextConfiguration.AUTO);
TextConfiguration config = builder.build();
textDetector.setVisionConfiguration(config);
// 调用ITextDetector的detect()方法
if (!IS_ASYNC) {
int result2 = textDetector.detect(image, text, null); // 同步
sendResult(text.getValue());
} else {
int result2 = textDetector.detect(image, null, visionCallback); // 异步
}
}
@Override
public void onServiceDisconnect() {
// 释放
if ((!IS_ASYNC && (result == 0)) || (IS_ASYNC && (result == IS_ASYNC_CODE))) {
textDetector.release();
}
if (pixelMap != null) {
pixelMap.release();
pixelMap = null;
}
VisionManager.destroy();
}
};
}
public void sendResult(String value) {
if (textDetector != null) {
textDetector.release();
}
if (pixelMap != null) {
pixelMap.release();
pixelMap = null;
VisionManager.destroy();
}
if (value != null) {
maps.put(mediaId, value);
}
if ((maps != null) && (maps.size() == pictureLists.length)) {
InnerEvent event = InnerEvent.get(1, 0, maps);
handle.sendEvent(event);
} else {
wordRecognition(slice, pictureLists, handle);
index++;
}
}
// 获取图片
private PixelMap getPixelMap(int resId) {
ResourceManager manager = slice.getResourceManager();
byte[] datas = new byte;
try {
Resource resource = manager.getResource(resId);
datas = readBytes(resource);
resource.close();
} catch (IOException | NotExistException e) {
LogUtil.error("get pixelmap failed, read resource bytes failed, ", e.getLocalizedMessage());
}
ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
srcOpts.formatHint = "image/jpg";
ImageSource imageSource;
imageSource = ImageSource.create(datas, srcOpts);
ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
decodingOpts.desiredSize = new Size(0, 0);
decodingOpts.desiredRegion = new Rect(0, 0, 0, 0);
decodingOpts.desiredPixelFormat = PixelFormat.ARGB_8888;
pixelMap = imageSource.createPixelmap(decodingOpts);
return pixelMap;
}
private static byte[] readBytes(Resource resource) {
final int bufferSize = 1024;
final int ioEnd = -1;
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffers = new byte;
byte[] results = new byte;
while (true) {
try {
int readLen = resource.read(buffers, 0, bufferSize);
if (readLen == ioEnd) {
results = output.toByteArray();
break;
}
output.write(buffers, 0, readLen);
} catch (IOException e) {
LogUtil.error("OrcAbilitySlice.getPixelMap", "read resource failed ");
break;
} finally {
try {
output.close();
} catch (IOException e) {
LogUtil.error("OrcAbilitySlice.getPixelMap", "close output failed");
}
}
}
return results;
}
}
```
WordSegment.java
```java
package com.example.ocrbaseonai.slice;
import ohos.ai.nlu.NluClient;
import ohos.ai.nlu.NluRequestType;
import ohos.ai.nlu.OnResultListener;
import ohos.ai.nlu.ResponseResult;
import ohos.app.Context;
import ohos.eventhandler.InnerEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class WordSegment {
private static final boolean IS_ASYNC = true;
private static final String WORDS = "words";
private static final int ZERO = 0;
private static final int TWO = 2;
private static final int STEP = 8;
private Context slice;
private MainAbilitySlice.MyEventHandle handle;
public void wordSegment(Context context, String requestData, MainAbilitySlice.MyEventHandle myEventHandle) {
slice = context;
handle = myEventHandle;
// 使用NluClient静态类进行初始化,通过异步方式获取服务的连接。
NluClient.getInstance().init(context, new OnResultListener<Integer>() {
@Override
public void onResult(Integer resultCode) {
if (!IS_ASYNC) {
// 同步
ResponseResult responseResult = NluClient.getInstance().getWordSegment(requestData,
NluRequestType.REQUEST_TYPE_LOCAL);
sendResult(responseResult.getResponseResult());
release();
} else {
// 异步
wordSegmentAsync(requestData);
}
}
}, true);
}
private void wordSegmentAsync(String requestData) {
ResponseResult responseResult = NluClient.getInstance().getWordSegment(requestData,
NluRequestType.REQUEST_TYPE_LOCAL, new OnResultListener<ResponseResult>() {
@Override
public void onResult(ResponseResult asyncResult) {
sendResult(asyncResult.getResponseResult());
release();
}
});
}
private void sendResult(String result) {
List lists = null; // 分词识别结果
// 将result中分词结果转换成list
if (result.contains("\"message\":\"success\"")) {
String words = result.substring(result.indexOf(WORDS) + STEP,
result.lastIndexOf("]")).replaceAll("\"", "");
if ((words == null) || ("".equals(words))) {
lists = new ArrayList(1);
lists.add("no keywords"); // 未识别到分词结果,返回"no keywords"
} else {
lists = Arrays.asList(words.split(","));
}
}
InnerEvent event = InnerEvent.get(TWO, ZERO, lists);
handle.sendEvent(event);
}
private void release() {
NluClient.getInstance().destroy(slice);
}
}
```
MainAbility.java 和 MyApplication.java 保持初始状态不变
## 效果展示
![%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/Untitled%201.png](https://i.loli.net/2021/04/22/ZnOh1GE7TzgANuX.png)
输入关键字会闪退,可能是本机内存不够,导致无法弹出输入键盘就闪退了 流批流批 高手 啊,学习了, 不明觉厉,有没有大神给科普下 膜拜大佬!! Anyling 发表于 2021-4-24 13:03
不明觉厉,有没有大神给科普下
并没有多厉害,鸿蒙官网的例子,拿来跑了一遍 我也用的官网的例子,但是匹配的图片数是0,不知道哪里有问题
页:
[1]