目次
序文
このプロジェクトは Keras 深層学習モデルに依存しており、手話をリアルタイムで分類および認識するように設計されています。この目標を達成するために、このプロジェクトでは OpenCV ライブラリの関連アルゴリズムを組み込んで手の位置をキャプチャし、ビデオ ストリームや画像内の手話のリアルタイム認識を可能にします。
まず、プロジェクトは OpenCV ライブラリのアルゴリズムを使用して、ビデオ ストリームまたは画像内の手の位置をキャプチャします。これには、手話ジェスチャーを正確に特定するための肌の色検出、動き検出、ジェスチャー検出などのテクノロジーが含まれる場合があります。
次に、プロジェクトは CNN 深層学習モデルを使用して、キャプチャされた手話を分類し、トレーニング後、さまざまな手話のジェスチャーを特定のカテゴリまたは文字として認識できるようになります。
リアルタイム認識プロセス中に、ビデオ ストリームまたは画像内の手話ジェスチャーが CNN 深層学習モデルに渡され、推論が行われ、ジェスチャーが対応するカテゴリに認識されます。これにより、システムは手話ジェスチャーをリアルタイムで認識し、テキストまたは他の形式の出力に変換できます。
全体として、このプロジェクトはコンピューター ビジョンと深層学習テクノロジーを組み合わせて、手話認識のリアルタイム ソリューションを提供します。これは、聴覚障害のある人や手話ユーザーにとって、他者とのコミュニケーションや理解をより容易にするための有益なツールです。
全体的なデザイン
このパートには、システム全体の構成図とシステム フローチャートが含まれます。
全体システム構成図
システムの全体構成を図に示します。
システムフローチャート
システムフローを図に示します。
動作環境
この部分には、Python 環境、TensorFlow 環境、Keras 環境、Android 環境が含まれます。
モジュールの実装
このプロジェクトには、データ前処理、データ強化、モデル構築、モデルのトレーニングと保存、モデルの評価、モデルのテストの 6 つのモジュールが含まれており、各モジュールの機能と関連コードを以下に紹介します。
1. データの前処理
Kaggle で対応するデータセットをダウンロードします (ダウンロード アドレスはhttps://www.kaggle.com/ardamavi/sign- language-digits-dataset です) 。
2. データの強化
生成された画像の効果の表示とパラメーターの微調整を容易にするために、このプロジェクトでは、ジェネレーターを直接トレーニングするために keras を使用せず、代わりに、強化されたデータセットが最初に生成され、その後モデルのトレーニングに使用されます。
3. モデルの構築
データをモデルにロードした後、モデル構造を定義し、損失関数を最適化する必要があります。
4. モデルのトレーニングと保存
このセクションには、モデルのトレーニングとモデルの保存に関連するコードが含まれています。
5. モデルの評価
インターネット上には手話認識関連のモデルが不足しているため、さまざまなモデルの中から最適なモデルの選択とモデルの最適化を容易にするために、モデルを Android プロジェクトに適用する前に、予備実行テスト用の PC デバイス上の Python ファイルこのソリューションの手話認識戦略が実現可能かどうかを検証し、最適な分類モデルを選択します。
6. モデルのテスト
モデル全体の実現可能性を評価した後、手話認識モデルを Android Studio プロジェクトに適用して APP を完成させます。具体的な手順は以下の通りです。
1) 許可登録
まず、ビデオと写真の中の手話を認識する 2 つのアクティビティをそれぞれ登録し、次にカメラを呼び出してAndroidManifest.xml
カメラの許可とストレージの許可を申請します。SD カードにアクセスするには、 を登録しFileProvider
、最後にプログラムの入り口と起動方法を指定します。
関連するコードは次のとおりです。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cameraapp">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="landscape">
android:label="手语实时识别">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.cameraapp.Second"
android:label="图片识别">
<intent-filter>
<action android:name="com.litreily.SecondActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.cameraapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
2) モデルのインポート
モデルインポートに関する操作は以下のとおりです。
(1) トレーニング済みの .pb ファイルを挿入しますapp/src/main/assets
。アセット ディレクトリが存在しない場合は、メニュー項目を右クリックしmain→new→Directory
、「assets」と入力してディレクトリを作成します。
(2) 新しいクラスを作成しPredictionTF.java
、このクラスに so ライブラリをロードし、TensorFlow モデルを呼び出して予測結果を取得します。
(3)MainActivity.java
でモデルの格納パスを宣言し、PredictTF オブジェクトを作成し、PredictionTF クラスを呼び出し、アプリケーションに対応するクラスを入力します。関連するコードは次のとおりです。
//加载模型
String MODEL_FILE = "file:///android_asset/trained_model_imageDataGenerator.pb"; //模型地址
PredictTF tf =new PredictTF(getAssets(),MODEL_FILE);
(4)MainActivity.java的onCreate()
メソッド内で OpenCV ライブラリをロードします。関連するコードは次のとおりです。
//加载OpenCV库
private void staticLoadCVLibraries(){
boolean load = OpenCVLoader.initDebug();
if(load) {
Log.i("CV", "Open CV Libraries loaded...");
}
}
3) 全体モデルの構築
PredictTF 内で予測に必要な対応する関数を構築します。具体的な手順は次のとおりです。
(1) so ライブラリをロードし、必要な属性を宣言します。関連するコードは次のとおりです。
private static final String TAG = "PredictTF";
TensorFlowInferenceInterface tf;
static {
//加载libtensorflow_inference.so库文件
System.loadLibrary("tensorflow_inference");
Log.e(TAG, "libtensorflow_inference.so库加载成功");
}
//PATH TO OUR MODEL FILE AND NAMES OF THE INPUT AND OUTPUT NODES
//各节点名称
private String INPUT_NAME = "conv2d_1_input";
private String OUTPUT_NAME = "output_1";
float[] PREDICTIONS = new float[10]; //手语分类模型的输出
private int[] INPUT_SIZE = {
64, 64, 1}; //模型的输入形状
(2) OpenCVを利用して手の位置を取得する
肌の色の検出しきい値を満たさない領域を黒くします。関連するコードは次のとおりです。
//肤色识别
private Mat skin(Mat frame) {
int iLowH = 0;
int iHighH = 20;
int iLowS = 40;
int iHighS = 255;
int iLowV = 80;
int iHighV = 255;
Mat hsv=new Mat();
Imgproc.cvtColor(frame,hsv,Imgproc.COLOR_RGBA2BGR); //将输入图片转为灰度图
Imgproc.cvtColor(hsv,hsv,Imgproc.COLOR_BGR2HSV);
Mat skinMask=new Mat();
Core.inRange(hsv, new Scalar(iLowH, iLowS, iLowV), new Scalar(iHighH, iHighS, iHighV),skinMask);
Imgproc.GaussianBlur(skinMask,skinMask,new Size(5,5),0); //高斯滤波
Mat skin=new Mat();
bitwise_and(frame,frame,skin,skinMask); //将不符合肤色阈值的区域涂黑
return skin;
}
予測関数では、抽出した領域にガウスフィルタリングと二値化処理を施し、輪郭抽出にはfindContour関数を使用します。等高線のサイズを比較し、面積がしきい値より小さい接続ドメインを無視します。最後に、boundingRect 関数を使用して元の画像を抽出します。
関連するコードは次のとおりです。
//载入位图并转为openCV的Mat对象
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
Mat frame = new Mat();
if (imagePath == null) {
System.out.print("imagePath is null");}
Utils.bitmapToMat(bitmap, frame);
Core.flip(frame,frame,1); //水平镜像翻转
//图片预处理
frame=skin(frame); //肤色识别
//Mat frame1=new Mat();
Imgproc.cvtColor(frame,frame,Imgproc.COLOR_BGR2GRAY); //转化为灰度图
Mat frame1=new Mat();
Imgproc.GaussianBlur(frame,frame1,new Size(5,5),0); //高斯滤波
double res=Imgproc.threshold(frame1, frame1,50, 255, Imgproc.THRESH_BINARY); //二值化
//Imgproc.cvtColor(frame,frame,);
//提取所有轮廓
List<MatOfPoint> contours=new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(frame1, contours, hierarchy,
Imgproc.RETR_EXTERNAL,
Imgproc.CHAIN_APPROX_SIMPLE); //轮廓提取
//找到最大区域并填充
int max_idx= 0;
List<Double> area=new ArrayList<>();
for (int i=0;i < contours.size();i++) {
area.add(Imgproc.contourArea((contours).get(i)));
//max_idx = area.indexOf(Collections.max(area));
}
max_idx = area.indexOf(Collections.max(area));
//得到矩形
Rect rect= Imgproc.boundingRect(contours.get(max_idx));
//提取相关区域
String mess;
mess= String.valueOf(frame.channels());
Log.i("CV", "the type of frame is:"+mess);
Mat chepai_raw = new Mat(frame,rect); //提取相关区域
Mat cheapi=new Mat();
Core.flip(chepai_raw,cheapi,1); //水平镜面反转
(3) OpenCVで抽出した領域を手話分類モデルに入力
抽出された bitmpa ビットマップをモデル入力用の 1 次元配列に変換します。関連するコードは次のとおりです。
Bitmap input = Bitmap.createBitmap(cheapi.cols(),
cheapi.rows(),
Bitmap.Config.ARGB_8888);
Utils.matToBitmap(cheapi,input);
float[] input_data=bitmapToFloatArray(input,64,64);
Bitmap.createBitmap()
関数定義に関連するコードは次のとおりです。
//将bitmap转化为一维浮点数组
//本函数修改自https://blog.csdn.net/chaofeili/article/details/89374324
public static float[] bitmapToFloatArray(Bitmap bitmap,int rx, int ry){
int height = bitmap.getHeight();
int width = bitmap.getWidth();
//计算缩放比例
float scaleWidth = ((float) rx) / width;
float scaleHeight = ((float) ry) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
bitmap=Bitmap.createBitmap(bitmap,0,0,width,height, matrix, true);
height = bitmap.getHeight();
width = bitmap.getWidth();
float[] result = new float[height*width];
int k = 0;
//行优先
for(int j = 0;j < height;j++){
for (int i = 0;i < width;i++){
int argb = bitmap.getPixel(i,j);
int r = Color.red(argb);
int g = Color.green(argb);
int b = Color.blue(argb);
int a = Color.alpha(argb);
//由于是灰度图,所以r,g,b分量是相等的
assert(r==g && g==b);
//Log.i(TAG,i+","+j+":argb = "+argb+", a="+a+", r="+r+", g="+g+", b="+b);
result[k++] = r / 255.0f;
}
}
return result;
}
元のデータ セットにはラベル エラーがあるため、ラベル エラーを修正するための正しいマッピング ディクショナリを提供する必要があります。関連するコードは次のとおりです。
Map<Integer, Integer> label_map=new HashMap<Integer, Integer>();
label_map.put(0,9);label_map.put(1,0);label_map.put(2,7);
label_map.put(3,6);label_map.put(4,1);
label_map.put(5,8);label_map.put(6,4);
label_map.put(7,3);label_map.put(8,2);label_map.put(9,5); //标签纠错
モデルの適用を開始し、予測結果を取得します。関連するコードは次のとおりです。
//开始应用模型
tf = new TensorFlowInferenceInterface(getAssets(),MODEL_PATH); //加载模型
tf.feed(INPUT_NAME, input_data, 1, 64, 64, 1);
tf.run(new String[]{
OUTPUT_NAME});
//copy the output into the PREDICTIONS array
tf.fetch(OUTPUT_NAME,PREDICTIONS);
//Obtained highest prediction
Object[] results = argmax(PREDICTIONS); //找到预测置信度最大的类作为分类结果
int class_index = (Integer) results[0];
float confidence = (Float) results[1];
/*try{
final String conf = String.valueOf(confidence * 100).substring(0,5);
//Convert predicted class index into actual label name
final String label = ImageUtils.getLabel(getAssets().open("labels.json"),class_index);
//Display result on UI
runOnUiThread(new Runnable() {
@Override
public void run() {
resultView.setText(label + " : " + conf + "%");
}
});
} catch (Exception e){
}//*/
int label=label_map.get(class_index); //标签纠错
results [0]=label;
予測関数に関連するコードは次のとおりです。
private Object[] perdict (Bitmap bitmap){
//载入位图并转为openCV的Mat对象
//Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
Mat frame = new Mat();
//if (imagePath == null) {System.out.print("imagePath is null");}
Utils.bitmapToMat(bitmap, frame);
//Core.flip(frame,frame,1); //水平镜像翻转
//图片预处理
Mat frame2=new Mat();
frame2=skin(frame); //肤色识别
Imgproc.cvtColor(frame2,frame2,Imgproc.COLOR_BGR2GRAY); //转化为灰度图
Mat frame1=new Mat();
Imgproc.GaussianBlur(frame2,frame1,new Size(5,5),0); //高斯滤波
double res=Imgproc.threshold(frame1, frame1,50, 255, Imgproc.THRESH_BINARY); //二值化
//提取所有轮廓
List<MatOfPoint> contours=new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(frame1, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); //轮廓提取
//找到最大区域并填充
int max_idx= 0;
List<Double> area=new ArrayList<>();
for (int i=0;i < contours.size();i++) {
area.add(Imgproc.contourArea((contours).get(i)));
}
max_idx = area.indexOf(Collections.max(area));
//得到矩形
Rect rect= Imgproc.boundingRect(contours.get(max_idx));
//提取相关区域
String mess;
Imgproc.cvtColor(frame,frame,Imgproc.COLOR_BGR2GRAY);//将原图转化为灰度图
mess= String.valueOf(frame.channels());
Log.i("CV", "the type of frame is:"+mess);
Mat chepai_raw = new Mat(frame,rect);
mess= String.valueOf(chepai_raw.channels());
Log.i("CV", "the type of chepai_raw is:"+mess);
Mat cheapi=new Mat();
Core.flip(chepai_raw,cheapi,1); //水平镜面反转
//将提取的区域转为一维浮点数组输入模型
Bitmap input = Bitmap.createBitmap(cheapi.cols(), cheapi.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(cheapi,input);
float[] input_data=bitmapToFloatArray(input,64,64);
Map<Integer, Integer> label_map=new HashMap<Integer, Integer>();
//{0:9,1:0, 2:7, 3:6, 4:1, 5:8, 6:4, 7:3, 8:2, 9:5};
label_map.put(0,9);label_map.put(1,0);label_map.put(2,7);label_map.put(3,6);label_map.put(4,1);
label_map.put(5,8);label_map.put(6,4);label_map.put(7,3);label_map.put(8,2);label_map.put(9,5); //标签纠错
//开始应用模型
tf=new TensorFlowInferenceInterface(getAssets(),MODEL_PATH); //加载模型
tf.feed(INPUT_NAME, input_data, 1, 64, 64, 1);
tf.run(new String[]{
OUTPUT_NAME});
//copy the output into the PREDICTIONS array
tf.fetch(OUTPUT_NAME,PREDICTIONS);
//选择预测结果中置信度最大的类别
Object[] results = argmax(PREDICTIONS);
int class_index = (Integer) results[0];
float confidence = (Float) results[1];
/*try{
final String conf = String.valueOf(confidence * 100).substring(0,5);
//将预测的结果转化为0~9的标签
final String label = ImageUtils.getLabel(getAssets().open("labels.json"),class_index);
//显示结果
runOnUiThread(new Runnable() {
@Override
public void run() {
resultView.setText(label + " : " + conf + "%");
}
});
} catch (Exception e){
}
int label=label_map.get(class_index); //标签纠错
results [0]=label;
return results;
}
4) ビデオ内のプレビュー フレーム データを処理する
プレビュー フレームの処理とは、カメラ プレビュー中にデータの各フレームを処理し、特定のフレーム内の手話の種類を識別することです。具体的な手順は次のとおりです。
CameraPreview
(1) 新規クラスとプレビューフレームの処理に必要なProcessWithThreadPool
クラスを作成する
(2) カメラ映像のプレビュー開始時にイベントをバインドする
を変更し、mainActivity
新しく作成したメソッドstartPreview()
にカメラ プレビューを初期化するコードを追加し、最後にstopPreview()
メソッドでカメラ プレビューを停止します。カメラ プレビューが削除されると、クラス内の関連する終了メソッドがremoveAllViews()
トリガーされてカメラ プレビューを閉じます。CameraPreview
関連するコードは次のとおりです。
public void startPreview() {
//加载模型
StringMODEL_FILE="file:///android_asset/trained_model_imageDataGenerator.pb";
//模型地址
PredictTF tf =new PredictTF(getAssets(),MODEL_FILE);
//新建相机预览对象
final CameraPreview mPreview = new CameraPreview(this,tf);
//新建可视布局
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
//调用并初始化摄像头
SettingsFragment.passCamera(mPreview.getCameraInstance());
PreferenceManager.setDefaultValues(this, R.xml.preferences, false); SettingsFragment.setDefault(PreferenceManager.getDefaultSharedPreferences(this)); SettingsFragment.init(PreferenceManager.getDefaultSharedPreferences(this));
//设置开始按钮监听器
Button buttonSettings = (Button) findViewById(R.id.button_settings);
buttonSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager().beginTransaction().replace(R.id.camera_preview, new SettingsFragment()).addToBackStack(null).commit();
}
});
}
public void stopPreview() {
//移除相机预览界面
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.removeAllViews();
}
mainActivity では、onCreate()
上記のメソッドをバインドするメソッド内で 2 つのボタンが使用されており、関連するコードは次のとおりです。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//加载OpenCV库
staticLoadCVLibraries();
//绑定相机预览开始按钮
Button buttonStartPreview = (Button) findViewById(R.id.button_start_preview);
buttonStartPreview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startPreview();
}
});
//绑定相机预览停止按钮
Button buttonStopPreview = (Button) findViewById(R.id.button_stop_preview);
buttonStopPreview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopPreview();
}
});
}
(3) フレームデータのリアルタイム処理
CameraPreview
クラス内にインターフェース宣言を追加し、Camera.PreviewCallback
このインターフェース配下に各フレームのデータを取得するonPreviewFrame()
メソッドを実装します。関連するコードは次のとおりです。
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback{
}
onPreviewFrame() メソッドを実装し、手話認識のために PredictTF クラスを呼び出します。関連するコードは次のとおりです。
public void onPreviewFrame(byte[] data, Camera camera) {
switch (processType) {
case PROCESS_WITH_HANDLER_THREAD:
processFrameHandler.obtainMessage(ProcessWithHandlerThread.WHAT_PROCESS_FRAME, data).sendToTarget();
break;
case PROCESS_WITH_QUEUE:
try {
frameQueue.put(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
case PROCESS_WITH_ASYNC_TASK:
new ProcessWithAsyncTask().execute(data);
break;
case PROCESS_WITH_THREAD_POOL:
processFrameThreadPool.post(data);
try {
//延时3s处理
Thread.sleep(500); //手掌位置捕捉的延时
NV21ToBitmap transform = new NV21ToBitmap(getContext().getApplicationContext());
Bitmap bitmap= transform.nv21ToBitmap(data,1920,1080);
String num;
Object[] results=tf.perdict(bitmap);
if (results!=null) {
num = "result:" + results[0].toString() + " confidence:" + results[1].toString();
Toast.makeText(getContext().getApplicationContext(), num, Toast.LENGTH_SHORT).show();
Thread.sleep(3000); //如果监测到有效手语,则进行延时
}
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
default:
throw new IllegalStateException("Unexpected value: " + processType);
//*/
}
}
5) 画像データの加工
この部分には、カメラを呼び出して写真を取得すること、アルバムから写真を選択すること、写真を予測して表示することが含まれます。
(1) カメラを呼び出して画像を取得します。
ファイル オブジェクトは、カメラで撮影した写真を保存するために使用され、現在のアプリケーション キャッシュ データの場所に保存され、getUriForFile()
このディレクトリを取得するためにメソッドが呼び出されます。
下位バージョンとの互換性を保つために、その後の判断が行われます。システムのバージョンが Android 7.0 よりも低い場合は、Uri メソッドが呼び出され、formFlie ()
File オブジェクトが Uri オブジェクトに変換され、それ以外の場合は、FileProvider オブジェクトのメソッドが呼び出さgetUriForFile ()
れ、Uri オブジェクトに変換されます。 File オブジェクトを Uri オブジェクトに変換します。
Intent オブジェクトを構築し、アクションを として指定しandroid.media.action.IMAGE_CAPTURE
、putExtra ()
メソッドを呼び出して画像の出力アドレスを指定し、startActivityForResul () メソッドを呼び出してカメラ プログラムを開き、TAKE_PHOTO
成功後に処理の次のステップに戻ります。関連するコードは次のとおりです。
//创建File对象,用于存储拍照后的图片
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT < 24) {
imageUri = Uri.fromFile(outputImage);
} else {
imageUri=FileProvider.getUriForFile(Second.this,"com.example.cameraapp.fileprovider", outputImage);
}
//启动相机程序
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
}
新しいファイルを追加するfile_paths.xml
と、関連するコードは次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="/" />
</paths>
Androidfest.xml
ファイルに fileProvider を登録します。関連するコードは次のとおりです。
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.cameraapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
(2) アルバムから写真を選択
アルバムから写真を選択するには携帯電話の SD カード データにアクセスする必要があるため、AndroidManifest.xml
ファイルで許可を申請してください。WRITE_EXTERNAL_STORAGE
Intent オブジェクトを作成し、 action を指定しandroid.intent.action.GET_CONTENT
、メソッドを呼び出してstartActivityForResult()
携帯電話のアルバムを開き、CHOOSE_PHOTO
成功後の次の処理ステップでそれを返します。
関連するコードは次のとおりです。
private void openAlbum() {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO); // 打开相册
}
下位バージョンとの互換性を保つために、バージョン 4.4 以降の携帯電話からの画像を処理するには、handleImageOnKitKat()
および関数を使用してください。handleImageBeforeKitKat()
handleImageOnKitKat()
これは、解析前にカプセル化された Uri オブジェクトです。UrihandleImageBeforeKitKat()
はカプセル化されていないため、Uri オブジェクトを getImagePath() に直接渡すことができます。関連するコードは次のとおりです。
private void handleImageOnKitKat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
Log.d("TAG", "handleImageOnKitKat: uri is " + uri);
if (DocumentsContract.isDocumentUri(this, uri)) {
// 如果是document类型的Uri,则通过document ID处理
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":")[1]; //解析数字格式的ID
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
}
else
if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
Uri contentUri=ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
imagePath = getImagePath(contentUri, null);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
//如果是content类型的Uri,则使用普通方式处理
imagePath = getImagePath(uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
//如果是file类型的Uri,直接获取图片路径即可
imagePath = uri.getPath();
}
displayImage(imagePath); //根据图片路径显示图片
}
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
displayImage(imagePath);
}
(3) 画像を予測して表示する
オブジェクト関連のメソッドを使用して、PredictTF
カメラまたはアルバムから取得した画像ビットマップ オブジェクト内のジェスチャ タイプを予測し、setImageBitmap
関数を使用して表示します。
関連するコードは次のとおりです。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode,resultCode, data);
switch (requestCode)
{
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
//将拍摄的照片显示出来并进行识别
Bitmapbitmap=BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
//识别图片
Object[] results=tf.perdict(bitmap);
String num;
num ="result:"+results[0].toString()+" confidence:"+results[1].toString();
Toast.makeText(Second.this, num , Toast.LENGTH_SHORT).show();
//展示图片
picture.setImageBitmap(bitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
case CHOOSE_PHOTO:
if (resultCode == RESULT_OK) {
//判断手机系统版本号
if (Build.VERSION.SDK_INT >= 19) {
//4.4及以上系统使用这个方法处理图片
handleImageOnKitKat(data);
} else {
//4.4以下系统使用这个方法处理图片
handleImageBeforeKitKat(data);
}
}
break;
default:
break;
}
}
6) 複数のページ設定
動画や写真を使った予測用のカテゴリを作成したら、AndroidManifest.xml
ファイルにアクティビティを2つ登録します。このうち、MainActivity.java
動画は予測に使用されSecond.java
、画像は予測に使用されます。
関連するコードは次のとおりです。
<activity android:name=".MainActivity"
android:screenOrientation="landscape">
android:label="手语实时识别">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.cameraapp.Second"
android:label="图片识别">
<intent-filter>
<action android:name="com.litreily.SecondActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
各ページにジャンプ ボタンとイベントをリッスンし、setClass()
メソッドでジャンプ ページを設定し、startActivityForResult()
メソッドを使用してジャンプを開始します。
関連するコードは次のとおりです。
//设置跳转按钮
Button buttonSettings = (Button) findViewById(R.id.button_settings);
buttonSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v){
Intent intent2 =new Intent(); //新建Intent对象
intent2.setClass(MainActivity.this, Second.class);
startActivityForResult(intent2,0); //返回前一页
}
});
7) レイアウトファイルのコード
レイアウト ファイルに関連するコードは次のとおりです。
アクティビティ.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="0px"
android:layout_height="fill_parent"
android:layout_weight="1" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:orientation="vertical">
<Button
android:id="@+id/button_settings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="转到图片识别" />
<Button
android:id="@+id/button_start_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始翻译" />
<Button
android:id="@+id/button_stop_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止翻译" />
<TextView
android:id="@+id/info1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="本次识别结果:" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/info2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手部区域:" />
<ImageView
android:id="@+id/picture"
android:layout_width="100dp"
android:layout_height="80dp"
android:layout_gravity="center_horizontal" />
<TextView
android:id="@+id/info3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="句子翻译:" />
<TextView
android:id="@+id/translated_statement"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="start"/>
</LinearLayout>
</LinearLayout>
second.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/take_photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="拍照识别" />
<Button
android:id="@+id/choose_from_album"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="从相册中选择" />
<Button
android:id="@+id/button_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="转到实时翻译" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
関連する他のブログ
プロジェクトのソースコードのダウンロード
詳細については、私のブログ リソースのダウンロード ページをご覧ください。
その他の情報をダウンロードする
人工知能に関連する学習ルートと知識システムを引き続き理解したい場合は、私の別のブログ「ヘビー級 | 完全な人工知能 AI 学習 - 基礎知識の学習ルート」を参照してください。すべての情報はネットワーク ディスクから直接ダウンロードできます。》
このブログは、Github の有名なオープンソース プラットフォーム、AI テクノロジー プラットフォーム、および関連分野の専門家 (Datawhale、ApacheCN、AI Youdao、Dr. Huang Haiguang など) について言及しており、約 100G の関連情報があります。私の友人全員を助けることができれば幸いです。