ジャパンサーチのトップページ

ジャパンサーチ(https://jpsearch.go.jp)の「画像AI検索」機能は、テキストによるモチーフ検索と、画像をアップロードしての類似画像検索を提供しています。公式の簡易Web APIガイドにはテキスト検索(text2image)と既存アイテムIDによる類似検索(imageパラメータ)の説明がありますが、画像アップロードによる検索については記載がありません。

Web UIのネットワーク通信を調査したところ、画像アップロード検索は3段階のAPIで実現されているようです。

APIの3段階フロー

Step 1: 画像から特徴量ベクトルを抽出

エンドポイント: POST https://jpsearch.go.jp/dl/api/imagefeatures/

画像をBase64エンコードして送信すると、64次元の特徴量ベクトルが返ります。

リクエスト:

{
  "img_b64": "data:image/jpeg;base64,/9j/4AAQ..."
}

レスポンス:

{
  "body": [-0.0879, -0.0091, -0.0712, ...(64次元)]
}

Step 2: 特徴量ベクトルから一時的な検索IDを生成

エンドポイント: POST https://jpsearch.go.jp/api/item/create-image-feature

Step 1で取得した特徴量ベクトル(配列)をそのままPOSTすると、一時的なIDが文字列で返ります。

リクエスト:

[-0.0879, -0.0091, -0.0712, ...]

レスポンス(プレーンテキスト):

jpsxt-Wnl3p6dJJ8b

このリクエストには X-Requested-With: XmlHttpRequest ヘッダーが必要です。

Step 3: IDで類似画像検索を実行

エンドポイント: GET https://jpsearch.go.jp/api/item/search/jps-cross?image={ID}&size=20

Step 2で取得したIDを、通常の類似画像検索と同じ image パラメータに渡します。このIDはJPSの既存アイテムIDと同じ形式で扱われるため、通常の検索APIがそのまま使えます。

レスポンスは通常のアイテム検索と同じJSON形式です。

検証結果

アプリのアイコン画像(虫眼鏡のイラスト)で検索したところ、1,932件がヒットし、民博の「肩掛袋」「上衣」「腰帯」などテキスタイル系の資料が上位に返りました。色味や形状の特徴で類似度が計算されていると考えられます。

ヘッダーの要件

調査の過程で、いくつかのヘッダーが必要であることがわかりました。

ヘッダーStep 1Step 2Step 3
Content-Type: application/json必要必要不要
Origin: https://jpsearch.go.jp必要必要不要
X-Requested-With: XmlHttpRequest不要必要不要

Step 1とStep 2はPOST、Step 3はGETです。

text2imageとの違い

項目text2image画像アップロード検索
入力テキスト(「鶴の日本画」等)画像ファイル
特徴量サーバー側で計算Step 1で明示的に取得
検索方法text2image=テキストimage=一時ID
多言語対応日本語、英語、フランス語等言語非依存

text2imageは内部でテキストと画像の埋め込みを計算して類似度を出していると考えられます。画像アップロード検索は画像同士の特徴量で直接比較する仕組みのようです。

Flutterアプリでの実装

この仕組みを使えば、モバイルアプリからカメラで撮影した画像で直接ジャパンサーチの類似画像検索が可能です。

Future<JpsSearchResult> searchByImageBytes({
  required Uint8List imageBytes,
  String mimeType = 'image/jpeg',
}) async {
  // Step 1: 特徴量取得
  final b64 = 'data:$mimeType;base64,${base64Encode(imageBytes)}';
  final featuresResponse = await client.post(
    Uri.parse('https://jpsearch.go.jp/dl/api/imagefeatures/'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({'img_b64': b64}),
  );
  final features = jsonDecode(featuresResponse.body)['body'];

  // Step 2: 一時ID取得
  final idResponse = await client.post(
    Uri.parse('https://jpsearch.go.jp/api/item/create-image-feature'),
    headers: {
      'Content-Type': 'application/json',
      'X-Requested-With': 'XmlHttpRequest',
    },
    body: jsonEncode(features),
  );
  final featureId = idResponse.body.trim();

  // Step 3: 類似画像検索
  return searchSimilarImages(itemId: featureId);
}

注意点

Step 1とStep 2のエンドポイントは公式APIガイドには記載されていません。Web UIの内部APIとして使用されているもので、仕様が変更される可能性があります。