สร้างแอป Android เพื่อตรวจจับวัตถุภายในรูปภาพ

1. ข้อควรทราบก่อนที่จะเริ่มต้น

ใน Codelab นี้ คุณดูวิธีเรียกใช้การอนุมานการตรวจหาออบเจ็กต์จากแอป Android โดยใช้ TensorFlow Serving กับ REST และ gRPC

สิ่งที่ต้องมีก่อน

  • ความรู้เบื้องต้นเกี่ยวกับการพัฒนา Android ด้วย Java
  • ความรู้เบื้องต้นเกี่ยวกับแมชชีนเลิร์นนิงด้วย TensorFlow เช่น การฝึกอบรมและการทําให้ใช้งานได้
  • ความรู้เบื้องต้นเกี่ยวกับเทอร์มินัลและ Docker

สิ่งที่คุณจะได้เรียนรู้

  • วิธีค้นหาโมเดลการตรวจจับออบเจ็กต์ที่ฝึกไว้แล้วล่วงหน้าใน TensorFlow Hub
  • วิธีสร้างแอป Android แบบง่ายๆ และการคาดคะเนด้วยโมเดลการตรวจจับออบเจ็กต์ที่ดาวน์โหลดผ่าน TensorFlow Serving (REST และ gRPC)
  • วิธีแสดงผลการตรวจจับใน UI

สิ่งที่ต้องมี

2. ตั้งค่า

วิธีดาวน์โหลดโค้ดสําหรับ Codelab นี้

  1. ไปที่ที่เก็บ GitHub สําหรับ Codelab นี้
  2. คลิก Code > Download ZIP เพื่อดาวน์โหลดโค้ดทั้งหมดสําหรับ Codelab นี้

a72f2bb4caa9a96.png

  1. แตกไฟล์ ZIP ที่ดาวน์โหลดเพื่อคลายโฟลเดอร์รูท codelabs ด้วยทรัพยากรทั้งหมดที่ต้องการ

Codelab นี้ต้องการเฉพาะไฟล์ในไดเรกทอรีย่อย TFServing/ObjectDetectionAndroid ในที่เก็บ ซึ่งมี 2 โฟลเดอร์ดังนี้

  • โฟลเดอร์ starter มีโค้ดเริ่มต้นที่คุณสร้างสําหรับ Codelab นี้
  • โฟลเดอร์ finished มีโค้ดที่สมบูรณ์สําหรับแอปตัวอย่างที่สมบูรณ์

3. เพิ่มทรัพยากร Dependency ลงในโปรเจ็กต์

นําเข้าแอปเริ่มต้นไปยัง Android Studio

  • ใน Android Studio ให้คลิก File > New > Import project แล้วเลือกโฟลเดอร์ starter จากซอร์สโค้ดที่คุณดาวน์โหลดไว้ก่อนหน้านี้

เพิ่มทรัพยากร Dependency สําหรับ OkHttp และ gRPC

  • ในไฟล์ app/build.gradle ของโปรเจ็กต์ ให้ยืนยันว่ามีทรัพยากร Dependency อยู่
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'
}

ซิงค์โปรเจ็กต์ด้วยไฟล์ Gradle

  • เลือก541e90b497a7fef7.pngซิงค์โปรเจ็กต์ด้วย Gradle Files จากเมนูการนําทาง

4. เรียกใช้แอปเริ่มต้น

เรียกใช้และสํารวจแอป

แอปควรเปิดตัวในอุปกรณ์ Android UI มีความเรียบง่าย: จะมีรูปภาพแมวที่คุณต้องการตรวจหาออบเจ็กต์และผู้ใช้เลือกวิธีส่งข้อมูลไปยังแบ็กเอนด์ได้ด้วย REST หรือ gRPC แบ็กเอนด์จะตรวจจับออบเจ็กต์ในรูปภาพและส่งผลลัพธ์ของการตรวจจับไปยังแอปไคลเอ็นต์ ซึ่งจะแสดงผล UI อีกครั้ง

24eab579530e9645.png

ขณะนี้หากคุณคลิกเรียกใช้การอนุมาน ก็จะไม่มีอะไรเกิดขึ้น เนื่องจากยังไม่สามารถสื่อสารกับแบ็กเอนด์

5. ทําให้โมเดลการตรวจจับออบเจ็กต์ใช้งานได้ด้วยการแสดงผล TensorFlow

การตรวจหาออบเจ็กต์เป็นงาน ML ที่พบบ่อยที่สุดและเป้าหมายของออบเจ็กต์นี้คือ การตรวจหาออบเจ็กต์ภายในรูปภาพ ซึ่งก็คือการคาดการณ์หมวดหมู่ที่เป็นไปได้ของออบเจ็กต์และกรอบล้อมรอบ ด้านล่างนี้เป็นตัวอย่างของผลการตรวจหา

a68f9308fb2fc17b.png

Google ได้เผยแพร่โมเดลที่ฝึกล่วงหน้าแล้วจํานวนหนึ่งใน TensorFlow Hub หากต้องการดูรายการทั้งหมด ให้ไปที่หน้า Object_detection คุณใช้โมเดล SSD MobileNet V2 FPNLite 320x320 ที่ค่อนข้างหนักสําหรับ Codelab นี้เพื่อที่จะได้ไม่ต้องใช้ GPU ในการเรียกใช้

หากต้องการทําให้โมเดลการตรวจจับออบเจ็กต์ใช้งานได้ด้วยการแสดงผล TensorFlow ให้ทําดังนี้

  1. ดาวน์โหลดไฟล์โมเดล
  2. ยกเลิกการบีบอัดไฟล์ .tar.gz โดยใช้เครื่องมือขยายข้อมูลที่บีบอัด เช่น 7-Zip
  3. สร้างโฟลเดอร์ ssd_mobilenet_v2_2_320 แล้วสร้างโฟลเดอร์ย่อย 123 ในโฟลเดอร์นั้น
  4. วางโฟลเดอร์ variables และไฟล์ saved_model.pb ที่แยกไว้ในโฟลเดอร์ย่อย 123

คุณจะอ้างอิงโฟลเดอร์ ssd_mobilenet_v2_2_320 ว่าเป็นโฟลเดอร์ SavedModel ก็ได้ 123 เป็นหมายเลขเวอร์ชันตัวอย่าง คุณเลือกหมายเลขอื่นได้หากต้องการ

โครงสร้างโฟลเดอร์ควรมีลักษณะเช่นนี้

42c8150a42033767.png

เริ่มแสดงโฆษณา TensorFlow

  • ในเทอร์มินัล ให้เริ่มแสดงโฆษณา TensorFlow ด้วย Docker แต่แทนที่ตัวยึดตําแหน่ง PATH/TO/SAVEDMODEL ด้วยเส้นทางสัมบูรณ์ของโฟลเดอร์ ssd_mobilenet_v2_2_320 ในคอมพิวเตอร์
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 จะดาวน์โหลดอิมเมจการแสดงผล TensorFlow โดยอัตโนมัติก่อน ซึ่งจะใช้เวลา 1 นาที หลังจากนั้น TensorFlow Serving ควรจะเริ่มแสดง บันทึกควรมีลักษณะเหมือนข้อมูลโค้ดนี้

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 กับ TensorFlow Serving ผ่าน REST

แบ็กเอนด์พร้อมใช้งานแล้ว คุณจึงส่งคําขอของไคลเอ็นต์ไปยังการแสดงของ TensorFlow เพื่อตรวจหาออบเจ็กต์ภายในรูปภาพได้ คุณส่งคําขอไปยังการแสดง TensorFlow ได้ 2 วิธีดังนี้

  • REST
  • gRPC

ส่งคําขอและรับการตอบกลับผ่าน REST

มี 3 ขั้นตอนง่ายๆ ดังนี้

  • สร้างคําขอ REST
  • ส่งคําขอ REST ไปยังการแสดงผล TensorFlow
  • ดึงผลลัพธ์ที่คาดการณ์ไว้จากการตอบกลับของ REST และแสดง UI

คุณจะบรรลุเป้าหมายเหล่านี้ใน MainActivity.java.

สร้างคําขอ REST

ขณะนี้มีฟังก์ชัน createRESTRequest() ที่ว่างเปล่าในไฟล์ MainActivity.java คุณใช้ฟังก์ชันนี้เพื่อสร้างคําขอ REST ได้

private Request createRESTRequest() {
}

การแสดงผล TensorFlow ต้องการคําขอ POST ที่มี Tensor รูปภาพสําหรับโมเดล SSD MobileNet ที่คุณใช้ คุณจึงต้องดึงค่า RGB จากแต่ละพิกเซลของรูปภาพเป็นอาร์เรย์ แล้วรวมอาร์เรย์ไว้ใน JSON ซึ่งเป็นเพย์โหลดของคําขอ

  • เพิ่มโค้ดนี้ลงในฟังก์ชัน createRESTRequest()
//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 ไปยังการแสดงโฆษณา TensorFlow

แอปช่วยให้ผู้ใช้เลือก REST หรือ gRPC เพื่อสื่อสารกับ TensorFlow Serving จึงมี 2 สาขาใน Listener onClick(View view)

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

            }
        }
    }
)
  • เพิ่มโค้ดนี้ลงในสาขา REST ของ Listener onClick(View view) เพื่อใช้ OkHttp เพื่อส่งคําขอไปยัง TensorFlow Serving ดังต่อไปนี้
// 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;
}

ประมวลผลการตอบกลับ REST จาก TensorFlow Serving

โมเดล SDD MobileNet จะแสดงผลลัพธ์จํานวนหนึ่ง ซึ่งรวมถึง

  • num_detections: จํานวนการตรวจจับ
  • detection_scores: คะแนนการตรวจจับ
  • detection_classes: ดัชนีคลาสการตรวจจับ
  • detection_boxes: พิกัดกล่องขอบเขต

คุณใช้ฟังก์ชัน postprocessRESTResponse() ในการจัดการคําตอบ

private void postprocessRESTResponse(Predict.PredictResponse response) {

}
  • เพิ่มโค้ดนี้ลงในฟังก์ชัน postprocessRESTResponse()
// 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);

ตอนนี้ฟังก์ชันหลังการประมวลผลจะดึงค่าที่คาดการณ์ไว้จากการตอบกลับ ระบุหมวดหมู่ที่น่าจะเป็นมากที่สุดของวัตถุ และพิกัดของจุดยอดกล่องล้อมรอบ และแสดงผลกรอบล้อมรอบการตรวจจับบน UI ครั้งล่าสุด

เรียกใช้

  1. คลิกpng เรียกใช้ ‘app' ในเมนูการนําทางแล้วรอให้แอปโหลด
  2. เลือก REST > Run inference

ระบบจะใช้เวลา 2-3 วินาทีก่อนที่แอปจะแสดงผลกรอบล้อมรอบของแมวและแสดง 17 เป็นหมวดหมู่ของออบเจ็กต์ที่แมปกับออบเจ็กต์ cat ในชุดข้อมูล COCO

5a1a32768dc516d6.png

7. เชื่อมต่อแอป Android กับ TensorFlow Serving ผ่าน gRPC

นอกจาก REST แล้ว TensorFlow Serving ยังรองรับ gRPC อีกด้วย

b6f4449c2c850b0e.png

gRPC เป็นเฟรมเวิร์ก Call Procedure Call (RPC) แบบโอเพนซอร์สที่มีประสิทธิภาพสูงและใช้งานได้กับทุกสภาพแวดล้อม เครื่องมือนี้จะเชื่อมบริการอย่างมีประสิทธิภาพทั่วทั้งศูนย์ข้อมูลต่างๆ ที่รองรับความสามารถในการจัดสรรภาระงาน การติดตาม การตรวจสอบประสิทธิภาพการทํางาน และการตรวจสอบสิทธิ์แบบเสียบปลั๊กได้ พบว่า gRPC มีประสิทธิภาพมากกว่า REST ในทางปฏิบัติ

ส่งคําขอและรับการตอบกลับด้วย gRPC

มี 4 ขั้นตอนง่ายๆ ดังนี้

  • [ไม่บังคับ] สร้างโค้ดไคลเอ็นต์ gRPC
  • สร้างคําขอ gRPC
  • ส่งคําขอ gRPC ไปยัง TensorFlow Serving
  • ดึงผลลัพธ์ที่คาดการณ์ไว้จากการตอบกลับของ gRPC และแสดง UI

คุณจะบรรลุเป้าหมายเหล่านี้ใน MainActivity.java.

ไม่บังคับ: สร้างโค้ดไคลเอ็นต์ gRPC

หากต้องการใช้ gRPC กับการให้บริการ TensorFlow คุณต้องทําตามเวิร์กโฟลว์ gRPC ดูข้อมูลเพิ่มเติมเกี่ยวกับรายละเอียดได้ที่เอกสารประกอบ gRPC

a9d0e5cb543467b4.png

TensorFlow Serving และ TensorFlow จะกําหนดไฟล์ .proto ให้คุณ เมื่อคํานึงถึง TensorFlow และ TensorFlow Serving 2.8 จึงต้องมีไฟล์ .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
  • หากต้องการสร้างโค้ดนี้ ให้เพิ่มโค้ดนี้ลงในไฟล์ app/build.gradle
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

คุณสร้างคําขอ gRPC ในฟังก์ชัน createGRPCRequest() เช่นเดียวกับคําขอ REST

private Request createGRPCRequest() {

}
  • เพิ่มโค้ดนี้ลงในฟังก์ชัน createGRPCRequest():
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 ไปยังการแสดงผล TensorFlow

คุณฟัง onClick(View view) Listener ได้แล้ว

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

            }
            else {
                // TODO: gRPC request
            }
        }
    }
)
  • เพิ่มโค้ดนี้ลงในสาขา gRPC
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;
}

ประมวลผลการตอบสนอง gRPC จาก TensorFlow Serving

คุณใช้ฟังก์ชัน postprocessGRPCResponse() เพื่อจัดการการตอบกลับได้เช่นเดียวกับ gRPC

private void postprocessGRPCResponse(Predict.PredictResponse response) {

}
  • เพิ่มโค้ดนี้ลงในฟังก์ชัน postprocessGRPCResponse()
// 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);

ตอนนี้ฟังก์ชันหลังการประมวลผลสามารถแยกค่าที่คาดการณ์ไว้จากการตอบกลับ และแสดงผลกล่องขอบเขตการตรวจจับใน UI ได้

เรียกใช้

  1. คลิกpng เรียกใช้ ‘app' ในเมนูการนําทางแล้วรอให้แอปโหลด
  2. เลือก gRPC > Run inference

โปรดรอ 2-3 วินาทีก่อนที่แอปจะแสดงผลกรอบล้อมรอบของแมวและแสดง 17 เป็นหมวดหมู่ของออบเจ็กต์ที่แมปกับหมวดหมู่ cat ในชุดข้อมูล COCO

8. ยินดีด้วย

คุณใช้ TensorFlow Serving เพื่อเพิ่มความสามารถในการตรวจหาออบเจ็กต์ในแอปแล้ว

ดูข้อมูลเพิ่มเติม