Android NCNN识别文字(OCR)

news/2024/11/19 12:25:04/

效果

动态识别:

触发识别

资源

文章依赖开源库

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;}}


http://www.ppmy.cn/news/404527.html

相关文章

Tesseract Ocr文字识别实战(新版本,扩展手写文字识别)

目录 1.Tesseract Ocr文字识别 1.1 运行环境 1.2 python模块 1.3 配置tesseract运行文件 1.4 代码识别 2. 手写汉字识别 2.1 下载库 2.2 代码 1.Tesseract Ocr文字识别 前半部分原github地址&#xff1a;faceai/tesseractOCR.md at master vipstone/faceai GitHub 1…

我为开放原子全球开源峰会助力:共建开源之梦

我为开放原子全球开源峰会助力&#xff1a;共建开源之梦 6月11日&#xff0c;以“开源赋能&#xff0c;普惠未来”为主题的2023开放原子全球开源峰会开幕式暨高峰论坛在北京成功举办。 开源的力量与魅力 开源是当今软件行业中不可忽视的力量&#xff0c;它为技术的快速发展和…

【Dart】Dart学习(一)Dart的一些概念和变量说明

简单的 Dart 程序 下面的应用程序代码用到了很多 Dart 的基本功能&#xff1a; // Define a function. void printInteger(int aNumber) {print(The number is $aNumber.); // Print to console. }// This is where the app starts executing. void main() {var number 42; …

细说如何封装一个日历组件(多视图、可选择、国际化)

前言 最近好奇日历组件是怎么实现的。于是阅读了下react-calendar的源码&#xff0c;并实现了简化版的日历组件。本文把实现日历的设计思路分享给大家。只要理清了主要逻辑&#xff0c;就不难实现了。 技术栈&#xff1a;react、typescript 预览 在线预览demo&#xff1a;c…

5.数据类型,自动转换

java数据类型 基本类型 整数类型 byte[1] short[2] int[4] long[8]布尔类型 boolean[1]浮点类型 double[8] float[4]字符型 char[2] 引用类型 类(class)接口(interface)数组([]) 低精度向高精度 char->int->long->float->double byte->short->int->lo…

修改鼠标图标

今日向大家分享一个修改鼠标光标的网站。 网址如下&#xff1a;https://zhutix.com/tag/cursors/page/5/ 在这个网站&#xff0c;你可以选择自己心怡的个性鼠标光标&#xff0c;下面我来教大家如何设置自己的鼠标光标。 1.下载光标类型 选择自己心怡的鼠标光标&#xff0c;只…

【Unity】Cursor类——隐藏鼠标、锁定鼠标、设置鼠标图标

1.隐藏鼠标 using System.Collections; using System.Collections.Generic; using UnityEngine;public class Lesson2 : MonoBehaviour {void Start(){//true&#xff1a;显示//false&#xff1a;隐藏Cursor.visible false;} }2.锁定鼠标 using System.Collections; using S…

地图中显示鼠标点

/// <summary>/// 根据屏幕坐标&#xff08;X,Y&#xff09;创建IPoint /// </summary>/// <param name"pActiveView">活动视图</param>/// <param name"X"></param>/// <param name"Y"></param&g…