Cette fois, je vais utiliser TensorFlowLite sur Android (Java) pour classer les images! Si vous faites une erreur dans le code, etc. et que vous avez des améliorations, veuillez nous en informer!
Selon le Guide TensorFlow Lite,
TensorFlow Lite est un ensemble d'outils permettant d'utiliser les modèles TensorFlow sur les smartphones et les appareils Iot.
** Interpréteur TensorFlow Lite Exécute un modèle spécifiquement optimisé pour de nombreux types de matériel différents, y compris les téléphones mobiles, les périphériques Linux embarqués et les microcontrôleurs. ** **
Vous pouvez transformer votre modèle TensorFlow en un format efficace à utiliser par l'interpréteur de convertisseur de lumière TensorFlow et introduire des optimisations pour améliorer la taille et les performances binaires.
** ⇒ En d'autres termes, c'est une version légère de TensorFlow qui peut être facilement exécutée non seulement sur les PC mais aussi sur les smartphones et les appareils Iot! ** ** À l'avenir, vous pourrez même apprendre avec un simple smartphone! Hou la la!
Préparez un modèle qui a été formé avec TensorFlow. Cette fois, je vais utiliser le modèle hébergé, donc je vais l'omettre!
TensorFlow Lite ne peut pas utiliser le modèle TensorFlow tel quel, alors convertissez-le dans un format dédié (tflite). ~~ Veuillez vous référer à l'article ici pour les méthodes de conversion. ~~ Puisque l'article a été supprimé, je publierai l'article que j'ai écrit et l'article officiel. Méthode de conversion de modèle Article officiel
Cette fois, je vais vous expliquer comment intégrer Android (Java)!
Veuillez utiliser n'importe quel nom pour le nom du projet, etc.! Cette fois, nous utiliserons AndroidX. Si vous cochez "Utiliser les artefacts android x. *", C'est OK. Vous n'êtes pas obligé d'utiliser Android X car il est facultatif.
Dans build.gradle sous le répertoire de l'application
build.gradle(app)
dependencies {
implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'
implementation 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly'
}
Ajouter. À ce rythme, il contient ABI pour tous les processeurs et jeux d'instructions, mais "armeab-v7a" et "arm64-v8a" S'il est inclus, il peut couvrir la plupart des appareils Android, alors configurez-le pour ne pas inclure d'autres ABI. Peu importe s'il est inclus, mais c'est recommandé car cela réduit la taille de l'application.
build.gradle(app)
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
}
Pour ABI, veuillez consulter l'article ici pour une compréhension facile.
Android compresse ce qui se trouve dans le dossier d'actifs, donc si vous placez le modèle dans le dossier d'actifs, il sera compressé et ne pourra pas être lu. Par conséquent, je spécifierai de ne pas compresser le fichier tflite.
build.gradle(app)
android {
defaultConfig {
aaptOptions {
noCompress "tflite"
}
}
}
Placez le modèle et le label_text dans le dossier des ressources. Veuillez télécharger le modèle depuis ici.
Commencez par créer un dossier d'actifs. Copiez le fichier du dossier décompressé. Après la copie, renommez-le en "model.tflite" et "labels.txt".
Ceci termine l'installation du modèle.
Échantillon Android de TensorFlow Lite 3 classes et [ici](https://github.com/tensorflow/examples/blob/master/lite/examples/image_classification/android/app/src/main/java/org/tensorflow/lite/examples/ Copiez Logger.java depuis classification / env / Logger.java). Si vous le copiez simplement, vous obtiendrez une erreur. Réécrivez la destination d'importation de la classe Logger avec Classifier.java.
Classfier.java
import org.tensorflow.lite.Interpreter;
//Supprimer ici importer l'organisation.tensorflow.lite.examples.classification.env.Logger;
import org.tensorflow.lite.gpu.GpuDelegate;
/** A classifier specialized to label images using TensorFlow Lite. */
public abstract class Classifier {
private static final Logger LOGGER = new Logger();
Si vous le supprimez, Android Studio vous demandera quelque chose comme ça, donc si vous appuyez sur "Alt + Entrée", il sera importé automatiquement. Lors de l'importation
Je pense qu'il y aura deux types, alors sélectionnez celui qui ne dit pas <Android API ~ Platform> (android.jar).
Je pense que toutes les erreurs ont maintenant disparu.
ClassifierFloatMobileNet.java ClassifierQuantizedMobileNet.java Modification de la partie de chargement du modèle qui est commune aux deux
*** Original ***
java:ClassifierFloatMobileNet.java,ClassifierQuantizedMobileNet.java
@Override
protected String getModelPath() {
// you can download this file from
// see build.gradle for where to obtain this file. It should be auto
// downloaded into assets.
return "mobilenet_v1_1.0_224.tflite";
}
*** Après le changement ***
ClassifierFloatMobileNet.java,ClassifierQuantizedMobileNet
@Override
protected String getModelPath() {
// you can download this file from
// see build.gradle for where to obtain this file. It should be auto
// downloaded into assets.
return "model.tflite";
}
Organisez TextView, Button, ImageView comme ceci. Réglez surCliquez sur Bouton et appuyez dessus ↑ Est-il préférable pour les auditeurs de définir onClick? S'il vous plaît dites-moi une personne détaillée
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="select"
android:text="Sélectionnez une image" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:srcCompat="@tools:sample/avatars" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Commençons par déclarer les variables à utiliser!
MainActivity.java
ImageView imageView;
TextView textView;
Classifier classifier;
private static final int RESULT_IMAGEFILE = 1001; //Code de demande utilisé lors de l'acquisition d'images
Associez textview et ImageView dans onCreate.
MainActivity.java
imageView = findViewById(R.id.imageView);
textView = findViewById(R.id.textView);
Appelez ensuite Classfier.
MainActivity.java
try {
classifier = Classifier.create(this,QUANTIZED,CPU,2);
} catch (IOException e) {
e.printStackTrace();
}
Les arguments spécifient l'Acritivy, le type de modèle, le périphérique utilisé pour le calcul et le nombre de threads à utiliser. En gros, ce paramètre fonctionnera, mais modifions-le de manière flexible.
Après avoir appuyé sur le bouton, ouvrez la galerie et ignorez l'intention afin de pouvoir sélectionner l'image.
MainAcritivy.java
public void image(View V){
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, RESULT_IMAGEFILE);
}
Pour plus d'informations à ce sujet, cliquez ici (https://qiita.com/yukiyamadajp/items/137d15a4e65ed2308787)
Lorsque vous revenez de la galerie, récupérez l'image et traitez-la.
MainAcritivty.java
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == RESULT_IMAGEFILE && resultCode == Activity.RESULT_OK) {
if (resultData.getData() != null) {
ParcelFileDescriptor pfDescriptor = null;
try {
Uri uri = resultData.getData();
pfDescriptor = getContentResolver().openFileDescriptor(uri, "r");
if (pfDescriptor != null) {
FileDescriptor fileDescriptor = pfDescriptor.getFileDescriptor();
Bitmap bmp = BitmapFactory.decodeFileDescriptor(fileDescriptor);
pfDescriptor.close();
int height = bmp.getHeight();
int width = bmp.getWidth();
while (true) {
int i = 2;
if (width < 500 && height < 500) {
break;
} else {
if (width > 500 || height > 500) {
width = width / i;
height = height / i;
} else {
break;
}
i++;
}
}
Bitmap croppedBitmap = Bitmap.createScaledBitmap(bmp, width, height, false);
imageView.setImageBitmap(croppedBitmap);
List<Classifier.Recognition> results = classifier.recognizeImage(croppedBitmap,classfier);
String text;
for (Classifier.Recognition result : results) {
RectF location = result.getLocation();
Float conf = result.getConfidence();
String title = result.getTitle();
text += title + "\n";
}
textView.setText(text);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (pfDescriptor != null) {
pfDescriptor.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
Puisqu'il est long, je vais l'expliquer séparément.
Ce code est appelé lorsque vous revenez à l'activité pour déterminer si elle est revenue de la galerie.
MainAcrivity.java
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == RESULT_IMAGEFILE && resultCode == Activity.RESULT_OK) {
}
}
Ce code obtient l'URI de la valeur de retour et prend les données du fichier avec ParceFileDescriptor. Puisque vous pouvez obtenir un tel URI "content: //com.android.providers.media.documents/document/image%3A325268", j'obtiens l'image d'ici.
MainAcrivity.java
if (resultData.getData() != null) {
ParcelFileDescriptor pfDescriptor = null;
try {
Uri uri = resultData.getData();
pfDescriptor = getContentResolver().openFileDescriptor(uri, "r");
if (pfDescriptor != null) {
FileDescriptor fileDescriptor = pfDescriptor.getFileDescriptor();
Ce code convertit l'image obtenue précédemment en bitmap afin que la taille de l'image soit inférieure à 300.
Si l'image est plus grande que 300, elle ne peut pas être jugée normalement et elle sera supprimée en raison d'une erreur.
Caused by: java.lang.ArrayIndexOutOfBoundsException
Par conséquent, tout en conservant le rapport hauteur / largeur, le rapport hauteur / largeur est maintenu à 300.
MainAcrivity.java
Bitmap bmp = BitmapFactory.decodeFileDescriptor(fileDescriptor);
pfDescriptor.close();
if (!bmp.isMutable()) {
bmp = bmp.copy(Bitmap.Config.ARGB_8888, true);
}
int height = bmp.getHeight();
int width = bmp.getWidth();
while (true) {
int i = 2;
if (width < 300 && height < 300) {
break;
} else {
if (width > 300 || height > 300) {
width = width / i;
height = height / i;
} else {
break;
}
i++;
}
}
Bitmap croppedBitmap = Bitmap.createScaledBitmap(bmp, width, height, false);
C'est finalement un jugement. Ce code discrimine l'image traitée et la reçoit dans sa propre liste. Ensuite, la liste est tournée avec pour pour obtenir le résultat et l'afficher dans le textView. Cette fois, seul le nom de l'élément déterminé est affiché, mais vous pouvez également avoir la possibilité qu'il s'agisse d'un élément.
MainAcrivity.java
List<Classifier.Recognition> results = classifier.recognizeImage(croppedBitmap);
String text="";
for (Classifier.Recognition result : results) {
/*
RectF location = result.getLocation();
Float conf = result.getConfidence();
*/
String title = result.getTitle();
text += title + "\n";
}
textView.setText(text);
C'est tout! !!
Ensuite, j'aimerais vraiment le déplacer! Tout d'abord, l'image du chien Banc de parc ... clou,,, Caméléon américain ... Hmm La précision est subtile Vient ensuite une image d'un beau paysage. Le paysage urbain de Delft
Écran de fenêtre ... Paillasson ... aveugle,,, Hmm Non!
La précision était subtile, mais bon? J'ai pu classer les images! La prochaine fois, j'aimerais classer en temps réel! À bientôt!