看看天空 发表于 2021-4-24 11:30

【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)

输入关键字会闪退,可能是本机内存不够,导致无法弹出输入键盘就闪退了

huluwa123 发表于 2021-4-24 11:45

流批流批

andyfky 发表于 2021-4-24 12:03

高手 啊,学习了,

Anyling 发表于 2021-4-24 13:03

不明觉厉,有没有大神给科普下

xiaoxs11 发表于 2021-4-24 13:39

膜拜大佬!!

看看天空 发表于 2021-4-24 15:57

Anyling 发表于 2021-4-24 13:03
不明觉厉,有没有大神给科普下

并没有多厉害,鸿蒙官网的例子,拿来跑了一遍

未晴雾香 发表于 2021-7-18 22:33

我也用的官网的例子,但是匹配的图片数是0,不知道哪里有问题
页: [1]
查看完整版本: 【AI文字识别】HarmonyOS&#160;Codelab挑战赛记录