您的位置:首页 > 文旅 > 美景 > 镇江详情设计_山西手机版建站系统信息_推广普通话手抄报图片_百度推广基木鱼

镇江详情设计_山西手机版建站系统信息_推广普通话手抄报图片_百度推广基木鱼

2025/3/31 16:53:40 来源:https://blog.csdn.net/qq_56460466/article/details/146554989  浏览:    关键词:镇江详情设计_山西手机版建站系统信息_推广普通话手抄报图片_百度推广基木鱼
镇江详情设计_山西手机版建站系统信息_推广普通话手抄报图片_百度推广基木鱼

这两天尝试把pytorch构建的yolov5模型和opencv集成到安卓程序中,做个笔记记录一下流程。

使用anaconda新建环境

python版本:3.9

需要下载的库

pytorch和paddlepaddle支持(以下为具体下载版本与机器有关)

pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
conda install paddlepaddle-gpu==3.0.0b1 paddlepaddle-cuda=12.3 -c paddle -c nvidia

p.s.安装库时pytorch和paddlepaddle如果一个是cpu版本一个是gpu版本在可选的paddlepaddle转paddlelite步骤中会报错。

paddlepaddle转换支持

pip install x2paddle paddlelite requests pandas

python程序执行所需的库

pip install opencv-python

python转换程序

pytorch转换

import torch
import cv2
from torchvision.ops import nms
from torchvision import transforms
import numpy as np
import torch.nn as nn# paddlelite不支持silu算子,使用x * sigmoid(x)替换
class replace_lay(nn.Module):def __init__(self):super(replace_lay, self).__init__()self.sigmoid_lay = nn.Sigmoid()def forward(self, x):x = x * self.sigmoid_lay(x)return x# 定义一个函数,递归遍历模型并替换所有 SiLU 为 ReLU
def replace_silu_with_relu(model):# 收集需要替换的层信息layers_to_replace = []def collect_layers(module, prefix=""):for name, sub_module in module.named_children():full_name = f"{prefix}.{name}" if prefix else nameif isinstance(sub_module, nn.SiLU):  # 如果当前模块是 SiLUlayers_to_replace.append((module, name))  # 记录父模块和层名else:# 如果当前模块有子模块,递归调用collect_layers(sub_module, full_name)# 第一步:收集需要替换的层collect_layers(model)# 第二步:批量替换for parent_module, layer_name in layers_to_replace:setattr(parent_module, layer_name, replace_lay())obj_type = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat','traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat','dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack','umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball','kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket','bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple','sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair','couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote','keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book','clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'
]target_index = obj_type.index('dog')origin_img = cv2.imread('img/dog.jpg')
scale_matrix = np.array([origin_img.shape[1] / 224, origin_img.shape[0] / 224, origin_img.shape[1] / 224, origin_img.shape[0] / 224])img_to_tensor_transform = transforms.Compose([# cv2转PLTtransforms.ToPILImage(),# 尺寸转为224*224transforms.Resize((224, 224)),# PLT转Tensortransforms.ToTensor(),# 归一化# transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
x = img_to_tensor_transform(origin_img).unsqueeze(0)print(x)model = torch.hub.load('ultralytics/yolov5', 'yolov5n', pretrained=True)
replace_silu_with_relu(model)model.eval()
result = model(x).cpu().detach().numpy()[0]
print(result.shape)confidence = result[:, 4]
scores = confidence * result[:, 5 + target_index]
mask = scores > 0.5
boxes = result[mask][:, :4]
boxes = torch.tensor(boxes)
scores = torch.tensor(scores[mask])
boxes_indices = nms(boxes, scores, 0.5)
boxes = (boxes.numpy()[boxes_indices] * scale_matrix)
boxes = boxes.astype(int)
if len(boxes_indices) == 1:boxes = [boxes]
for box in boxes:x, y, w, h = boxw = int(w / 2)h = int(h / 2)print(x, y, w, h)cv2.rectangle(origin_img, (x - w, y - h), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('result', origin_img)
cv2.waitKey(0)# cv2.resize(origin_img, (224, 224), interpolation=cv2.INTER_CUBIC)# pytorch转paddlepaddle
from x2paddle.convert import pytorch2paddle
pytorch2paddle(model,save_dir="model/yolov5n_paddle",jit_type="trace",input_examples=[torch.randn(1, 3, 224, 224)])# pytorch转paddlelite
from x2paddle.convert import pytorch2paddle
pytorch2paddle(model,'model/android',jit_type="trace",input_examples=[torch.randn(1, 3, 224, 224)],convert_to_lite=True,lite_valid_places="arm",lite_model_type="naive_buffer")

下图为运行结果 model/android下是一步转为paddlelite的结果,model/yolov5n_paddle是pytorch转paddlepaddle的结果

pytorch2paddlelite

paddlepaddle验证并转换(可选)

from model.yolov5n_paddle.x2paddle_code import AutoShape as YOLOV5
import paddle
import cv2
import paddle.vision.transforms as T
import numpy as np
from tools import nmsobj_type = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat','traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat','dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack','umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball','kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket','bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple','sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair','couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote','keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book','clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'
]target_index = obj_type.index('person')origin_img = cv2.imread('img/dog.jpg')
scale_matrix = np.array([origin_img.shape[1] / 224, origin_img.shape[0] / 224, origin_img.shape[1] / 224, origin_img.shape[0] / 224])img_to_tensor_transform = T.Compose([# 尺寸转为224*224T.Resize((224, 224)),# 转TensorT.ToTensor(),])
rgb_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2RGB)
# print(rgb_img)
x = img_to_tensor_transform(rgb_img).unsqueeze(0).cuda()
print(x)paddle.disable_static()
params = paddle.load(r'model\yolov5n_paddle\model.pdparams')
model = YOLOV5()
model.set_dict(params, use_structured_name=True)
model.eval()result = model(x).detach().numpy()[0]print(result)confidence = result[:, 4]
scores = confidence * result[:, 5 + target_index]
mask = scores > 0.5
boxes = result[mask][:, :4]
scores = scores[mask]
# boxes = paddle.to_tensor(boxes, dtype='float32')
# scores = paddle.to_tensor(scores, dtype='float32')
boxes_indices = nms(boxes, scores, 0.5)
boxes = (boxes[boxes_indices] * scale_matrix)
boxes = boxes.astype(int)
for box in boxes:x, y, w, h = boxw = int(w / 2)h = int(h / 2)print(x, y, w, h)cv2.rectangle(origin_img, (x - w, y - h), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('result', origin_img)
cv2.waitKey(0)# 引用Paddlelite预测库
from paddlelite.lite import *
# 1. 创建opt实例
opt=Opt()
# 2. 指定输入模型地址
opt.set_model_dir("model/yolov5n_paddle/inference_model")
# 3. 指定转化类型: arm、x86、opencl、npu
opt.set_valid_places("arm")
# 4. 指定模型转化类型: naive_buffer、protobuf
opt.set_model_type("naive_buffer")
# 5. 输出模型地址
opt.set_optimize_out("model/yolov5n_opt")
# 6. 执行模型优化
opt.run()

结果model/yolov5n_opt.nb就是转换出来的文件跟上面的opt是一样的

paddlepaddle2paddlelite

安卓开发部分

项目构建

项目以paddlelite提供的预编译库为基础构建

paddlelite预编译库下载

预编译库随时间会有改变,这里给出一个参考

android_sdk

压缩报内的demo/java/android/PaddlePredictor就是我们需要的项目框架

编写项目前的准备

启动前修改

查询同一目录下的prepare_demo.bash文件可以得知jar包和os文件的路径

  1. 复制压缩包的java/so/libpaddlelitejni.so到项目框架的app\src\main\jniLibs\arm64-v8a和app\src\main\jniLibs\armeabi-v7a

  2. 复制压缩包的java/jar/PaddlePredictor.jar到项目框架的app\libs

  3. 修改项目的gradle\wrapper\gradle-wrapper.properties

  • services.gradle.org/distributions 改为 mirrors.cloud.tencent.com/gradle 使用腾讯的gradle镜像
  1. 使用androidstudio启动项目

编译报错解决

使用java8

到oracle_java官网下载并安装java8

在setting中选择使用java8 如果你的这里没有需要选择下面的add选项手动添加刚刚下载的java8

指定java8

在app模型的gradle中指定使用java8编译,如果你的项目结构跟我的不一样,点击左上角的下拉选框切换为Project

指定使用java8编译

添加opencv依赖

修改app模块的build.gradle的dependencies,新增依赖

dependencies {implementation 'com.quickbirdstudios:opencv:4.5.3.0'
}

编写安卓程序

编写页面

修改activity_main.xml

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><!--        <ImageView-->
<!--            android:id="@+id/image_view"-->
<!--            android:layout_width="wrap_content"-->
<!--            android:layout_height="wrap_content" />--><org.opencv.android.JavaCameraViewandroid:layout_width="fill_parent"android:layout_height="fill_parent"android:id="@+id/yolo_view" /><!--        <ImageView-->
<!--            android:id="@+id/imageView"-->
<!--            android:layout_width="match_parent"-->
<!--            android:layout_height="wrap_content"-->
<!--            android:scaleType="centerCrop" />--></LinearLayout></android.support.constraint.ConstraintLayout>

编写逻辑处理

新增实体类IndexAndScore存放bounding box的索引 位置 置信度信息
import java.util.ArrayList;
import java.util.List;public class IndexAndScore {private List<Integer> indices = new ArrayList<>();private List<int[]> boxList = new ArrayList<>();private List<Float> score = new ArrayList<>();public IndexAndScore() {}public void add(int index, int[] position, float score) {this.indices.add(index);this.boxList.add(position);this.score.add(score);}public List<int[]> getBoxList() {return boxList;}public void setBoxList(List<int[]> boxList) {this.boxList = boxList;}public List<Integer> getIndices() {return indices;}public List<Float> getScore() {return score;}
}
新增工具类实现非极大值抑制
public class NonMaximumSuppression {/*** 非极大值抑制 (NMS)** @param boxes       边界框列表,每个框格式为 [x1, y1, x2, y2],表示左上角和右下角坐标* @param scores      每个框的置信度分数* @param threshold   IoU 阈值,用于判断是否抑制* @return            经过 NMS 处理后保留的框索引*/public static List<Integer> nms(List<int[]> boxes, final List<Float> scores, float threshold) {// 保存最终保留的框索引List<Integer> pickedIndices = new ArrayList<>();// 创建一个索引数组,并按照分数从高到低排序Integer[] sortedIndices = new Integer[scores.size()];for (int i = 0; i < scores.size(); i++) {sortedIndices[i] = i;}// 使用 Comparator 接口进行排序Arrays.sort(sortedIndices, new Comparator<Integer>() {@Overridepublic int compare(Integer i1, Integer i2) {return Float.compare(scores.get(i2), scores.get(i1)); // 降序排序}});while (sortedIndices.length > 0) {// 当前最高分的框int current = sortedIndices[0];pickedIndices.add(current);// 计算当前框与其他框的 IoUList<Integer> remainingIndices = new ArrayList<>();for (int i = 1; i < sortedIndices.length; i++) {int other = sortedIndices[i];float iou = calculateIoU(boxes.get(current), boxes.get(other));// 如果 IoU 小于阈值,则保留if (iou <= threshold) {remainingIndices.add(other);}}// 更新剩余框sortedIndices = remainingIndices.toArray(new Integer[0]);}return pickedIndices;}/*** 计算两个框的交并比 (IoU)** @param box1 第一个框 [x1, y1, x2, y2]* @param box2 第二个框 [x1, y1, x2, y2]* @return IoU 值*/private static float calculateIoU(int[] box1, int[] box2) {// 计算交集区域的坐标int interX1 = Math.max(box1[0], box2[0]);int interY1 = Math.max(box1[1], box2[1]);int interX2 = Math.min(box1[2], box2[2]);int interY2 = Math.min(box1[3], box2[3]);// 计算交集面积int interArea = Math.max(0, interX2 - interX1) * Math.max(0, interY2 - interY1);// 计算每个框的面积int box1Area = (box1[2] - box1[0]) * (box1[3] - box1[1]);int box2Area = (box2[2] - box2[0]) * (box2[3] - box2[1]);// 计算并集面积int unionArea = box1Area + box2Area - interArea;// 返回 IoUreturn (float) interArea / unionArea;}}
修改MainActivity
package com.baidu.paddle.lite;import android.content.Context;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;import com.baidu.paddle.lite.entity.IndexAndScore;
import com.baidu.paddle.lite.tools.NonMaximumSuppression;import org.opencv.android.CameraActivity;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class MainActivity extends CameraActivity implements CameraBridgeViewBase.CvCameraViewListener2 {private static final String    TAG  = "Main";private static final int inputScale = 224;private static final Scalar BOX_COLOR         = new Scalar(0, 255, 0);private List<String> object_target_list = Arrays.asList("person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat","traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat","dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack","umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball","kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket","bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple","sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair","couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote","keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book","clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush");private Mat mRgba;private Size mInputSize = new Size(inputScale, inputScale);private CameraBridgeViewBase   mOpenCvCameraView;private static final String[] EXTENSIONS_TO_COPY = {"nb", "jpg"};private PaddlePredictor predictor;public MainActivity() {Log.i(TAG, "Instantiated new " + this.getClass());}private static void copyAssetsToSdcard(Context context, File destFolder, String[] extensions) {AssetManager assetManager = context.getAssets();try {// List all files in the assets folder onceString[] assetFiles = assetManager.list("");if (assetFiles == null) return;for (String assetFileName : assetFiles) {// Check if file matches any of the provided extensionsfor (String extension : extensions) {if (assetFileName.endsWith("." + extension)) {File outFile = new File(destFolder, assetFileName);// Skip if file already existsif (outFile.exists()) break;// Copy the file from assets to the destination foldertry (InputStream inputStream = assetManager.open(assetFileName);OutputStream outputStream = new FileOutputStream(outFile)) {byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}}break; // No need to check further extensions}}}} catch (IOException e) {e.printStackTrace();}}/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {Log.i(TAG, "called onCreate");super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 复制assets的内容到手机外存File sdcardDataFolder = this.getExternalFilesDir(null);if (sdcardDataFolder == null) {Log.e(TAG, "Failed to get external storage directory");return;}copyAssetsToSdcard(this, sdcardDataFolder, EXTENSIONS_TO_COPY);if (OpenCVLoader.initDebug()) {Log.i(TAG, "OpenCV loaded successfully");} else {Log.e(TAG, "OpenCV initialization failed!");(Toast.makeText(this, "OpenCV initialization failed!", Toast.LENGTH_LONG)).show();return;}// 实例化 PaddlePredictorMobileConfig config = new MobileConfig();config.setModelFromFile(sdcardDataFolder.getAbsolutePath() + "/opt.nb");config.setPowerMode(PowerMode.LITE_POWER_FULL);config.setThreads(1);predictor = PaddlePredictor.createPaddlePredictor(config);// 为opencv展示框设置监听器mOpenCvCameraView = findViewById(R.id.yolo_view);mOpenCvCameraView.setMaxFrameSize(720, 720);mOpenCvCameraView.setCvCameraViewListener(this);}@Overridepublic void onPause() {super.onPause();if (mOpenCvCameraView != null)mOpenCvCameraView.disableView();}@Overridepublic void onResume() {super.onResume();if (mOpenCvCameraView != null)mOpenCvCameraView.enableView();}@Overrideprotected List<? extends CameraBridgeViewBase> getCameraViewList() {return Collections.singletonList(mOpenCvCameraView);}public void onDestroy() {super.onDestroy();if (mOpenCvCameraView != null)mOpenCvCameraView.disableView();}public void onCameraViewStarted(int width, int height) {mRgba = new Mat();}public void onCameraViewStopped() {mRgba.release();}public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {//        Log.d("camera", "获取到数据");// opencv的读取结果是横向的 进行旋转mRgba = inputFrame.rgba();
//        Imgproc.resize(mRgba, mRgba, new Size(720,720), Imgproc.INTER_CUBIC);mRgba = rotateImage(mRgba);// 记录宽高,并计算缩放比例int rows = mRgba.rows();int cols = mRgba.cols();
//        Log.d(TAG, "onCameraFrame: " + rows + "\t" + cols);int changeRows = rows / inputScale;int changeCols = cols / inputScale;// 获取tensor输入数据float[] inputData = processFrame(mRgba);
//       Log.d("input", Arrays.toString(inputData));// 输入数据到 TensorTensor inputTensor = predictor.getInput(0);inputTensor.resize(new long[]{1, 3, inputScale, inputScale});inputTensor.setData(inputData);// 执行预测器,获取结果predictor.run();Tensor outputTensor = predictor.getOutput(0);float[] outputData = outputTensor.getFloatData();
//        Log.d("output", Arrays.toString(outputData));Map<Integer, IndexAndScore> classes = new HashMap<>();for (int i = 0 ; i < outputData.length / 85; i++) {// 获取每个类别的置信度float confidence = outputData[i * 85 + 4];if (confidence < 0.5) {continue;}
//            Log.d("congraduation", "发现一个物体");// 获取类别及其类别置信度int classId = 0;float max_score = 0;for (int offset = 0; offset < 80; ++offset) {if (outputData[i * 85 + 5 + offset] > max_score) {classId = offset;max_score = outputData[i * 85 + 5 + offset];}}if (max_score < 0.5) {continue;}// 计算坐标int xc = (int) (outputData[i * 85] * changeCols);int yc = (int) (outputData[i * 85 + 1] * changeRows);int w = (int) (outputData[i * 85 + 2] * changeCols);int h = (int) (outputData[i * 85 + 3] * changeRows);int x1 = xc - w / 2;int y1 = yc - h / 2;int x2 = xc + w / 2;int y2 = yc + h / 2;//            Log.d("congraduation", "找到一个目标:" + object_target_list.get(classId));//            Log.d("position", w + "\t" + h + "\t" + w * changeCols + "\t" + h * changeRows);// 新增类别的box信息if (classes.get(classId) == null) {classes.put(classId, new IndexAndScore());}classes.get(classId).add(i, new int[]{x1, y1, x2, y2}, max_score);
//            Imgproc.rectangle(mRgba, new org.opencv.core.Point(x - w, y - h), new org.opencv.core.Point(x +  w, y +  h), BOX_COLOR, 2);}for (Map.Entry<Integer, IndexAndScore> integerIndexAndScoreEntry : classes.entrySet()) {Integer classId = integerIndexAndScoreEntry.getKey();IndexAndScore indexAndScore = integerIndexAndScoreEntry.getValue();// 非极大值抑制List<Integer> pickedIndices = NonMaximumSuppression.nms(indexAndScore.getBoxList(), indexAndScore.getScore(), 0.1f);// 绘制框for (Integer pickedIndex : pickedIndices) {int[] box = indexAndScore.getBoxList().get(pickedIndex);
//                Log.d("position", box[1] + "\t" + box[3] + "\t" + box[0] + "\t" + box[2]);
//                float score = indexAndScore.getScore().get(pickedIndex);Imgproc.rectangle(mRgba, new org.opencv.core.Point(box[0], box[1]), new org.opencv.core.Point(box[2], box[3]), BOX_COLOR, 2);Imgproc.putText(mRgba, object_target_list.get(classId), new org.opencv.core.Point(box[0], box[1]), 3, 1, BOX_COLOR);}}return mRgba;}/*** 处理帧为tensor输入数据* @param rgbaMat 帧数据* @return tensor输入数据*/private float[] processFrame(Mat rgbaMat) {// 转换为 RGBMat rgbMat = new Mat();
//        warpAffine(rgbaMat, rgbMat, rotate, rgbaMat.size());
//        Mat floatMat = new Mat();
//        Mat nornalizeMat = new Mat();Imgproc.cvtColor(rgbaMat, rgbMat, Imgproc.COLOR_RGBA2RGB);
//        int rows = mRgba.rows();
//        int cols = mRgba.cols();//        rgbMat.convertTo(floatMat, CvType.CV_32F);
//        Core.divide(floatMat, new Scalar(255.0), nornalizeMat);
//
//        // 标准化(减去均值并除以标准差)
//        Scalar mean = new Scalar(0.485, 0.456, 0.406); // ImageNet 均值
//        Scalar std = new Scalar(0.229, 0.224, 0.225);  // ImageNet 标准差
//
//        Mat normalizedImage = new Mat();
//        Core.subtract(nornalizeMat, mean, normalizedImage);
//        Core.divide(normalizedImage, std, normalizedImage);//        // 将图像数据转换为 float 数组
//        float[] imageData = new float[(int) (normalizedImage.total() * normalizedImage.channels())];
//        normalizedImage.get(0, 0, imageData);
//
//        return imageData;// 调整尺寸Imgproc.resize(rgbMat, rgbMat, new Size(inputScale, inputScale), Imgproc.INTER_CUBIC);float[] inputData = new float[3 * inputScale * inputScale];for (int i = 0; i < inputScale; i++) {for (int j = 0; j < inputScale; j++) {double[] pixel = rgbMat.get(i, j);inputData[0 * inputScale * inputScale + i * inputScale + j] = (float)pixel[0] / 255.0f; // (float) ((pixel[0] - 0.485) / 0.229); // RinputData[1 * inputScale * inputScale + i * inputScale + j] = (float)pixel[1] / 255.0f; // (float) ((pixel[1] - 0.456) / 0.224); // GinputData[2 * inputScale * inputScale + i * inputScale + j] = (float)pixel[2] / 255.0f; // (float) ((pixel[2] - 0.406) / 0.225); // B}}return inputData;}/*** 旋转图像* @param src 原图像* @return 旋转后的图像*/public static Mat rotateImage(Mat src) {Mat dst = new Mat();Point center = new Point(src.cols() / 2, src.rows() / 2);Mat rotationMatrix = Imgproc.getRotationMatrix2D(center, 270, 1.0);Size size = new Size(src.cols(), src.rows());Imgproc.warpAffine(src, dst, rotationMatrix, size);return dst;}}
在AndroidManifest.xml中添加权限请求信息

mainfest标签中添加

 <supports-screens android:resizeable="true"android:smallScreens="true"android:normalScreens="true"android:largeScreens="true"android:anyDensity="true" /><uses-permission android:name="android.permission.CAMERA"/><uses-feature android:name="android.hardware.camera" android:required="false"/><uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/><uses-feature android:name="android.hardware.camera.front" android:required="false"/><uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>

模型复制

把之前转换的模型移动到assets目录下注意加载的名称要对应

模型复制到安卓

手机进入开发者模式打开usb调试即可运行程序

运行结果

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com