安卓NDK视觉开发——手机拍照文档边缘检测实现方法与库封装
一、项目创建
创建NDK项目有两种方式,一种从新创建整个项目,一个在创建好的项目添加NDK接口。
1.创建NDK项目
创建 一个Native C++项目:

选择包名、API版本与算法交互的语言:

选择C++版本:

创建完之后,可以在项目中看到一个jni或者cpp的目录,目录包含一个CMakeLists.txt文件一个xxx.cpp文件:

2.添加NDK项目
在main目录添加一个目录,可命名为cpp或者jni都行:

把创建好的目录转化为JNI交互目录:

转化成功之后,目录下包含一个CMakeLists.txt文件一个xxx.cpp文件:

3.添加NDK依赖
选择使用的NDK版本:

选择CMake版本:

把下载好的NDK添加到配置文件:

4.测试与使用
添加类Java交互类:

在java交互类里面接口与jni交互的API:
package com.example.docscan;
public class scanlib
{public native String stringFromJNI();// Used to load the 'docscan' library on application startup.static {System.loadLibrary("docscan");}}
在xxx.cpp里面实现函数功能:
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_docscan_scanlib_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
在MainActivity类里面调用函数:
public class MainActivity extends AppCompatActivity {private ScanLib scan_lib = new ScanLib();private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());// Example of a call to a native methodTextView tv = binding.sampleText;tv.setText(scan_lib.stringFromJNI());}
}
二、添加依赖库
1.OpenCV
OpenCV是图像处理的基础,完整的包有上百M的大小,基于apk包大小的考虑,要对OpenCV做剪枝,之后重新编译成SDK,复制到jni(cpp)目录下:

2.NCNN
NCNN是深度学习算法模型的推理加速库,可以基于CPU或NPU进行推理,对应市场常用机型,选择使用NCNN版本并添加jni(cpp)目录下:

3.算法代码
把算法实现代码添加jni(cpp)目录下:

3. 源码编译
在CMakeLists.txt文件中添加这两个库与算法代码:
project(ScanJiaLib)
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()## opencv 库
set(OpenCV_DIR "${CMAKE_SOURCE_DIR}/sdk/native/jni")
find_package(OpenCV REQUIRED)if (OpenCV_FOUND)message(STATUS "OpenCV_LIBS: ${OpenCV_LIBS}")message(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}")
else ()message(FATAL_ERROR "opencv Not Found!")
endif (OpenCV_FOUND)#ncnn库
set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20221128-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn)
find_package(ncnn REQUIRED)
set_target_properties(ncnn PROPERTIESINTERFACE_COMPILE_OPTIONS "-frtti;-fexceptions"# ncnn.cmake 里面是关的,把它重新打开防止跟opencv2冲突,如果是重新编译ncnn的请自己尝试要开还是关
)#算法代码
add_library(ScanJia-jni SHARED ScanJia_jni.cpp BitmapUtils.cpp DocumentEdge.cpp)target_link_libraries(ScanJia-jni ${OnnxRuntime_LIBS} ncnn ${OpenCV_LIBS} jnigraphics)
4.封装成so包
在CMakeLists.txt里面添加封装库保存目录和要封装的cpp文件,重新编译:
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
add_library(DocScan SHARED BitmapUtils.cpp DocumentEdge.cpp ScanJia_jni.cpp)
编译完成之后,在jni(cpp)目录生成封装好的so包,生成完成之后,注释掉上面的语句:

5.调用so包
在CMakeLists.txt里面添加so库目录:
add_library(DocScan SHARED)
set_target_properties(DocScanPROPERTIES IMPORTED_LOCATION${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libDocScan.so)
在java交互类里面添加so包名:
static{System.loadLibrary("DocScan");}
在build.gradle里面添加要调用的库:
ndk {moduleName "DocScan"abiFilters "armeabi-v7a", "arm64-v8a"}
三、 API文档
1.Java交互类
在交互Java交互类ScanJiaSim.java中添加调用接口:
//初始化算法类,boolean useGPU——是否启用gpu加速public native boolean init(AssetManager mgr,boolean useGPU);//通用文档边缘检测,Bitmap bitmap——传入图像,返回PointI是检测到的四个点public native PointI edgeDetector(Bitmap bitmap);//书本边缘检测,Bitmap bitmap——传入图像,返回PointI是检测到的四个点public native PointI bookEdgeDetect(Bitmap bitmap);//边缘校正,Bitmap bitmap——传入图像,返回校正后的图像,如果校正的点没有手动更新,则使用边缘检测到的点进行校正public native Bitmap reveseEdge(Bitmap bitmap);//接收手动更新过的边缘点,如果手动更新过边缘点,则调用这个函数把更新的点发回校正函数使用public native int sendPoint(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4);
2.JNI文件
在jni文件ScanJia_jni.cpp中实现交互类定义的接口:
extern "C" JNIEXPORT jboolean JNICALL Java_com_dashu_scanjia_ScanJiaSim_init(JNIEnv* env,
jobject thiz, jobject assetManager,jboolean cpu_gpu);extern "C" JNIEXPORT jobject JNICALL Java_com_dashu_scanjia_ScanJiaSim_edgeDetector(JNIEnv *env,jobject thiz, jobject b_image);extern "C" JNIEXPORT jobject JNICALLJava_com_dashu_scanjia_ScanJiaSim_bookEdgeDetect(JNIEnv *env,jobject thiz, jobject b_image);extern "C" JNIEXPORT jobject JNICALL Java_com_dashu_scanjia_ScanJiaSim_reveseEdge(JNIEnv *env,jobject, jobject image);extern "C" JNIEXPORT int JNICALL Java_com_dashu_scanjia_ScanJiaSim_sendPoint(JNIEnv *env, jobject instance,int x1,int
y1,int x2,int y2,int x3,int y3,int x4,int y4)
3.算法代码实现
在cpp算法代码中实现接口:
/// 读取模型/// \param mgr /// \param edge_model_parma -边缘模型路径/// \param edge_model_bin -边缘模型路径/// \param mid_model_parma - 书本中线模型路径/// \param mid_model_bin - 书本中线模型路径/// \param use_gpu -是否启用GPU推理/// \return int read_model(AAssetManager* mgr,std::string edge_model_parma = "ED210113FP16.param",std::string edge_model_bin = "ED210113FP16.bin",std::string mid_model_parma = "M20210325F.param",std::string mid_model_bin = "M20210325F.bin",bool use_gpu = true);/// 边缘检测/// \param cv_src -原图像/// \param points_out -检测到的点集/// \param is_book -是否是书本/// \return int detect(cv::Mat cv_src, std::vector<cv::Point>& points_out, bool is_book);/// 图像校正/// \param cv_src -原图像/// \param cv_dst -结果图像/// \param in_points -校正点集/// \return int revise_image(cv::Mat& cv_src, cv::Mat& cv_dst, std::vector<cv::Point>& in_points);
4.调用接口
在MainActivity.java类中调用接口:
//实例化接口类
private ScanJiaSim scan_jia_sim = new ScanJiaSim();//初始化类,根据匹配的机型选择是否启用GPU,启用状态只是参考,最终是否能启用是基于底层是否能检测到GPU
boolean ret_init = scan_jia_sim.init(getAssets(),use_gpu);//调用边缘检测
ScanJiaSim.PointI point = scan_jia_sim.edgeDetector(b_image);//调用书本边缘检测
ScanJiaSim.PointI point = scan_jia_sim.bookEdgeDetect(b_image);//图像校正,如果手动更新过边缘点,则要先调用upPoint()函数
Bitmap bitmap = scan_jia_sim.reveseEdge(b_image);//手动更新过边缘点,则在校正之前把边缘点传入
private void upPoint(Point p1, Point p2, Point p3, Point p4) throws IOException
四、实现效果



相关文章:
安卓NDK视觉开发——手机拍照文档边缘检测实现方法与库封装
一、项目创建 创建NDK项目有两种方式,一种从新创建整个项目,一个在创建好的项目添加NDK接口。 1.创建NDK项目 创建 一个Native C项目: 选择包名、API版本与算法交互的语言: 选择C版本: 创建完之后,可…...
第二届 Sui 游戏峰会将于 3 月 18 日在旧金山举行
3 月中旬,Sui 基金会和 Mysten Labs 将共同举办第二届 Sui 游戏峰会(Sui Gaming Summit),这是一个专注于 Sui 游戏平台的 GDC 周边活动。此次峰会将与旧金山的年度游戏开发者大会(GDC,Game Developers Conf…...
自动驾驶相关知识学习笔记
一、概要 因为想知道SIL、HIL是什么仿真工具,故而浏览了自动驾驶相关的知识。 资料来源《自动驾驶——人工智能理论与实践》胡波 林青 陈强 著;出版时间:2023年3月 二、图像的分类、分割与检测任务区别 如图所示,这些更高阶的…...
uniapp - 基于uniapp+vue3实现自定义增强版table表格组件体验「兼容H5+小程序+App端」
本文提供增强版table表格组件体验,打造跨端表格的新标杆. uv3-table:一款基于uniappvue3跨端自定义手机端增强版表格组件。支持固定表头/列、边框、斑马纹、单选/多选,自定义表头/表体插槽、左右固定列阴影高亮显示。支持编译兼容H5小程序端App端。 提供…...
新时期下k8s 网络插件calico 安装
1、k8s master节点初始化完毕以后一直处于notreadey状态,一直怀疑是安装有问题或者是初始化有问题(当然,如果真有问题要先解决这些问题),经过不断探索才发现是网络插件没有安装导致的,根据建议安装calico插…...
【SQL】COUNT()函数 用法详解
COUNT()函数 COUNT函数用法:COUNT ( [ALL | DISTINCT] column | expression | *) ALL关键字指示统计所有值,而DISTINCT关键字强制函数仅对不同的值进行操作。 默认情况下,使用ALL选项。条件表达式 COUNT()函数中条件表达式加 OR null。例如…...
【HTML+CSS+JS+VUE】web前端教程-6-图片路径详解
绝对路径 绝对路径是电脑盘符存储与访问的具体位置 E:\xxx\1.jpg <img src"E:\xxx\1.jpg">相对路径 两者相对关系,两者在同一路径下可以直接访问 子级关系:/ 父级关系:../ 同级关系: ./网络路径 具体的网络地址:…...
C++中面向对象的三大特性是什么?
封装(Encapsulation) 概念:封装是把数据和操作数据的函数绑定在一起,对数据的访问进行限制。通过将数据成员设为私有(private)或受保护(protected),并提供公共ÿ…...
Centos 修改 yum 源为阿里云
参考 https://serverfault.com/questions/1161816/mirrorlist-centos-org-no-longer-resolve 修改 Centos 的 yum 源为阿里云 去阿里云 yum 镜像源官网: https://developer.aliyun.com/mirror/ 选择自己对应的操作系统,这里以 centos7 演示…...
Qt之Cannot create children for a parent that is in a different thread问题分析
问题 在多线程场景中,使用QSerialPort,QTcpSocket等QIODevice设备时出现报Cannot create children for a parent that is in a different thread 分析 QObject构造函数中会检查父对象的线程数据与当前对象的线程数据是否一致 static bool check_parent_thread(Q…...
均值滤波从图像复原角度的解释
廖老师说若将图像生成看作一个随机过程,均值滤波(Mean Filtering)可以视为在高斯噪声模型下的线性最小均方估计(Linear Minimum Mean Squared Error, LMMSE)或者极大似然估计(Maximum Likelihood Estimatio…...
Tableau数据可视化与仪表盘搭建-数据连接
目录 连接本地文件 课程操作 连接方式(实时/数据提取) 保存工作簿 筛选器 数据处理 连接数据有三种类型 第一种,连接到本地文件,例如Excel,csv,JSON等 第二种,连接到数据库,例…...
VsCode对Arduino的开发配置
ps:我的情况是在对esp32进行编译、烧录时,找不到按钮,无法识别Arduino文件,适合已经有ini文件的情况。 1.在vscode中安装拓展 2.打开设置,点击右上角,转到settings.json文件 3.复制以下代码并保存 {"…...
2024版idea 插件无法加载
解决方法: 进入Settings 点击plugins 选择 HTTP Proxy Settings 设置成如图所示...
VLMs之Agent之CogAgent:CogAgent的简介、安装和使用方法、案例应用之详细攻略
VLMs之Agent之CogAgent:CogAgent的简介、安装和使用方法、案例应用之详细攻略 导读:在2024年末,智谱于11月29日正式提出了GLM-OS概念,并推出了两款Agent产品——AutoGLM和GLM-PC。为了促进大模型Agent生态的发展,智谱决…...
Unity3D仿星露谷物语开发19之库存栏丢弃及交互道具
1、目标 从库存栏中把道具拖到游戏场景中,库存栏中道具数相应做减法或者删除道具。同时在库存栏中可以交换两个道具的位置。 2、UIInventorySlot设置Raycast属性 在UIInventorySlot中,我们只希望最外层的UIInventorySlot响应Raycast,他下面…...
Kafka优势剖析-消费者组、并行消费
目录 1. 消费者组(Consumer Group) 1.1 什么是消费者组? 1.2 消费者组的工作原理 1.3 消费者组的优势 2. 并行消费(Parallel Consumption) 2.1 什么是并行消费? 2.2 并行消费的工作原理 2.3 并行消…...
Docker+Jmeter+InfluxDB+Grafana 搭建性能监控平台
当今互联网发展迅速,应用程序的性能监控显得越来越重要。 DockerJmeterInfluxDBGrafana 是一种常用的性能监控平台,可以帮助开发者快速搭建一套可靠的监控体系。在本文中,我们将介绍如何使用这些工具搭建性能监控平台,以便开发人…...
Maven 详细配置:Maven settings 配置文件的详细说明
Maven settings 配置文件是 Maven 环境的重要组成部分,它用于定义用户特定的配置信息和全局设置,例如本地仓库路径、远程仓库镜像、代理服务器以及认证信息等。settings 文件分为全局配置文件(settings.xml)和用户配置文件&#x…...
【文本分类】bert二分类
import os import torch from torch.utils.data import DataLoader, Dataset from transformers import BertTokenizer, BertForSequenceClassification, AdamW from sklearn.metrics import accuracy_score, classification_report from tqdm import tqdm# 自定义数据集 class…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
【java面试】微服务篇
【java面试】微服务篇 一、总体框架二、Springcloud(一)Springcloud五大组件(二)服务注册和发现1、Eureka2、Nacos (三)负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...
