效果
动态识别:
触发识别
资源
文章依赖开源库
GitHub - cmdbug/YOLOv5_NCNN: 🍅 Deploy ncnn on mobile phones. Support Android and iOS. 移动端ncnn部署,支持Android与iOS。🍅 Deploy ncnn on mobile phones. Support Android and iOS. 移动端ncnn部署,支持Android与iOS。 - GitHub - cmdbug/YOLOv5_NCNN: 🍅 Deploy ncnn on mobile phones. Support Android and iOS. 移动端ncnn部署,支持Android与iOS。https://github.com/cmdbug/YOLOv5_NCNN
训练的模型
初始化模型
dbnet->load_param(mgr, "dbnet_op.param");
dbnet->load_model(mgr, "dbnet_op.bin");
crnn_net = new ncnn::Net();
crnn_net->opt.use_vulkan_compute = toUseGPU; // gpu
crnn_net->opt.use_fp16_arithmetic = true; // fp16运算加速
crnn_net->load_param(mgr, "crnn_lite_op.param");
crnn_net->load_model(mgr, "crnn_lite_op.bin");
angle_net = new ncnn::Net();
angle_net->opt.use_vulkan_compute = toUseGPU; // gpu
angle_net->opt.use_fp16_arithmetic = true; // fp16运算加速
angle_net->load_param(mgr, "angle_op.param");
angle_net->load_model(mgr, "angle_op.bin");
/*获取文件名并打开*/
jboolean iscopy;
jstring filename = env->NewStringUTF("keys.txt");
const char *mfile = env->GetStringUTFChars(filename, &iscopy);
AAsset *asset = AAssetManager_open(mgr, mfile, AASSET_MODE_BUFFER);
env->ReleaseStringUTFChars(filename, mfile);
文字识别和文字轮廓识别
std::vector<OCRResult> OCR::detect(JNIEnv *env, jobject image, int short_size) {AndroidBitmapInfo img_size;AndroidBitmap_getInfo(env, image, &img_size);ncnn::Mat src_img = ncnn::Mat::from_android_bitmap_resize(env, image, ncnn::Mat::PIXEL_RGBA2RGB,img_size.width, img_size.height);cv::Mat im_bgr(src_img.h, src_img.w, CV_8UC3);src_img.to_pixels(im_bgr.data, ncnn::Mat::PIXEL_RGB2BGR);// 图像缩放auto im = resize_img(im_bgr, short_size);int wid = im.cols;int hi = im.rows;int srcwid = im_bgr.cols;int srchi = im_bgr.rows;float h_scale = im_bgr.rows * 1.0 / im.rows;float w_scale = im_bgr.cols * 1.0 / im.cols;ncnn::Mat in = ncnn::Mat::from_pixels(im.data, ncnn::Mat::PIXEL_BGR2RGB, im.cols, im.rows);in.substract_mean_normalize(mean_vals_dbnet, norm_vals_dbnet);// LOGD("jni ocr input size:%d x %d", in.w, in.h);ncnn::Extractor ex = dbnet->create_extractor();ex.set_light_mode(true);ex.set_num_threads(num_thread);if (toUseGPU) { // 消除提示ex.set_vulkan_compute(toUseGPU);}ex.input("input0", in);ncnn::Mat dbnet_out;double time1 = static_cast<double>(cv::getTickCount());ex.extract("out1", dbnet_out);//LOGE("jni ocr dbnet forward time:%lfs", (static_cast<double>(cv::getTickCount()) - time1) / cv::getTickFrequency());//LOGE("jni ocr output size:%d x %d", dbnet_out.w, dbnet_out.h);time1 = static_cast<double>(cv::getTickCount());cv::Mat fmapmat(hi, wid, CV_32FC1);memcpy(fmapmat.data, (float *) dbnet_out.data, wid * hi * sizeof(float));cv::Mat norfmapmat;norfmapmat = fmapmat > thresh;time1 = static_cast<double>(cv::getTickCount());std::vector<std::vector<cv::Point>> contours;cv::findContours(norfmapmat, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);for (auto & contour : contours) {std::vector<cv::Point> minbox;float minedgesize, alledgesize;get_mini_boxes(contour, minbox, minedgesize, alledgesize);if (minedgesize < min_size)continue;float score = box_score_fast(fmapmat, contour);if (score < box_thresh)continue;std::vector<cv::Point> newbox;unclip(minbox, alledgesize, newbox, unclip_ratio);get_mini_boxes(newbox, minbox, minedgesize, alledgesize);if (minedgesize < min_size + 2)continue;for (auto & i : minbox) {i.x *= w_scale;i.y *= h_scale;}boxes.push_back(minbox);box_score.push_back(score); // teng
// LOGD("jni ocr box score:%f", score);}LOGE("jni ocr 轮廓识别耗时:%lfs", (static_cast<double>(cv::getTickCount()) - time1) / cv::getTickFrequency());//LOGE("jni ocr dbnet decode time:%lfs", (static_cast<double>(cv::getTickCount()) - time1) / cv::getTickFrequency());//LOGE("jni ocr box size:%u", boxes.size());// auto result = draw_bbox(im_bgr, boxes);
// cv::imwrite("./imgs/result.jpg", result);time1 = static_cast<double>(cv::getTickCount());//开始行文本角度检测和文字识别for (int i = boxes.size() - 1; i >= 0; i--) {std::vector<cv::Point> temp_box = boxes[i];cv::Mat part_im;part_im = GetRotateCropImage(im_bgr, temp_box);int part_im_w = part_im.cols;int part_im_h = part_im.rows;// 开始文本识别int crnn_w_target;float scale = crnn_h * 1.0 / part_im_h;crnn_w_target = int(part_im.cols * scale);cv::Mat img2 = part_im.clone();ncnn::Mat crnn_in = ncnn::Mat::from_pixels_resize(img2.data,ncnn::Mat::PIXEL_BGR2RGB, img2.cols, img2.rows, crnn_w_target,crnn_h);//角度检测int crnn_w = crnn_in.w;int crnn_h = crnn_in.h;ncnn::Mat angle_in;if (crnn_w >= angle_target_w) copy_cut_border(crnn_in, angle_in, 0, 0, 0, crnn_w - angle_target_w);else copy_make_border(crnn_in, angle_in, 0, 0, 0, angle_target_w - crnn_w, 0, 255.f);angle_in.substract_mean_normalize(mean_vals_crnn_angle, norm_vals_crnn_angle);ncnn::Extractor angle_ex = angle_net->create_extractor();angle_ex.set_light_mode(true);angle_ex.set_num_threads(num_thread);if (toUseGPU) { // 消除提示angle_ex.set_vulkan_compute(toUseGPU);}angle_ex.input("input", angle_in);ncnn::Mat angle_preds;angle_ex.extract("out", angle_preds);auto *srcdata = (float *) angle_preds.data;float angle_score = srcdata[0];
// LOGD("jni ocr angle score:%f", angle_score);//判断方向if (angle_score < 0.5) {part_im = matRotateClockWise180(part_im);}//crnn识别crnn_in.substract_mean_normalize(mean_vals_crnn_angle, norm_vals_crnn_angle);ncnn::Mat crnn_preds;ncnn::Extractor crnn_ex = crnn_net->create_extractor();crnn_ex.set_light_mode(true);crnn_ex.set_num_threads(num_thread);if (toUseGPU) { // 消除提示crnn_ex.set_vulkan_compute(toUseGPU);}crnn_ex.input("input", crnn_in);ncnn::Mat blob162;crnn_ex.extract("443", blob162);ncnn::Mat blob263(5532, blob162.h);//batch fcfor (int i = 0; i < blob162.h; i++) {ncnn::Extractor crnn_ex_2 = crnn_net->create_extractor();crnn_ex_2.set_light_mode(true);crnn_ex_2.set_num_threads(num_thread);if (toUseGPU) { // 消除提示crnn_ex_2.set_vulkan_compute(toUseGPU);}ncnn::Mat blob243_i = blob162.row_range(i, 1);crnn_ex_2.input("457", blob243_i);ncnn::Mat blob263_i;crnn_ex_2.extract("458", blob263_i);memcpy(blob263.row(i), blob263_i, 5532 * sizeof(float));}crnn_preds = blob263;auto res_pre = crnn_deocde(crnn_preds, alphabetChinese);
// pre_res.push_back(res_pre);// tengpre_res.insert(pre_res.begin(), res_pre);// teng// for (int i = 0; i < res_pre.size(); i++) {
// LOGD("jni ocr res_pre:%s", res_pre[i].c_str());
// }}LOGE("jni ocr 文字识别耗时:%lfs", (static_cast<double>(cv::getTickCount()) - time1) / cv::getTickFrequency());//LOGE("jni ocr time:%lfs", (static_cast<double>(cv::getTickCount()) - time1) / cv::getTickFrequency());//LOGE("jni ocr boxes size:%u", boxes.size());//LOGE("jni ocr pre_res size:%u", pre_res.size());//LOGE("jni ocr box_score size:%u", box_score.size());std::vector<OCRResult> resutls;//resutls.reserve(boxes.size());//for (int i = 0; i < boxes.size(); i++) {// OCRResult ocrInfo;// ocrInfo.boxes = boxes[i];// ocrInfo.pre_res = pre_res[i];// ocrInfo.box_score = box_score[i];// resutls.push_back(ocrInfo);// LOGE("jni ocr ocrresult[%d]:%s",i, ocrInfo.pre_res[0].c_str());//}return resutls;
}
接口
JNI接口
初始化
extern "C" JNIEXPORT void JNICALL
Java_com_ocr_library_ChineseOCRLite_init(JNIEnv *env, jclass clazz, jobject assetManager, jboolean useGPU) {if (OCR::detector != nullptr) {delete OCR::detector;OCR::detector = nullptr;}if (OCR::detector == nullptr) {AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);OCR::detector = new OCR(env, clazz, mgr, useGPU);}
}
识别
extern "C" JNIEXPORT jobjectArray JNICALL
Java_com_ocr_library_ChineseOCRLite_detect(JNIEnv *env, jclass clazz, jobject bitmap, jint short_size) {double time1 = static_cast<double>(cv::getTickCount());OCR::detector->detect(env, bitmap, short_size);LOGE("jni ocr 识别耗时:%lfs", (static_cast<double>(cv::getTickCount()) - time1) / cv::getTickFrequency());double time2 = static_cast<double>(cv::getTickCount());
// LOGD("jni ocr result size:%ld", ocrResult.size());
// LOGD("jni ocr ocrresult[0].pre_res size:%ld", ocrResult[0].pre_res.size());
// LOGD("jni ocr ocrresult[0][0]:%s", ocrResult[0].pre_res[0].c_str());auto ocr_result = env->FindClass("com/ocr/library/OCRResult");auto cid = env->GetMethodID(ocr_result, "<init>", "([D[DLjava/lang/String;)V");jobjectArray ret = env->NewObjectArray(OCR::detector->boxes.size(), ocr_result, nullptr);for (int i = 0; i < OCR::detector->boxes.size(); i++) {// boxScoreenv->PushLocalFrame(1);jdoubleArray boxScoreData = env->NewDoubleArray(1);auto *bsnum = new jdouble[1];for (int j = 0; j < 1; ++j) {*(bsnum + j) = OCR::detector->box_score[i];}env->SetDoubleArrayRegion(boxScoreData, 0, 1, bsnum);delete[] bsnum;// textchar *cp = new char;for (auto &pre_re : OCR::detector->pre_res[i]) {strcat(cp, pre_re.c_str());}jstring text = str2jstring(env, cp);delete cp;// boxsjdoubleArray boxsData = env->NewDoubleArray(OCR::detector->boxes[i].size() * 2);auto *bnum = new jdouble[OCR::detector->boxes[i].size() * 2];for (int j = 0; j < OCR::detector->boxes[i].size(); j++) {*(bnum + j * 2) = OCR::detector->boxes[i][j].x;*(bnum + j * 2 + 1) = OCR::detector->boxes[i][j].y;}env->SetDoubleArrayRegion(boxsData, 0, OCR::detector->boxes[i].size() * 2, bnum);delete[] bnum;// 合并一下jobject obj = env->NewObject(ocr_result, cid, boxScoreData, boxsData, text);obj = env->PopLocalFrame(obj);env->SetObjectArrayElement(ret, i, obj);}OCR::detector->boxes.clear();OCR::detector->pre_res.clear();OCR::detector->box_score.clear();LOGE("jni ocr 处理耗时:%lfs", (static_cast<double>(cv::getTickCount()) - time2) / cv::getTickFrequency());LOGE("jni ocr 总耗时耗时:%lfs", (static_cast<double>(cv::getTickCount()) - time1) / cv::getTickFrequency());
// LOGD("jni ocr return");return ret;
}
java接口
public class ChineseOCRLite {static {System.loadLibrary("OCRLite");}public static native void init(AssetManager manager, boolean useGPU);public static native OCRResult[] detect(Bitmap bitmap, int short_size);
}
CmakeList编译
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp")
if(DEFINED ANDROID_NDK_MAJOR AND ${ANDROID_NDK_MAJOR} GREATER 20)set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-openmp")
endif()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.# 搜索当前目录下的所有.cpp文件
aux_source_directory(ocr SRC_LIST)add_library(OCRLiteSHARED${SRC_LIST}
)#add_library( # Sets the name of the library.
# yolov5
#
# # Sets the library as a shared library.
# SHARED
#
# # Provides a relative path to your source file(s).
# jni_interface.cpp
# YoloV5.cpp
# YoloV4.cpp
# SimplePose.cpp
# Yolact.cpp
# ocr/clipper.cpp
# ocr/NCNNDBNet.cpp
# ocr/ocr.cpp
# ocr/RRLib.cpp
# ocr/ZUtil.cpp
# ENet.cpp
# FaceLandmark.cpp
# DBFace.cpp
# MbnFCN.cpp
# MobileNetV3Seg.cpp
# YoloV5CustomLayer.cpp
# NanoDet.cpp
# )include_directories(ncnnvulkan/includeocr
)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.find_library(log-lib log)
find_library(android-lib android)
find_library(vulkan-lib vulkan)
find_library(jnigraphics-lib jnigraphics)
add_library( ncnn STATIC IMPORTED )
#add_library( ncnnvulkan STATIC IMPORTED )
set_target_properties( # Specifies the target library.ncnn#ncnnvulkan# Specifies the parameter you want to define.PROPERTIES IMPORTED_LOCATION# Provides the path to the library you want to import.${CMAKE_SOURCE_DIR}/ncnnvulkan/${ANDROID_ABI}/libncnn.a )add_compile_options(-g)
# ncnnvulkan
add_library(glslang STATIC IMPORTED)
add_library(OGLCompiler STATIC IMPORTED)
add_library(OSDependent STATIC IMPORTED)
add_library(SPIRV STATIC IMPORTED)
set_target_properties(glslang PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/ncnnvulkan/${ANDROID_ABI}/libglslang.a)
set_target_properties(OGLCompiler PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/ncnnvulkan/${ANDROID_ABI}/libOGLCompiler.a)
set_target_properties(OSDependent PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/ncnnvulkan/${ANDROID_ABI}/libOSDependent.a)
set_target_properties(SPIRV PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/ncnnvulkan/${ANDROID_ABI}/libSPIRV.a)# disable rtti and exceptions
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
# enable rtti and exceptions
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti")include_directories(${CMAKE_SOURCE_DIR}/opencv/include/
)
add_library(libopencv_java4 STATIC IMPORTED)
set_target_properties(libopencv_java4PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/opencv/${ANDROID_ABI}/libopencv_java4.so
)
target_link_libraries( # Specifies the target library.OCRLite# Links the target library to the log library# included in the NDK.${log-lib}${vulkan-lib}${android-lib}${jnigraphics-lib}libopencv_java4)# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.OCRLite# Links the target library to the log library# included in the NDK.${log-lib}${vulkan-lib}${android-lib}${jnigraphics-lib}ncnnglslang SPIRV OGLCompiler OSDependent )#target_link_libraries( # Specifies the target library.
# OCRLite
#
# # Links the target library to the log library
# # included in the NDK.
# ${log-lib}
# ${vulkan-lib}
# ${android-lib}
# ${jnigraphics-lib}
# ncnnvulkan
# glslang SPIRV OGLCompiler OSDependent )
项目结构
使用
文字识别用的ncnn库,轮廓识别用的opencv
使用CameraX来采集文字图片信息
private PreviewView previewView;
private ImageCapture imageCapture;
private ProcessCameraProvider cameraProvider;
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private Preview preview;cameraProviderFuture = ProcessCameraProvider.getInstance(getActivity())
cameraProviderFuture.addListener(() -> {try {cameraProvider = cameraProviderFuture.get();//相机ID CameraSelector.LENS_FACING_FRONT前置;CameraSelector.LENS_FbindPreview(getActivity(), cameraProvider);} catch (Exception e) {e.printStackTrace();}
}, ContextCompat.getMainExecutor(getActivity()));preview = new Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9) //设置宽高比.setTargetRotation(Surface.ROTATION_0) // 设置旋转角度.build();
CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3) //设置宽高比.setTargetRotation(Surface.ROTATION_0) // 设置旋转角度.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build();
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(activity), imageProxy -> {if (!isLocked)initOCR(previewView.getBitmap());imageProxy.close();
});
//拍摄图像的配置
imageCapture = new ImageCapture.Builder()//CAPTURE_MODE_MAXIMIZE_QUALITY 拍摄高质量图片,图像质量优先于延迟,可能需要更长的时间//CAPTURE_MODE_MINIMIZE_LATENCY.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY).setTargetAspectRatio(AspectRatio.RATIO_4_3) //设置宽高比.setTargetRotation(Surface.ROTATION_0) // 设置旋转角度.build();
cameraProvider.unbindAll();
cameraProvider.bindToLifecycle((LifecycleOwner) activity, cameraSelector, imageCapture, preview/*, imageAnalysis*/);
//这里previewView是预览图层,需要在布局中实现,然后在这里使用
preview.setSurfaceProvider(previewView.getSurfaceProvider());//识别动作触发后
initOCR(previewView.getBitmap());
OCR接口初始化
调用接口ChineseOCRLite.init();
这里getAssets()是训练模型的保存路径,可以自定义路径。
public class BaseApplication extends Application {@Overridepublic void onCreate() {super.onCreate();new Thread(() -> ChineseOCRLite.init(getAssets(), false)).start();}
}
识别接口调用
调用接口ChineseOCRLite.detect();
所需要的数据是图片数据转换成的bitmap,文中是把相机获取到的数据直接转换成了bitmap.
识别出来的结果是OCRResult类型的数组。
private void initOCR(Bitmap bitmap) {isLocked = true;new Thread(() -> {try {outBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);bitmap.recycle();int maxSize = Math.max(outBitmap.getWidth(), outBitmap.getHeight());final OCRResult[] ocrResult = ChineseOCRLite.detect(outBitmap, maxSize);final StringBuilder allText = new StringBuilder();if (ocrResult != null && ocrResult.length > 0) {outBitmap = drawResult(outBitmap, ocrResult);for (OCRResult result : ocrResult) {//Log.e("NCNN", "text=" + result.text);//Log.e("NCNN", "boxes=" + Arrays.toString(result.boxes));//Log.e("NCNN", "boxScore=" + Arrays.toString(result.boxScore));allText.append(result.text);}}handler.sendEmptyMessage(98);} catch (Exception e) {e.printStackTrace();}}).start();
}//识别结果绘制
protected Bitmap drawResult(Bitmap mutableBitmap, OCRResult[] results) {if (results == null || results.length <= 0) {return mutableBitmap;}Canvas canvas = new Canvas(mutableBitmap);final Paint boxPaint = new Paint();boxPaint.setAlpha(200);boxPaint.setStyle(Paint.Style.STROKE);boxPaint.setStrokeWidth(2 * Math.min(mutableBitmap.getWidth(), mutableBitmap.getHeight()) / 800.0f);boxPaint.setTextSize(15 * Math.min(mutableBitmap.getWidth(), mutableBitmap.getHeight()) / 800.0f);boxPaint.setColor(Color.BLUE);boxPaint.setAntiAlias(true);for (OCRResult result : results) {boxPaint.setColor(Color.RED);boxPaint.setStyle(Paint.Style.FILL);// 框canvas.drawLine((float) result.boxes[0], (float) result.boxes[1], (float) result.boxes[2], (float) result.boxes[3], boxPaint);canvas.drawLine((float) result.boxes[2], (float) result.boxes[3], (float) result.boxes[4], (float) result.boxes[5], boxPaint);canvas.drawLine((float) result.boxes[4], (float) result.boxes[5], (float) result.boxes[6], (float) result.boxes[7], boxPaint);canvas.drawLine((float) result.boxes[6], (float) result.boxes[7], (float) result.boxes[0], (float) result.boxes[1], boxPaint);// 文字if (showText) { // 防止太乱double angle = getBoxAngle(result, true);canvas.save();canvas.rotate((float) angle, (float) result.boxes[0], (float) result.boxes[1] - 5);boxPaint.setColor(Color.BLUE); // 防止有角度的框与之重叠if (angle > 70) {canvas.drawText(String.format(Locale.CHINESE, "%s (%.3f)", result.text, result.boxScore[0]),(float) result.boxes[0] + 5, (float) result.boxes[1] + 15, boxPaint);} else {canvas.drawText(String.format(Locale.CHINESE, "%s (%.3f)", result.text, result.boxScore[0]),(float) result.boxes[0], (float) result.boxes[1] - 5, boxPaint);}canvas.restore();}// 提示boxPaint.setColor(Color.YELLOW); // 左上角画个红点canvas.drawPoint((float) result.boxes[0], (float) result.boxes[1], boxPaint);boxPaint.setColor(Color.GREEN); // 右下角画个绿点canvas.drawPoint((float) result.boxes[4], (float) result.boxes[5], boxPaint);}return mutableBitmap;
}
protected double getBoxAngle(OCRResult ocrResult, boolean toDegrees) {double angle = 0.0f;if (ocrResult == null) {return angle;}// 0 1 2 3 4 5 6 7// x0y0 x1y1 x2y2 x3y3double dx1 = ocrResult.boxes[2] - ocrResult.boxes[0];double dy1 = ocrResult.boxes[3] - ocrResult.boxes[1];double dis1 = dy1 * dy1 + dx1 * dx1;double dx2 = ocrResult.boxes[4] - ocrResult.boxes[2];double dy2 = ocrResult.boxes[5] - ocrResult.boxes[3];double dis2 = dy2 * dy2 + dx2 * dx2;if (dis1 > dis2) {if (dx1 != 0) {angle = Math.asin(dy1 / dx1);}} else {if (dx2 != 0) {angle = Math.asin(dx2 / dy2);}}if (toDegrees) {angle = Math.toDegrees(angle);if (dis2 > dis1) {angle = angle + 90;}return angle;}return angle;
}private final Handler.Callback callback = new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {if (msg.what == 98) {imageView.setImageBitmap(outBitmap);isLocked = false;}return false;}
};
private final Handler handler = new Handler(Looper.myLooper(), callback);
@Override
public void onDestroyView() {super.onDestroyView();handler.removeCallbacksAndMessages(callback);
}
这里public double[] boxScore; 取boxScore[0]即可。识别结果准确度
public String text;存放识别到的文字
public double[] boxes; 存放文字位置,可根据x,y的坐标位置,绘制出文字框
public class OCRResult {public double[] boxScore; // 直接 boxScore[0] 就行public String text;public double[] boxes; // xy xy xy xypublic OCRResult (double[] boxScore, double[] boxes, String text) {this.boxScore = boxScore;this.boxes = boxes;this.text = text;}public double[] getBoxScore() {return boxScore;}public void setBoxScore(double[] boxScore) {this.boxScore = boxScore;}public double[] getBoxes() {return boxes;}public void setBoxes(double[] boxes) {this.boxes = boxes;}public String getText() {return text;}public void setText(String text) {this.text = text;}}