安卓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;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView 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 PROPERTIES
        INTERFACE_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(DocScan
        PROPERTIES 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

四、实现效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950940.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot教程之五十二:CrudRepository 和 JpaRepository 之间的区别

Spring Boot – CrudRepository 和 JpaRepository 之间的区别 Spring Boot建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。由于其快速的生产就绪环境&#xff0c;使开发人员能够直接专注于逻辑&#xff0c;而不必费力配置和设置&#xff0c;因此如今它正成为开发人员…

【网页自动化】篡改猴入门教程

安装篡改猴 打开浏览器扩展商店&#xff08;Edge、Chrome、Firefox 等&#xff09;。搜索 Tampermonkey 并安装。 如图安装后&#xff0c;浏览器右上角会显示一个带有猴子图标的按钮。 创建用户脚本 已进入篡改猴管理面板点击创建 脚本注释说明 name&#xff1a;脚本名称。…

spark汇总

目录 描述运行模式1. Windows模式代码示例 2. Local模式3. Standalone模式 RDD描述特性RDD创建代码示例&#xff08;并行化创建&#xff09;代码示例&#xff08;读取外部数据&#xff09;代码示例&#xff08;读取目录下的所有文件&#xff09; 算子DAGSparkSQLSparkStreaming…

Spring AMQP-保证发送者消息的可靠性

1. 消息发送者的可靠性 保证消息的可靠性可以通过发送者重连和发送者确认来实现 发送者重连 发送者重连机制就是在发送信息的时候如果连接不上mq不会立即结束&#xff0c;而是会在一定的时间间隔之类进行重新连接&#xff0c;连接的次数和时间都是由我们在配置文件中指定的&…

vs2022编译webrtc步骤

1、主要步骤说明 概述&#xff1a;基础环境必须有&#xff0c;比如git&#xff0c;Powershell这些&#xff0c;就不写到下面了。 1.1 安装vs2022 1、选择使用C的桌面开发 2、 Windows 10 SDK安装10.0.20348.0 3、勾选MFC及ATL这两项 4、 安装完VS2022后&#xff0c;必须安…

UnityWebGl:打包成webgl后UGUI不显示文字(中文)问题

是由于unity默认使用的是Arial,导致打包成webgl时中文不显示 解决方案&#xff1a; 可在电脑C盘下&#xff0c;路径为C:\Windows\Fonts 找个中文简体的字体文件放到unity里面&#xff0c;格式必须为. ttf

ffmpeg-avio实战:打开本地文件或者网络直播流dome

使用ffmpeg打开打开本地文件或者网络直播流的一个小dome。流程产靠ffmpeg4.x系列的解码流程-CSDN博客 #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavformat/avio.h> #include <libavutil/file.h> #include &l…

英伟达打造个人 AI 超级计算机:Project DIGITS 震撼登场

手掌大小的超级计算机 Nvidia 在 CES 2025 上为桌面用户推出了 一款大小和手掌差不多的超级计算机——Project DIGITS AI 超级计算机。虽然它的大小和一个手掌差不多&#xff0c;但性能方面可以说是强到惊人。 Project DIGITS Project DIGITS Project DIGITS 搭载全新的 GB10 G…

SAP SD学习笔记26 - 贩卖契约(框架协议)的概要,基本契约 - 数量契约

上一章讲了品目阶层&#xff08;产品层次结构&#xff09;&#xff0c;品揃Module(分类模块) 。 SAP SD学习笔记25 - 品目阶层&#xff08;产品层次结构&#xff09;、品揃Module&#xff08;分类模块&#xff09;-CSDN博客 本章继续讲SAP的知识&#xff1a;贩卖契约&#xff…

ESP32 IDF VScode出现头文件“无法打开 源 文件 ”,并有红色下划线警告

问题背景&#xff1a; ESP32 IDF VScode出现头文件“无法打开 源 文件 ”&#xff0c;并有红色下划线警告&#xff1a; 解决办法&#xff1a; 在工程里面的.vscode文件夹下&#xff0c;检查是否存在c_cpp_properties.json文件&#xff0c;如果没有可以手动创建添加。如图…

GaussDB事务和并发控制机制

目录 一、并发控制机制 二、MVCC实现方式 三、快照实现方式 四、GaussDB的并发控制机制 五、GaussDB基于事务提交时间戳的MVCC和快照机制 六、GaussDB分布式事务 七、总结与展望 事务是数据库的核心功能之一&#xff0c;其主要目的是保障数据库系统在并发处理、系统故障…

【YOLOv8老鼠检测】

YOLOv8老鼠检测 yolo老鼠检测数据集和模型YOLOv8老鼠检测步骤YOLOv8算法说明 yolo老鼠检测数据集和模型 数据集类别信息 train: E:\python_code\dataset_1\yolo_mouse_data_5000\train/images val: E:\python_code\dataset_1\yolo_mouse_data_5000\valid/images test: E:\pyt…

2025最新解决方案:新买的mac鼠标和这个触控板反向

solution1 &#xff1a;1.打开设置&#xff0c;搜索 触控 点击 自然滚动 ----->解决的是 触控板 但是还是解决不了鼠标反向的问题 solution1 ultra&#xff1a; 下载一个免费 且纯净的 软件 Scroll Reverser for macOS 这是给出的链接&#xff0c;非常简单&#xff0c;…

【C++习题】20. 两个数组的交集

题目&#xff1a;349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 链接&#x1f517;&#xff1a;349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 代码&#xff1a; class Solution { public:// 函数功能&#xff1a;求两个数组…

从零开始:使用VSCode搭建Python数据科学开发环境

引言 在数据科学领域&#xff0c;一个高效、稳定的开发环境是成功的关键。本文将详细介绍如何使用Visual Studio Code搭建一个完整的Python数据科学开发环境。通过本指南&#xff0c;您将学会&#xff1a; 安装和配置VSCode&#xff0c;包括基本设置和快捷键配置设置Python开…

JVM vs JDK vs JRE

JVM是Java虚拟机的缩写&#xff0c; 用于实现Java的一次编译&#xff0c;处处运行。 Java代码写成.class后&#xff0c;由本地的虚拟机运行。 JDK&#xff08;Java Development Kit&#xff09;是一个功能齐全的 Java 开发工具包&#xff0c;供开发者使用。 JDK包含了JRE。…

Redis Zset有序集合

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Redis Zset有序集合 收录于专栏[redis] 本专栏旨在分享学习Redis的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 概述 普通命令 ZAD…

【漫话机器学习系列】040.降采样(downsampling)

降采样&#xff08;Downsampling&#xff09; 降采样&#xff08;Downsampling&#xff09; 是一种在数据处理中常见的技术&#xff0c;目的是通过减少数据的数量来简化模型、加快计算速度&#xff0c;或减少存储空间的需求。降采样的核心思想是从原始数据中选取代表性的样本&…

国内使用博查SearchAPI进行智能搜索,通过API获取搜索引擎的天气、日历、百科、手机、火车票等信息

在现代开发中&#xff0c;网络资源搜索是关键且常见的需求。博查SearchAPI作为国内领先的智能搜索解决方案&#xff0c;已服务超过2000家企业和16000名开发者&#xff0c;获得腾讯元器、字节扣子、阿里钉钉等官方推荐。该API提供近百亿网页内容及多样的生态合作内容&#xff0c…

前端学习DAY33(外边距的折叠)

垂直外边距的重叠 在网页中相邻的垂直方向的外边距&#xff0c;会发生外边距的重叠 兄弟元素 兄弟元素之间的相邻外边距会取&#xff08;绝对值&#xff09;最大值&#xff0c;而不是取和&#xff0c;谁大取谁 特殊情况&#xff1a;如果相邻的外边距一正一负&#xff0c;则取两…