Resimlerdeki nesneleri algılamak için Android uygulaması oluşturun

1. Başlamadan önce

Bu codelab'de, REST ve gRPC ile TensorFlow Sunumu'nu kullanarak Android uygulamasından nesne algılamasını nasıl çalıştıracağınızı öğreneceksiniz.

Ön koşullar

  • Java ile Android geliştirmeyle ilgili temel bilgiler
  • TensorFlow'da eğitim ve dağıtım gibi temel makine öğrenimi bilgileri
  • Terminaller ve Docker hakkında temel bilgiler

Neler öğreneceksiniz?

  • TensorFlow Hub'da önceden eğitilmiş nesne algılama modelleri nasıl bulunur?
  • TensorFlow Sunumu (REST ve gRPC) aracılığıyla indirilen nesne algılama modeliyle basit bir Android uygulaması oluşturma ve tahminde bulunma.
  • Kullanıcı arayüzünde algılama sonucu nasıl oluşturulur?

Gerekenler

2. Hazırlanın

Bu codelab'in kodunu indirmek için:

  1. Bu codelab için GitHub veri havuzuna gidin.
  2. Bu codelab'e ait tüm kodu indirmek için Code > İndir ZIP'i tıklayın.

a72f2bb4caa9a96.png

  1. İhtiyaç duyduğunuz tüm kaynaklarla bir codelabs kök klasörünün paketini açmak için indirilen zip dosyasını açın.

Bu codelab için yalnızca depodaki TFServing/ObjectDetectionAndroid alt dizininde bulunan ve iki klasör içeren dosyalara ihtiyacınız vardır:

  • starter klasörü, bu codelab için geliştirmekte olduğunuz başlangıç kodunu içerir.
  • finished klasörü, tamamlanan örnek uygulama için tamamlanmış kodu içerir.

3. Bağımlıları projeye ekleyin

Başlangıç uygulamasını Android Studio'ya aktarma

  • Android Studio'da File > New > Import project'i (Projeyi içe aktar) ve daha önce indirdiğiniz kaynak koddan starter klasörünü seçin.

OkHttp ve gRPC için bağımlılıkları ekleyin

  • Projenizin app/build.gradle dosyasında bağımlılıkların varlığını onaylayın.
dependencies {
  // ...
    implementation 'com.squareup.okhttp3:okhttp:4.9.0'
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    implementation 'io.grpc:grpc-okhttp:1.29.0'
    implementation 'io.grpc:grpc-protobuf-lite:1.29.0'
    implementation 'io.grpc:grpc-stub:1.29.0'
}

Projenizi Gradle dosyalarıyla senkronize etme

  • Gezinme menüsünde 541e90b497a7fef7.png Projeyi Gradle Dosyalarıyla Senkronize Et'i seçin.

4. Başlangıç uygulamasını çalıştırma

Uygulamayı çalıştırma ve keşfetme

Uygulama, Android cihazınızda başlatılmalıdır. Kullanıcı arayüzü oldukça basit: Nesneleri algılamak istediğiniz kedi resmi var. REST veya gRPC ile kullanıcı arka uça veri gönderme yöntemini seçebilir. Arka uç, görüntüde nesne algılaması gerçekleştirir ve algılama sonuçlarını istemci uygulamasına döndürerek kullanıcı arayüzünü tekrar oluşturur.

24eab579530e9645.png

Şu anda Çıkarım'ı tıklarsanız hiçbir şey olmaz. Bunun nedeni, cihazın arka uçla henüz iletişim kuramamasıdır.

5. TensorFlow Yayınlama ile nesne algılama modeli dağıtma

Nesne algılama, çok yaygın bir makine öğrenimi görevidir. Bu yöntem, nesneler içindeki nesneleri ve sınırlayıcı kutuların olası kategorilerini tahmin etmek için resimlerdeki nesneleri algılamayı amaçlar. Bir algılama sonucu örneği:

a68f9308fb2fc17b.png

Google, TensorFlow Hub'da önceden eğitilmiş bir dizi model yayınlamıştır. Listenin tamamını görmek için object_detection sayfasını ziyaret edin. Bu codelab'de nispeten hafif SSD MobileNet V2 FPNLite 320x320 modeli kullanıyorsunuz. Bu nedenle, çalıştırmak için GPU kullanmanız gerekmez.

TensorFlow Sunumu ile nesne algılama modelini dağıtmak için:

  1. Model dosyasını indirin.
  2. İndirilen .tar.gz dosyasının sıkıştırmasını açmak için 7-Zip gibi bir sıkıştırma aracı kullanın.
  3. ssd_mobilenet_v2_2_320 klasörü ve ardından 123 alt klasörü oluşturun.
  4. Çıkarılan variables klasörünü ve saved_model.pb dosyasını 123 alt klasörüne yerleştirin.

ssd_mobilenet_v2_2_320 klasörünü SavedModel klasörü olarak adlandırabilirsiniz. 123 örnek bir sürüm numarasıdır. İsterseniz başka bir numara seçebilirsiniz.

Klasör yapısı aşağıdaki gibi görünmelidir:

42c8150a42033767.png

TensorFlow Sunumunu Başlat

  • Terminalinizde Docker ile TensorFlow Sunumu'nu başlatın ancak PATH/TO/SAVEDMODEL yer tutucusunu bilgisayarınızdaki ssd_mobilenet_v2_2_320 klasörünün mutlak yoluyla değiştirin.
docker pull tensorflow/serving

docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/ssd_mobilenet_v2_2" -e MODEL_NAME=ssd_mobilenet_v2_2 tensorflow/serving

Docker, önce bir dakika sürecek TensorFlow Sunumu resmini otomatik olarak indirir. Ardından, TensorFlow Sunumu başlatılmalıdır. Günlük, şu kod snippet'i gibi olmalıdır:

2022-02-25 06:01:12.513231: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle.
2022-02-25 06:01:12.585012: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 3000000000 Hz
2022-02-25 06:01:13.395083: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/ssd_mobilenet_v2_2/123
2022-02-25 06:01:13.837562: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 1928700 microseconds.
2022-02-25 06:01:13.877848: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/ssd_mobilenet_v2_2/123/assets.extra/tf_serving_warmup_requests
2022-02-25 06:01:13.929844: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: ssd_mobilenet_v2_2 version: 123}
2022-02-25 06:01:13.985848: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models
2022-02-25 06:01:13.985987: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled
2022-02-25 06:01:13.988994: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ...
[warn] getaddrinfo: address family for nodename not supported
2022-02-25 06:01:14.033872: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ...
[evhttp_server.cc : 245] NET_LOG: Entering the event loop ...

6. Android uygulamasını REST üzerinden TensorFlow Sunumu'na bağlama

Arka uç artık hazırdır. Böylece, resimlerdeki nesneleri algılamak için TensorFlow Sunumu'na istemci istekleri gönderebilirsiniz. TensorFlow Sunumu'na istek göndermenin iki yolu vardır:

  • REST
  • gRPC

İstek ve REST aracılığıyla yanıt alma

Üç basit adım vardır:

  • REST isteğini oluşturun.
  • REST isteğini TensorFlow Sunumu'na gönderin.
  • REST yanıtından tahmin edilen sonucu çıkarıp kullanıcı arayüzünü oluşturun.

MainActivity.java. içinde bu hedeflere ulaşacaksınız

REST isteğini oluşturun

Şu anda MainActivity.java dosyasında boş bir createRESTRequest() işlevi var. Bir REST isteği oluşturmak için bu işlevi uygularsınız.

private Request createRESTRequest() {
}

TensorFlow Sunumu, kullandığınız SSD MobileNet modeli için görüntü tensörü içeren bir POST isteği bekler. Bu nedenle, resmin her pikselinden RGB değerlerini bir diziye çıkarmanız ve ardından diziyi, isteğin yükü olan bir JSON içinde sarmalamanız gerekir.

  • Bu kodu createRESTRequest() işlevine ekleyin:
//Create the REST request.
int[] inputImg = new int[INPUT_IMG_HEIGHT * INPUT_IMG_WIDTH];
int[][][][] inputImgRGB = new int[1][INPUT_IMG_HEIGHT][INPUT_IMG_WIDTH][3];
inputImgBitmap.getPixels(inputImg, 0, INPUT_IMG_WIDTH, 0, 0, INPUT_IMG_WIDTH, INPUT_IMG_HEIGHT);
int pixel;
for (int i = 0; i < INPUT_IMG_HEIGHT; i++) {
    for (int j = 0; j < INPUT_IMG_WIDTH; j++) {
    // Extract RBG values from each pixel; alpha is ignored
    pixel = inputImg[i * INPUT_IMG_WIDTH + j];
    inputImgRGB[0][i][j][0] = ((pixel >> 16) & 0xff);
    inputImgRGB[0][i][j][1] = ((pixel >> 8) & 0xff);
    inputImgRGB[0][i][j][2] = ((pixel) & 0xff);
    }
}

RequestBody requestBody =
    RequestBody.create("{\"instances\": " + Arrays.deepToString(inputImgRGB) + "}", JSON);

Request request =
    new Request.Builder()
        .url("http://" + SERVER + ":" + REST_PORT + "/v1/models/" + MODEL_NAME + ":predict")
        .post(requestBody)
        .build();

return request;    

REST isteğini TensorFlow Sunumu'na gönder

Uygulama, kullanıcının TensorFlow Servet ile iletişim kurmak için REST veya gRPC seçmesine olanak tanır. Bu nedenle, onClick(View view) işleyicide iki dal vardır.

predictButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (requestRadioGroup.getCheckedRadioButtonId() == R.id.rest) {
                // TODO: REST request
            }
            else {

            }
        }
    }
)
  • İsteği TensorFlow Sunum'a göndermek üzere OkHttp kullanmak için onClick(View view) işleyicisinin REST dalına bu kodu ekleyin:
// Send the REST request.
Request request = createRESTRequest();
try {
    client =
        new OkHttpClient.Builder()
            .connectTimeout(20, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .callTimeout(20, TimeUnit.SECONDS)
            .build();
    Response response = client.newCall(request).execute();
    JSONObject responseObject = new JSONObject(response.body().string());
    postprocessRESTResponse(responseObject);
} catch (IOException | JSONException e) {
    Log.e(TAG, e.getMessage());
    responseTextView.setText(e.getMessage());
    return;
}

TensorFlow Delivery'den REST yanıtını işleme

SSD MobileNet modeli, aşağıdakiler de dahil olmak üzere çeşitli sonuçlar döndürür:

  • num_detections: algılama sayısı
  • detection_scores: algılama puanları
  • detection_classes: algılama sınıfı dizini
  • detection_boxes: sınırlayıcı kutu koordinatları

Yanıtı işlemek için postprocessRESTResponse() işlevini uygularsınız.

private void postprocessRESTResponse(Predict.PredictResponse response) {

}
  • Bu kodu postprocessRESTResponse() işlevine ekleyin:
// Process the REST response.
JSONArray predictionsArray = responseObject.getJSONArray("predictions");
//You only send one image, so you directly extract the first element.
JSONObject predictions = predictionsArray.getJSONObject(0);
// Argmax
int maxIndex = 0;
JSONArray detectionScores = predictions.getJSONArray("detection_scores");
for (int j = 0; j < predictions.getInt("num_detections"); j++) {
    maxIndex =
        detectionScores.getDouble(j) > detectionScores.getDouble(maxIndex + 1) ? j : maxIndex;
}
int detectionClass = predictions.getJSONArray("detection_classes").getInt(maxIndex);
JSONArray boundingBox = predictions.getJSONArray("detection_boxes").getJSONArray(maxIndex);
double ymin = boundingBox.getDouble(0);
double xmin = boundingBox.getDouble(1);
double ymax = boundingBox.getDouble(2);
double xmax = boundingBox.getDouble(3);
displayResult(detectionClass, (float) ymin, (float) xmin, (float) ymax, (float) xmax);

Şimdi son işleme işlevi yanıttan tahmin edilen değerleri ayıklar, nesnenin en olası kategorisini ve sınırlayıcı kutu köşelerinin koordinatlarını belirler ve son olarak da algılama sınırlayıcı kutusunu kullanıcı arayüzünde oluşturur.

Çalıştır

  1. Gezinme menüsünde yürütün.png "Uygulama''yı çalıştır'ı tıklayın ve ardından uygulamanın yüklenmesini bekleyin.
  2. REST > Çıkarım çıkar'ı seçin.

Uygulamanın, kedinin sınırlayıcı kutusunu oluşturması ve 17 öğesinin, COCO veri kümesindeki cat nesnesiyle eşlenen nesne kategorisi olarak göstermesi birkaç saniye sürer.

5a1a32768dc516d6.png

7. Android uygulamasını gRPC üzerinden TensorFlow Sunumu'na bağlama

TensorFlow Sunumu, REST'e ek olarak gRPC'yi de destekler.

b6f4449c2c850b0e.png

gRPC, tüm ortamlarda çalışabilen modern, açık kaynaklı, yüksek performanslı bir Uzaktan Prosedür Çağrısı (RPC) çerçevesidir. Yük dengeleme, izleme, sağlık kontrolü ve kimlik doğrulama konularında takılabilir destek sayesinde veri merkezlerinde ve veri merkezlerinde hizmetleri etkili şekilde bağlayabilir. gRPC'nin uygulamada REST'ten daha iyi performans gösterdiği gözlemlenmiştir.

gRPC ile istek gönderme ve yanıtlar alma

Dört basit adım vardır:

  • [İsteğe bağlı] gRPC istemci stub kodunu oluşturun.
  • gRPC isteğini oluşturun.
  • gRPC isteğini TensorFlow Sunumu'na gönderin.
  • gRPC yanıtından öngörülen sonucu ayıklayın ve kullanıcı arayüzünü oluşturun.

MainActivity.java. içinde bu hedeflere ulaşacaksınız

İsteğe bağlı: gRPC istemci stub kodunu oluşturun

gRPC'yi TensorFlow Sunumu ile kullanmak için gRPC iş akışını uygulamanız gerekir. Ayrıntılar hakkında bilgi edinmek için gRPC dokümanlarına bakın.

a9d0e5cb543467b4.png

TensorFlow Sunumu ve TensorFlow, .proto dosyalarını sizin için tanımlar. TensorFlow ve TensorFlow Sunumu 2.8 itibarıyla şu dosyalar gerekli .proto:

tensorflow/core/example/example.proto
tensorflow/core/example/feature.proto
tensorflow/core/protobuf/struct.proto
tensorflow/core/protobuf/saved_object_graph.proto
tensorflow/core/protobuf/saver.proto
tensorflow/core/protobuf/trackable_object_graph.proto
tensorflow/core/protobuf/meta_graph.proto
tensorflow/core/framework/node_def.proto
tensorflow/core/framework/attr_value.proto
tensorflow/core/framework/function.proto
tensorflow/core/framework/types.proto
tensorflow/core/framework/tensor_shape.proto
tensorflow/core/framework/full_type.proto
tensorflow/core/framework/versions.proto
tensorflow/core/framework/op_def.proto
tensorflow/core/framework/graph.proto
tensorflow/core/framework/tensor.proto
tensorflow/core/framework/resource_handle.proto
tensorflow/core/framework/variable.proto

tensorflow_serving/apis/inference.proto
tensorflow_serving/apis/classification.proto
tensorflow_serving/apis/predict.proto
tensorflow_serving/apis/regression.proto
tensorflow_serving/apis/get_model_metadata.proto
tensorflow_serving/apis/input.proto
tensorflow_serving/apis/prediction_service.proto
tensorflow_serving/apis/model.proto
  • Satırı oluşturmak için bu kodu app/build.gradle dosyasına ekleyin.
apply plugin: 'com.google.protobuf'

protobuf {
    protoc { artifact = 'com.google.protobuf:protoc:3.11.0' }
    plugins {
        grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.29.0'
        }
    }
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                java { option 'lite' }
            }
            task.plugins {
                grpc { option 'lite' }
            }
        }
    }
}

gRPC isteğini oluşturma

REST isteğine benzer şekilde, gRPC isteğini createGRPCRequest() işlevinde oluşturursunuz.

private Request createGRPCRequest() {

}
  • Bu kodu createGRPCRequest() işlevine ekleyin:
if (stub == null) {
  channel = ManagedChannelBuilder.forAddress(SERVER, GRPC_PORT).usePlaintext().build();
  stub = PredictionServiceGrpc.newBlockingStub(channel);
}

Model.ModelSpec.Builder modelSpecBuilder = Model.ModelSpec.newBuilder();
modelSpecBuilder.setName(MODEL_NAME);
modelSpecBuilder.setVersion(Int64Value.of(MODEL_VERSION));
modelSpecBuilder.setSignatureName(SIGNATURE_NAME);

Predict.PredictRequest.Builder builder = Predict.PredictRequest.newBuilder();
builder.setModelSpec(modelSpecBuilder);

TensorProto.Builder tensorProtoBuilder = TensorProto.newBuilder();
tensorProtoBuilder.setDtype(DataType.DT_UINT8);
TensorShapeProto.Builder tensorShapeBuilder = TensorShapeProto.newBuilder();
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(1));
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(INPUT_IMG_HEIGHT));
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(INPUT_IMG_WIDTH));
tensorShapeBuilder.addDim(TensorShapeProto.Dim.newBuilder().setSize(3));
tensorProtoBuilder.setTensorShape(tensorShapeBuilder.build());
int[] inputImg = new int[INPUT_IMG_HEIGHT * INPUT_IMG_WIDTH];
inputImgBitmap.getPixels(inputImg, 0, INPUT_IMG_WIDTH, 0, 0, INPUT_IMG_WIDTH, INPUT_IMG_HEIGHT);
int pixel;
for (int i = 0; i < INPUT_IMG_HEIGHT; i++) {
    for (int j = 0; j < INPUT_IMG_WIDTH; j++) {
    // Extract RBG values from each pixel; alpha is ignored.
    pixel = inputImg[i * INPUT_IMG_WIDTH + j];
    tensorProtoBuilder.addIntVal((pixel >> 16) & 0xff);
    tensorProtoBuilder.addIntVal((pixel >> 8) & 0xff);
    tensorProtoBuilder.addIntVal((pixel) & 0xff);
    }
}
TensorProto tensorProto = tensorProtoBuilder.build();

builder.putInputs("input_tensor", tensorProto);

builder.addOutputFilter("num_detections");
builder.addOutputFilter("detection_boxes");
builder.addOutputFilter("detection_classes");
builder.addOutputFilter("detection_scores");

return builder.build();

gRPC isteğini TensorFlow Sunumu'na gönderme

Artık onClick(View view) dinleyicisini tamamlayabilirsiniz.

predictButton.setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (requestRadioGroup.getCheckedRadioButtonId() == R.id.rest) {

            }
            else {
                // TODO: gRPC request
            }
        }
    }
)
  • Bu kodu gRPC dalına ekleyin:
try {
    Predict.PredictRequest request = createGRPCRequest();
    Predict.PredictResponse response = stub.predict(request);
    postprocessGRPCResponse(response);
} catch (Exception e) {
    Log.e(TAG, e.getMessage());
    responseTextView.setText(e.getMessage());
    return;
}

TensorFlow Sunumundan gRPC yanıtını işleme

gRPC'ye benzer şekilde, yanıtı yönetmek için postprocessGRPCResponse() işlevini uygularsınız.

private void postprocessGRPCResponse(Predict.PredictResponse response) {

}
  • Bu kodu postprocessGRPCResponse() işlevine ekleyin:
// Process the response.
float numDetections = response.getOutputsMap().get("num_detections").getFloatValList().get(0);
List<Float> detectionScores =    response.getOutputsMap().get("detection_scores").getFloatValList();
int maxIndex = 0;
for (int j = 0; j < numDetections; j++) {
    maxIndex = detectionScores.get(j) > detectionScores.get(maxIndex + 1) ? j : maxIndex;
}
Float detectionClass =    response.getOutputsMap().get("detection_classes").getFloatValList().get(maxIndex);
List<Float> boundingBoxValues =    response.getOutputsMap().get("detection_boxes").getFloatValList();
float ymin = boundingBoxValues.get(maxIndex * 4);
float xmin = boundingBoxValues.get(maxIndex * 4 + 1);
float ymax = boundingBoxValues.get(maxIndex * 4 + 2);
float xmax = boundingBoxValues.get(maxIndex * 4 + 3);
displayResult(detectionClass.intValue(), ymin, xmin, ymax, xmax);

Artık işleme sonrası işlevi, tahmin edilen değerleri yanıttan çıkarabilir ve kullanıcı arayüzünde algılama sınırlayıcı kutusunu oluşturabilir.

Çalıştır

  1. Gezinme menüsünde yürütün.png "Uygulama''yı çalıştır'ı tıklayın ve ardından uygulamanın yüklenmesini bekleyin.
  2. gRPC > Run inference'i seçin.

Uygulamanın, kedinin sınırlayıcı kutusunu oluşturması birkaç saniye sürer ve nesne kategorisi olarak 17 gösterilir. Bu, COCO veri kümesindeki cat kategorisiyle eşlenir.

8. Tebrikler

Uygulamanıza nesne algılama özellikleri eklemek için TensorFlow Sunumunu kullandınız.

Daha fazla bilgi