赞
踩
在Android Studio官网下载编译工具时,会出现无法下载的问题,可右键复制下载链接IDMan中进行下载。
安装过程中,需要将Android Virtual Device勾选,否则无法使用虚拟机。
安装启动后,会提示没有SDK,设置代码,直接选择cancel键。
完后,会有专门的SKD组件的安装,但是会有unavailable不可安装的情况出现,可通过创建项目后配置gradle后便可以安装了。
软件安装后可能出现打不开的情况,可选择以管理员身份启动即可解决问题。
选择New Project
选择喜欢的界面样式即可。
使用语言、SDK根据自行需求进行选择就行。
Build configuration language建议选择Kotlin DSL(build.gradle.kts)[Recommended],否则会出现缺少gradle文件的情况。
创建完后会出现如下项目目录,并不会直接出现app的文件夹,需要手动配置gradle。
按照如下目录gradle/wrapper/gradle-wrapper.properties修改distributionUrl为本地地址。(根据原先的地址下载对应的压缩包)
- #Wed May 01 21:02:04 CST 2024
- distributionBase=GRADLE_USER_HOME
- distributionPath=wrapper/dists
- distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
- zipStoreBase=GRADLE_USER_HOME
- zipStorePath=wrapper/dists
-
- 更变为
- #Wed May 01 21:02:04 CST 2024
- distributionBase=GRADLE_USER_HOME
- distributionPath=wrapper/dists
- # 对应的gradle-8.4-bin.zip本地地址即可
- distributionUrl=file:///D://Android//gradle-8.4-bin.zip
- zipStoreBase=GRADLE_USER_HOME
- zipStorePath=wrapper/dists
在settings.gradle.kts更换阿里源(直接复制粘贴即可)
-
- pluginManagement {
- repositories {
- maven { url=uri ("https://www.jitpack.io")}
- maven { url=uri ("https://maven.aliyun.com/repository/releases")}
- maven { url=uri ("https://maven.aliyun.com/repository/google")}
- maven { url=uri ("https://maven.aliyun.com/repository/central")}
- maven { url=uri ("https://maven.aliyun.com/repository/gradle-plugin")}
- maven { url=uri ("https://maven.aliyun.com/repository/public")}
-
- google()
- mavenCentral()
- gradlePluginPortal()
- }
- }
- dependencyResolutionManagement {
- repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
- repositories {
- maven { url=uri ("https://www.jitpack.io")}
- maven { url=uri ("https://maven.aliyun.com/repository/releases")}
- maven { url=uri ("https://maven.aliyun.com/repository/google")}
- maven { url=uri ("https://maven.aliyun.com/repository/central")}
- maven { url=uri ("https://maven.aliyun.com/repository/gradle-plugin")}
- maven { url=uri ("https://maven.aliyun.com/repository/public")}
-
-
- google()
- mavenCentral()
- }
- }
-
- rootProject.name = "Helloword"
- include(":app")
在build.gradle.kts中点击sync now即可自动配置,稍等即可便可变成app文件夹的形式。
选择Project,变成全部文件的形式。
初始新建项目即刻完成。
需将训练好的.pth文件转化为.pt文件
- """
- 该程序使用的是resnet32网络,用到其他网络可自行更改
- 保存的权重字典目录如下所示。
- ckpt = {
- 'weight': model.state_dict(),
- 'epoch': epoch,
- 'cfg': opt.model,
- 'index': name
- }
- """
- from models.resnet_cifar import resnet32 # 确保引用你的正确模型架构
- import torch
- import torch.nn as nn
- # 假设你的ResNet定义在resnet.py文件中
- model = resnet32()
- num_ftrs = model.fc.in_features
- model.fc = nn.Linear(num_ftrs, 100) # 修改这里的100为你的类别数
-
- # 加载权重
- checkpoint = torch.load('modelleader_best.pth', map_location=torch.device('cpu'))
- model.load_state_dict(checkpoint['weight'], strict=False) # 使用strict=False可以忽略不匹配的键
-
- model.eval()
- # 将模型转换为TorchScript
- example_input = torch.rand(1, 3, 32, 32) # 修改这里以匹配你的模型输入尺寸
- traced_script_module = torch.jit.trace(model, example_input)
- traced_script_module.save("model.pt")
在如下目录下创建assets文件,将转化好的模型放在里面即可,切记不可直接创建文件夹,会出现找不到模型问题。
在com/example/myapplication下创建了两个类cifarClassed,MainActivity。
MainActivity类
- package com.example.myapplication;
-
- import android.content.Context;
- import android.content.Intent;
- import android.content.pm.PackageManager;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.os.Bundle;
- import android.provider.MediaStore;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.TextView;
-
- import androidx.annotation.NonNull;
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.core.app.ActivityCompat;
- import androidx.core.content.ContextCompat;
- import androidx.core.content.FileProvider;
-
- import org.pytorch.IValue;
- import org.pytorch.Module;
- import org.pytorch.Tensor;
- import org.pytorch.torchvision.TensorImageUtils;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
-
- public class MainActivity extends AppCompatActivity {
- private static final int PERMISSION_REQUEST_CODE = 101;
-
- private static final int REQUEST_IMAGE_CAPTURE = 1;
- private static final int REQUEST_IMAGE_SELECT = 2;
- private ImageView imageView;
- private TextView textView;
- private Module module;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- // 检查相机权限
- if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA}, PERMISSION_REQUEST_CODE);
- }
-
- imageView = findViewById(R.id.image);
- textView = findViewById(R.id.text);
- ImageView logoImageView = findViewById(R.id.logo);
- logoImageView.setImageResource(R.drawable.logo);
-
-
- Button takePhotoButton = findViewById(R.id.button_take_photo);
- Button selectImageButton = findViewById(R.id.button_select_image);
-
- takePhotoButton.setOnClickListener(v -> dispatchTakePictureIntent());
- selectImageButton.setOnClickListener(v -> dispatchGalleryIntent());
-
- try {
- module = Module.load(assetFilePath(this, "model.pt"));
- } catch (IOException e) {
- Log.e("PytorchHelloWorld", "Error reading assets", e);
- finish();
- }
- }
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == PERMISSION_REQUEST_CODE) {
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- // 权限被授予
- Log.d("Permissions", "Camera permission granted");
- } else {
- // 权限被拒绝
- Log.d("Permissions", "Camera permission denied");
- }
- }
- }
- private void dispatchTakePictureIntent() {
- Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
- startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
- }
- }
-
- private void dispatchGalleryIntent() {
- Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
- startActivityForResult(intent, REQUEST_IMAGE_SELECT);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK && (requestCode == REQUEST_IMAGE_CAPTURE || requestCode == REQUEST_IMAGE_SELECT)) {
- Bitmap imageBitmap = null;
- if (requestCode == REQUEST_IMAGE_CAPTURE) {
- Bundle extras = data.getExtras();
- imageBitmap = (Bitmap) extras.get("data");
- } else if (requestCode == REQUEST_IMAGE_SELECT) {
- try {
- imageBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), data.getData());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- imageView.setImageBitmap(imageBitmap);
- classifyImage(imageBitmap);
- }
- }
-
- // private void classifyImage(Bitmap bitmap) {
- // Tensor inputTensor = TensorImageUtils.bitmapToFloat32Tensor(bitmap,
- // TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, TensorImageUtils.TORCHVISION_NORM_STD_RGB);
- // Tensor outputTensor = module.forward(IValue.from(inputTensor)).toTensor();
- // float[] scores = outputTensor.getDataAsFloatArray();
- // float maxScore = -Float.MAX_VALUE;
- // int maxScoreIdx = -1;
- // for (int i = 0; i < scores.length; i++) {
- // if (scores[i] > maxScore) {
- // maxScore = scores[i];
- // maxScoreIdx = i;
- // }
- // }
- // textView.setText("推理结果:" + CifarClassed.IMAGENET_CLASSES[maxScoreIdx]);
- // textView.setVisibility(View.VISIBLE); // 设置 TextView 可见
- // }
- // private void classifyImage(Bitmap bitmap) {
- // // 调整图像大小为 32x32 像素
- // Bitmap resizedBitmap = resizeBitmap(bitmap, 32, 32);
- //
- // // 将调整大小后的图像转换为 PyTorch Tensor
- // Tensor inputTensor = TensorImageUtils.bitmapToFloat32Tensor(resizedBitmap,
- // new float[]{0.485f, 0.456f, 0.406f}, // 均值 Mean
- // new float[]{0.229f, 0.224f, 0.225f}); // 标准差 Std
- //
- // // 推理
- // Tensor outputTensor = module.forward(IValue.from(inputTensor)).toTensor();
- // float[] scores = outputTensor.getDataAsFloatArray();
- // float maxScore = -Float.MAX_VALUE;
- // int maxScoreIdx = -1;
- // for (int i = 0; i < scores.length; i++) {
- // if (scores[i] > maxScore) {
- // maxScore = scores[i];
- // maxScoreIdx = i;
- // }
- // }
- // textView.setText("推理结果:" + CifarClassed.IMAGENET_CLASSES[maxScoreIdx]);
- // textView.setVisibility(View.VISIBLE); // 设置 TextView 可见
- // }
- //
- private float[] softmax(float[] scores) {
- float max = Float.NEGATIVE_INFINITY;
- for (float score : scores) {
- if (score > max) max = score;
- }
- float sum = 0.0f;
- float[] exps = new float[scores.length];
- for (int i = 0; i < scores.length; i++) {
- exps[i] = (float) Math.exp(scores[i] - max); // 减去最大值防止指数爆炸
- sum += exps[i];
- }
- for (int i = 0; i < exps.length; i++) {
- exps[i] /= sum; // 归一化
- }
- return exps;
- }
-
- // 图像分类方法
- private void classifyImage(Bitmap bitmap) {
- // 调整图像大小为 32x32 像素
- Bitmap resizedBitmap = resizeBitmap(bitmap, 32, 32);
-
- // 将调整大小后的图像转换为 PyTorch Tensor
- Tensor inputTensor = TensorImageUtils.bitmapToFloat32Tensor(resizedBitmap,
- new float[]{0.485f, 0.456f, 0.406f}, // 使用训练时相同的均值 Mean
- new float[]{0.229f, 0.224f, 0.225f}); // 使用训练时相同的标准差 Std
-
- // 推理
- Tensor outputTensor = module.forward(IValue.from(inputTensor)).toTensor();
- float[] scores = outputTensor.getDataAsFloatArray();
- // 应用自定义的 Softmax 函数获取概率分布
- float[] probabilities = softmax(scores);
- float maxScore = -Float.MAX_VALUE;
- int maxScoreIdx = -1;
- for (int i = 0; i < probabilities.length; i++) {
- if (probabilities[i] > maxScore) {
- maxScore = probabilities[i];
- maxScoreIdx = i;
- }
- }
-
- // 更新 UI 必须在主线程中完成
- final int maxIndex = maxScoreIdx;
- final float finalMaxScore = maxScore;
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- textView.setText("推理结果:" + CifarClassed.IMAGENET_CLASSES[maxIndex] + " (" + String.format("%.2f%%", finalMaxScore * 100) + ")");
- textView.setVisibility(View.VISIBLE); // 设置 TextView 可见
- }
- });
- }
-
- ///
-
- //
- // 方法来调整 Bitmap 的大小
- private Bitmap resizeBitmap(Bitmap originalBitmap, int targetWidth, int targetHeight) {
- return Bitmap.createScaledBitmap(originalBitmap, targetWidth, targetHeight, false);
- }
-
- public static String assetFilePath(Context context, String assetName) throws IOException {
- File file = new File(context.getFilesDir(), assetName);
- if (file.exists() && file.length() > 0) {
- return file.getAbsolutePath();
- }
-
- try (InputStream is = context.getAssets().open(assetName)) {
- try (OutputStream os = new FileOutputStream(file)) {
- byte[] buffer = new byte[4 * 1024];
- int read;
- while ((read = is.read(buffer)) != -1) {
- os.write(buffer, 0, read);
- }
- os.flush();
- }
- return file.getAbsolutePath();
- }
- }
-
- }
CifarClassed类
- package com.example.myapplication;
-
-
- public class CifarClassed {
- public static String[] IMAGENET_CLASSES = new String[]{
- "apple", "aquarium_fish", "baby", "bear", "beaver", "bed", "bee", "beetle",
- "bicycle", "bottle", "bowl", "boy", "bridge", "bus", "butterfly", "camel",
- "can", "castle", "caterpillar", "cattle", "chair", "chimpanzee", "clock",
- "cloud", "cockroach", "couch", "crab", "crocodile", "cup", "dinosaur",
- "dolphin", "elephant", "flatfish", "forest", "fox", "girl", "hamster", "house",
- "kangaroo", "keyboard", "lamp", "lawn_mower", "leopard", "lion", "lizard",
- "lobster", "man", "maple_tree", "motorcycle", "mountain", "mouse", "mushroom",
- "oak_tree", "orange", "orchid", "otter", "palm_tree", "pear", "pickup_truck",
- "pine_tree", "plain", "plate", "poppy", "porcupine", "possum", "rabbit", "raccoon",
- "ray", "road", "rocket", "rose", "sea", "seal", "shark", "shrew", "skunk",
- "skyscraper", "snail", "snake", "spider", "squirrel", "streetcar", "sunflower",
- "sweet_pepper", "table", "tank", "telephone", "television", "tiger", "tractor",
- "train", "trout", "tulip", "turtle", "wardrobe", "whale", "willow_tree", "wolf",
- "woman", "worm"
- };
- }
-
页面布局存放在MyApplication\app\src\main\res\layout\activity_main.xml文件中。
- <FrameLayout 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"
- tools:context=".MainActivity"
- android:background="#F0F0F0">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:orientation="vertical"
- android:gravity="center">
-
- <ImageView
- android:id="@+id/image"
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:scaleType="centerCrop"
- android:elevation="2dp" />
-
- <!-- 推理结果显示在图片与按钮之间的空白区域 -->
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="24sp"
- android:textColor="#FFF"
- android:gravity="center"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
- android:visibility="gone" /> <!-- 初始状态隐藏 -->
- </LinearLayout>
-
- <!-- 按钮位于屏幕底部 -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_gravity="bottom"
- android:elevation="4dp">
-
- <Button
- android:id="@+id/button_take_photo"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="拍照"
- android:backgroundTint="#FF6200EE"
- android:textColor="#FFFFFF"
- android:layout_margin="8dp"
- android:elevation="2dp"
- android:stateListAnimator="@null"/>
-
- <Button
- android:id="@+id/button_select_image"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="选择图片"
- android:backgroundTint="#FF018786"
- android:textColor="#FFFFFF"
- android:layout_margin="8dp"
- android:elevation="2dp"
- android:stateListAnimator="@null"/>
- </LinearLayout>
-
- <!-- 调整商标为小圆形并放置在顶部中间 -->
- <!-- 调整商标为小圆形并放置在顶部中间使用 CircleImageView -->
- <de.hdodenhof.circleimageview.CircleImageView
- android:id="@+id/logo"
- android:src="@drawable/logo"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:layout_gravity="top|center_horizontal"
- android:layout_marginTop="16dp"
- android:elevation="5dp"/>
- </FrameLayout>
在MyApplication\app\src\main\res\drawable\circle_shape.xml(自行创建)
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
- <solid android:color="#FFFFFF"/> <!-- 修改颜色以匹配你的需求 -->
- <size
- android:width="50dp"
- android:height="50dp"/> <!-- 定义圆的尺寸,确保它与 ImageView 的尺寸相匹配 -->
- </shape>
在MyApplication\app\src\main\res\drawable\rounded_background(自行创建)
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#FFFFFF"/> <!-- 背景色,半透明黑 -->
- <corners android:radius="10dp"/> <!-- 圆角的大小 -->
- </shape>
在MyApplication\app\src\main\AndroidManifest.xml添加相机与读取照片的权限。
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools">
- <uses-feature android:name="android.hardware.camera" android:required="true"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.CAMERA" />
-
-
-
-
- <application
- android:allowBackup="true"
- android:dataExtractionRules="@xml/data_extraction_rules"
- android:fullBackupContent="@xml/backup_rules"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
- android:supportsRtl="true"
- android:theme="@style/Theme.MyApplication"
- tools:targetApi="31">
- <activity
- android:name=".MainActivity"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- </manifest>
app级别build.gradle.kts(MyApplication\app\build.gradle.kts)配置如下。
- plugins {
- alias(libs.plugins.androidApplication)
- }
-
- android {
- namespace = "com.example.myapplication"
- compileSdk = 34
- sourceSets {
- getByName("main") {
- jniLibs.srcDir("libs")
- }
- }
-
- packaging {
- resources.excludes.add("META-INF/*")
- }
-
-
- defaultConfig {
- applicationId = "com.example.myapplication"
- minSdk = 24
- targetSdk = 34
- versionCode = 1
- versionName = "1.0"
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
- }
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
- }
-
-
- dependencies {
- // 使用 alias 来指定库,确保 libs.aliases.gradle 中已经定义了这些别名
- implementation(libs.appcompat)
- implementation(libs.material)
- implementation(libs.activity)
- implementation(libs.constraintlayout)
- testImplementation(libs.junit)
- androidTestImplementation(libs.ext.junit)
- androidTestImplementation(libs.espresso.core)
- implementation("org.pytorch:pytorch_android:1.12.1")
- implementation("org.pytorch:pytorch_android_torchvision:1.12.1")
- implementation("com.google.android.exoplayer:exoplayer:2.14.1")
- implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
- implementation("androidx.activity:activity:1.2.0")
- implementation("androidx.fragment:fragment:1.3.0")
- implementation("de.hdodenhof:circleimageview:3.1.0")
-
-
-
-
- }
这段可解决如下bug。
- packaging {
- resources.excludes.add("META-INF/*")
- }
Caused by: com.android.builder.merge.DuplicateRelativeFileException: 2 files found with path ‘META-INF/androidx.core_core.version’.
手动添加非常麻烦,因为不止一个文件冲突!!!
完成以上步骤再按下Sync Now完成依赖的配置工作,需在编译器中自行选择虚拟设备。
完成后即可在MainActivity.java文件启动项目。
点击create创建即可,便可得到apk文件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。