4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Vision API Product Search] Cloud Vision APIをJavaで使ってみる(画像検索)

Posted at

Googleが提供しているVision API Product Searchを
Javaで使ってみました。
Googleサービスアカウントを使用して、登録した商品画像に対して
ローカルにある画像から類似画像の検索をかけてみました。

Vision API Product Searchとは
Googleが提供しているAPIで、登録した画像から類似画像を検索するサービスです。

料金は、月間費用で予測費用とストレージ費用に分かれていて、
登録画像数が1000枚までならば、
 どちらも無料。
登録画像数が500万枚までならば、
 予測費用が$4.5/1000枚、ストレージ費用が$0.1/1000枚
登録画像数が2000万枚までならば、
 予測費用が$1.8/1000枚、ストレージ費用が$0.1/1000枚
となっています。

No 目次
1 Cloud Vision APIを使用するための準備
1 プロジェクトの作成
2 Cloud Vision APIの設定
3 Googleサービスアカウントの作成
2 Cloud Vision APIを使ってみる
1 googleライブラリの読み込み
2 商品情報の登録
3 商品画像で検索

1. Cloud Vision APIを使用するための準備

設定は下記から行います。
⧉Google クラウド プラットフォーム

1.1. プロジェクトの作成

こちらの記事を参照ください
⧉[Google Sheets API] Google Sheets API v4をJavaで操作する(1.1. プロジェクトの作成)

1.2. Cloud Vision APIの設定

Cloud Vision APIを使えるようにします。

1. メニューの 「APIとサービス」 -> 「ライブラリ」 を選択します。
2. Croud Visionと入力して「Cloud Vision API」を検索します。
3. 「有効にする」ボタンを押して「Cloud Vision API」を使用可能にします。

1.3. Googleサービスアカウントの作成

こちらの記事を参照ください
⧉[Google Sheets API] Google Sheets API v4をJavaで操作する(1.3. Googleサービスアカウントの作成)

上記の記事に加え、
サービスアカウント作成時に「Storageオブジェクト閲覧者」の権限を付与します。
(サービスアカウント作成後でもOK)
Cloud Vision APIで登録する商品画像は、Cloud Storageに登録されている画像のみが
参照可能となるためです。
(商品への登録画像は、ローカルの画像を参照させることはできません。)

1.4. Cloud Storageに使用する画像を置く

1. Cloud Storageに商品に登録する画像をおきます。
2. 登録した画像を選択して「gsutil URIコピー」を選択します。

このURLは後のプログラムで使用します。

2. Cloud Vision APIを使ってみる

2.1. googleライブラリの読み込み

Cloud Vision APIを使用するためにライブラリのパスを設定します。
私の環境はpom.xmlで下記を指定しています。
Javaのバージョンは21を使用しています。

dependenciesと同じ階層にdependencyManagementを追加します。

<dependencies>
....
    <dependency>
        <groupId>com.google.cloud</groupId>
        <artifactId>google-cloud-vision</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>libraries-bom</artifactId>
            <version>26.31.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2.2. CredentialsProviderの取得

サービスアカウントのJSONを指定してCredentialsProviderを取得します。

ファイルパスには、1.3. Googleサービスアカウントの作成
でダウンロードしたJSONファイルのパスを指定してください。

private static CredentialsProvider getCredentialsProvider() throws IOException {
    String jsonFilePath = "サービスアカウントJSONキーファイルのパス";
    FileInputStream jsonFileInputStream = new FileInputStream(jsonFilePath);
    GoogleCredentials credentials = GoogleCredentials.fromStream(jsonFileInputStream);

    return FixedCredentialsProvider.create(credentials);
}

2.3. 商品情報の登録

商品情報を登録します。

登録の順番
商品セットをまず作成し、それに対して商品を追加していきます。

1.商品セットの作成
2.商品の作成
3.商品セット(上記1.)に対して商品(上記2.)を登録
4.商品(上記2.)に対して画像を登録

それぞれの情報
商品セット、商品、画像のそれぞれの情報に下記を設定しています。

商品セットの情報には、
・商品セットID、商品セット名
商品の情報には、
・商品ID、商品名、商品のカテゴリ、商品画像
商品の画像には、
・画像ID、画像URL(Cloud Storageの画像パス)

画像の推奨事項
登録する商品画像はgoogleによると下記が推奨されているそうです。

・ファイルサイズは最大20MB
・商品の特徴となる部分をできるだけ画像内に収める
・複数をセットとする場合は全て画像に入れる(靴なら、右足だけではなく、左足も入れる)
・背景は白
・解像度を高くする
・PNGで透過ありなら、背景が透明なら単色の色する

public static void main(String[] args) throws IOException{
    String productSetId = "トミカ";                //商品セットのID
    String productSetDisplayName = "トミカシリーズ"; //商品セット名
    String productCategory = "toys-v2";         //商品のカテゴリ
    String[][] product_list = getProductList(); //登録する商品情報
    
    String locationName = LocationName.format(PROJECT_ID, COMPUTE_REGION);
    try (ProductSearchClient client = getProductSearchClient()) {
        //1.商品セットを作成
        CreateProductSetRequest request = CreateProductSetRequest.newBuilder()
                .setParent(locationName)
                .setProductSetId(productSetId) //商品セットID
                .setProductSet(ProductSet.newBuilder().setDisplayName(productSetDisplayName).build())
                                               //商品セット名
                .build();
        ProductSet productSet = client.createProductSet(request);

        for (String[] p : product_list) {
            //2.商品を作成
            Product myProduct = Product.newBuilder()
                    .setName(p[0])        //商品ID
                    .setDisplayName(p[1]) //商品名
                    .setProductCategory(productCategory) //商品カテゴリ
                    .build();
            Product product = client.createProduct(
                    locationName,
                    myProduct,           //商品情報
                    p[0]);               //商品ID

            //3.商品セットに商品を追加
            client.addProductToProductSet(productSet.getName(), product.getName());

            //4.商品画像を登録
            client.createReferenceImage(
                    product.getName(), //商品リソース名
                    ReferenceImage.newBuilder().setUri(p[2]).build(), //商品画像
                    p[0]);             //画像ID(1枚だけなのでとりあえず商品IDと一緒)
        }
    }
}

ProductSearchClientインスタンスを生成します。
商品関連の情報を扱うメソッドが提供されます。

private static ProductSearchClient getProductSearchClient() throws IOException {
    ProductSearchSettings productSearchSettings = ProductSearchSettings.newBuilder()
            .setCredentialsProvider(getCredentialsProvider()).build();
    return ProductSearchClient.create(productSearchSettings);
}

登録する商品のリストです。
本来クラスを用意してまとめた方がいいかもしれませんが、
とりあえずなので配列で用意しています。
(自前で撮影したトミカの画像を使用しています)
商品ID、商品名、商品画像(Cloud Storageのパス)の順です。

private static String[][] getProductList(){
    return { //商品ID、商品名、商品画像(Cloud Storageのパス)
        { "トミカ No.48",  "日野 プロフィア 葛飾トラック","gs://test_doran/IMG_7830.jpg" },
        { "トミカ No.100", "スズキ ジムニー JAF ロードサービスカー","gs://test_doran/IMG_7831.jpg" },
        { "トミカ No.101", "いすゞ ギガ ダンプカー","gs://test_doran/IMG_7832.jpg" },
        { "トミカ No.45",  "トヨタ ダイナ 清掃車", "gs://test_doran/IMG_7833.jpg" },
        { "トミカ プレミアム No.16", "陸上自衛隊 16式機動戦闘車","gs://test_doran/IMG_7834.jpg" }
    };
}

2.4. 商品画像で検索

ローカル画像を使用して登録した商品画像の類似画像を検索します。

public static void main(String[] args) throws IOException{
    String productSetId = "トミカ";         //検索する商品セットID
    String productCategory = "toys-v2";  //検索する商品のカテゴリ
    String path = "ローカルファイルのパス";    //検索する画像のパス
    try (ImageAnnotatorClient client = getImageAnnotatorClient()) {

        Feature featuresElement = Feature.newBuilder().setType(Type.PRODUCT_SEARCH).build();
        Image image = Image.newBuilder().setContent(ByteString.copyFrom(Files.readAllBytes(new File(path).toPath()))).build();
        ImageContext imageContext = ImageContext.newBuilder()
                .setProductSearchParams(
                        ProductSearchParams.newBuilder()
                                .setProductSet(getProductSetName(productSetId))
                                .addProductCategories(productCategory))
                .build();

        AnnotateImageRequest annotateImageRequest = AnnotateImageRequest.newBuilder()
                .addFeatures(featuresElement)
                .setImage(image)
                .setImageContext(imageContext)
                .build();
        
        BatchAnnotateImagesResponse response = client.batchAnnotateImages(Arrays.asList(annotateImageRequest));
        output(response);
    }
}

ImageAnnotatorClientインスタンスを生成します。
画像を扱うメソッドが提供されます。

private static ImageAnnotatorClient getImageAnnotatorClient() throws IOException {
    ImageAnnotatorSettings imageAnnotatorSettings = ImageAnnotatorSettings.newBuilder()
            .setCredentialsProvider(getCredentialsProvider()).build();
    return ImageAnnotatorClient.create(imageAnnotatorSettings);
}

検索結果を出力しています。
実行結果のResultでは以下の情報が取得できます。

名称 概要
商品情報 Product name 商品のリソース名
displayName 商品名
description 商品概要
productCategory 商品カテゴリ
productLabel 商品に添付しているキーと値のペア
スコア score 0〜1までのfloat値。
数値が高いほど信頼度が高いです。
画像名 image 画像のリソース名
private static void output(BatchAnnotateImagesResponse response) {
    System.out.println(response);
    List<Result> similarProducts = response.getResponses(0).getProductSearchResults().getResultsList();
    for (Result product : similarProducts) {
        System.out.println("--------------");
        System.out.println(("商品リソース名: "+product.getProduct().getName()));
        System.out.println("商品名: "+product.getProduct().getDisplayName());
        System.out.println("スコア: "+product.getScore());
        System.out.println("画像名: "+product.getImage());
        System.out.println("--------------");
    }
}

実行結果
近しい画像が検索されました。
スコアの高い方がより近い画像を示しています。
ここらへんはまた別の記事で検証したいと思います。

--------------
商品リソース名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.48
商品名: 日野 プロフィア 葛飾トラック
スコア: 0.7208772
画像名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.48/referenceImages/トミカ No.48
--------------
--------------
商品リソース名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.45
商品名: トヨタ ダイナ 清掃車
スコア: 0.4311223
画像名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.45/referenceImages/トミカ No.45
--------------
--------------
商品リソース名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.101
商品名: いすゞ ギガ ダンプカー
スコア: 0.33746362
画像名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.101/referenceImages/トミカ No.101
--------------
--------------
商品リソース名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.100
商品名: スズキ ジムニー JAF ロードサービスカー
スコア: 0.29908606
画像名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ No.100/referenceImages/トミカ No.100
--------------
--------------
商品リソース名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ プレミアム No.16
商品名: 陸上自衛隊 16式機動戦闘車
スコア: 0.20026323
画像名: projects/astute-tractor-400101/locations/asia-east1/products/トミカ プレミアム No.16/referenceImages/トミカ プレミアム No.16
--------------

おしまい。。。
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?