
ゲーム
イベント
マガジン
技術ブログ
はじめに はじめまして。 KINTO テクノロジーズで KINTO Unlimited Android アプリを開発している JR.Liang です。 本記事では、KINTO Unlimited アプリにて提供する「これなにガイド」スキャン機能の AR エフェクトについて、Android における技術的な検証を紹介します。 特に MediaPipe のソリューションを用いて幅広い Android デバイスで AR エフェクトを実現した実装にフォーカスします。 これなにガイドとは 「これなにガイド」は AR(拡張現実)を活用して、車内スイッチの用途や使い方をテキストと動画で案内する機能です。紹介動画をご覧ください。 https://youtube.com/watch?v=E8zfNzuHr7g&embeds_referring_euri=https%3A%2F%2Fcorp.kinto-jp.com%2F&source_ve_path=MjM4NTE 上記の紹介動画は iOS アプリでの動作を示しています。スイッチ上に表示された黄色の丸 🟡 が、AR 技術で実現した仮想コンテンツです。 機能全体の仕組みは以下の流れです。本記事では 3 番目(描画)に関する内容を扱います。 1. アプリのカメラを起動、カメラ画像を取得 2. 機械学習における物体認識を用いて、車内のスイッチを検出 3. 検出した座標を元に、ボタンとテキストをフレーム上に描画 4. ボタンをタップして、当該スイッチのテキストと動画を表示 Android AR 技術検証の経緯 当初の Android 版「これなにガイド」のスキャン機能では、Canvas を利用して毎フレーム検出される座標に描画する実装でした。そのため検出の時間差により、スマホ(カメラ)を動かすと描画のズレが生じていました。 2D Canvas 幸い、MediaPipe のソリューションである Instant Motion Tracking モジュールで 素早くかつ安定した AR エフェクトを実現できることがわかり、Android への導入を検証しました。 3D OpenGL MediaPipe Instant Motion Tracking MediaPipe は Google が開発したオープンソースの ML フレームワークで、顔検出・手のトラッキング・姿勢推定などリアルタイム映像処理のソリューションを提供します。 その中の Instant Motion Tracking は、現実世界のシーン上に 3D 仮想コンテンツをリアルタイムで正確に配置できる AR トラッキング機能です。初期化や厳密なキャリブレーションが不要で、静止面や動いている面の上にコンテンツを置くことが可能です。 @ card Android + MediaPipe AR アーキテクチャ graph TB A(Android CameraX) --> |Camera Frame| B(Instant Motion Tracking) B --> |Camera Image| C(TensorFlow Object Detection) C --> |Detections Information| B(Instant Motion Tracking) B --> |Output Stream| D(Android Surface Rendering) CameraX で取得したフレームを Instant Motion Tracking に渡し、TensorFlow Lite で物体検出した情報を元に AR コンテンツを描画・追従させるパイプラインです。 MediaPipe ライブラリの作成 MediaPipe では Bazel を使用してパッケージをビルドします。Android に適合する AAR として書き出してアプリに組み込みます。 https://chuoling.github.io/mediapipe/getting_started/android_archive_library.html AAR をビルドする BUILD ファイルを作成し、 instant_motion_tracking を基盤とした定義を記述します。 load("//mediapipe/java/com/google/mediapipe:mediapipe_aar.bzl", "mediapipe_aar") mediapipe_aar( name = "mediapipe_ar", calculators = ["//mediapipe/graphs/instant_motion_tracking:instant_motion_tracking_deps"] ) MediaPipe は C++ が中核のため、C++ ランタイムである libc++_shared.so を AAR に同梱する必要があります。 https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/third_party/BUILD#L399-L403 また Instant Motion Tracking では画像処理ライブラリ OpenCV を利用し、AR トラッキングを行います。 https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/WORKSPACE#L649-L655 上記サードパーティのライブラリを含めて、以下のコマンドで AAR をビルドします。 bazel build -c opt --strip=ALWAYS \ --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ --fat_apk_cpu=arm64-v8a \ --linkopt=-Wl,-z,max-page-size=16384 \ //path/to/the/aar/build/mediapipe_ar:mediapipe_ar.aar 市場に流通している Android デバイスは主に arm64-v8a アーキテクチャのため、AAR のサイズを抑える目的で fat_apk_cpu=arm64-v8a にします。 C++ ライブラリの 16KB page-size に対応するため、 max-page-size=16384 を追加します。 また AAR を利用するにはグラフ構造を定義するファイル( binarypb )が必要です。 bazel build -c opt mediapipe/graphs/instant_motion_tracking:instant_motion_tracking.binarypb Instant Motion Tracking の導入 AAR をアプリに組み込んで、Android 側の実装を解説していきます。 下記は AAR に組み込んだ instant_motion_tracking の全体構造です。 instant_motion_tracking.pbtxt の構成 グラフ定義ファイル instant_motion_tracking.pbtxt は、Calculator(処理ノード)・入出力ストリーム・サイドパケットの 3 要素で構成されます。 Calculator 各 Calculator がパイプライン上でどの処理を担うかを示します。 Calculator 役割 ImageTransformationCalculator カメラフレームを 320×320(FIT)にリサイズ。物体検出モデルの入力サイズに合わせる GpuBufferToImageFrameCalculator GPU テクスチャを CPU の ImageFrame に変換。TensorFlow Lite 推論に使用 StickerManagerCalculator Sticker Proto をパースし、初期アンカーの座標・回転・スケール・レンダリング種別に分解 RegionTrackingSubgraph ボックストラッキングでアンカー位置を追従。内部に TrackedAnchorManagerCalculator (アンカー管理)と BoxTrackingSubgraphGpu (GPU トラッキング)を持つ MatricesManagerCalculator トラッキング結果・回転・スケール・FOV・アスペクト比から OpenGL 用 4×4 モデル行列を生成 GlAnimationOverlayCalculator モデル行列とテクスチャを用いて、元のカメラフレーム上に AR コンテンツを OpenGL で描画し output_video として出力 input_stream / output_stream input_stream はフレームごとに Android 側から送信するデータ、 output_stream はグラフの処理結果です。 ストリーム名 C++ 型 方向 用途 input_video GpuBuffer Input カメラフレーム sticker_proto_string String(Serialized Proto) Input ステッカーの座標・スケール等(Sticker Proto) sticker_sentinels vector Input 座標をリセットするステッカー ID の配列 gif_textures vector Input AR コンテンツの Bitmap テクスチャ配列 gif_aspect_ratios vector Input 各テクスチャのアスペクト比 output_video GpuBuffer Output AR 描画済みフレーム input_side_packet input_side_packet は初期化時に一度だけ渡す定数で、グラフ実行中は変化しません。 パケット名 用途 vertical_fov_radians カメラの垂直 FOV(ラジアン) aspect_ratio カメラのアスペクト比 width / height カメラ解像度 gif_texture デフォルトテクスチャ(1x1 プレースホルダ) gif_asset_name AR テクスチャ描画用のポリゴンメッシュ( .obj )ファイル名 Android への導入に当たって、公式サンプルのコードを参考にします。 https://github.com/google-ai-edge/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking 1. 初期化 MediaPipe を使用する前に、ネイティブライブラリの読み込みとアセットマネージャーの初期化が必要です。 companion object { init { System.loadLibrary("mediapipe_jni") System.loadLibrary("opencv_java4") } } // onCreate 相当の処理 AndroidAssetUtil.initializeNativeAssetManager(context) mediapipe_jni : MediaPipe のコア処理を行う JNI ライブラリ opencv_java4 : AR トラッキングに使用する OpenCV ライブラリ initializeNativeAssetManager : ネイティブコードからアセット(binarypb 等)にアクセスするために必要 2. カメラを起動する 公式サンプルを参考に、以下の順序でパイプラインを構築します。 データフロー: CameraX → ExternalTextureConverter → FrameProcessor → SurfaceView 2.1 EGL 環境と FrameProcessor の初期化 val eglManager = EglManager(null) val frameProcessor = FrameProcessor( context, eglManager.nativeContext, "instant_motion_tracking.binarypb", "input_video", "output_video" ).apply { videoSurfaceOutput.setFlipY(true) setInputSidePackets( mapOf( "gif_asset_name" to packetCreator.createString("gif.obj.uuu"), "vertical_fov_radians" to packetCreator.createFloat32(fovRadians), "aspect_ratio" to packetCreator.createFloat32(resolution.width.toFloat() / resolution.height.toFloat()), "width" to packetCreator.createInt32(resolution.width), "height" to packetCreator.createInt32(resolution.height), "gif_texture" to packetCreator.createRgbaImageFrame(createBitmap(1, 1)) ) ) } EglManager : OpenGL ES の EGL コンテキストを作成・管理。MediaPipe のグラフ内 GPU Calculator( GlAnimationOverlayCalculator 等)が OpenGL で描画するために必要 FrameProcessor : EGL コンテキストを受け取り、グラフの読み込み・入出力ストリームの管理・フレームごとのグラフ実行を行う instant_motion_tracking.binarypb : .pbtxt を Bazel でコンパイルしたグラフ定義バイナリ input_video : MediaPipe グラフへカメラフレームを入力 output_video : グラフで処理(AR 描画など)された映像を出力 videoSurfaceOutput.setFlipY(true) : OpenGL とカメラの Y 軸方向が逆のため、出力映像を上下反転して正しい向きにする setInputSidePackets : グラフの input_side_packet に対応する定数をまとめて設定。カメラの FOV・アスペクト比・解像度など、グラフ実行中に変化しない値を初期化時に一度だけ渡す gif_asset_name は AR テクスチャを描画するための ポリゴンメッシュ(頂点データ) 、ここでは公式サンプルの gif.obj.uuu を利用 2.2 カメラ映像の変換パイプライン構築 val externalTextureConverter = ExternalTextureConverter(eglManager.context, 2).apply { setFlipY(true) setConsumer(frameProcessor) setDestinationSize(resolution.width, resolution.height) } val cameraHelper = object : CameraXPreviewHelper() { override fun getCameraCharacteristics(context: Context?, lensFacing: Int?) = cameraCharacteristics }.apply { setOnCameraStartedListener(onCameraStartedListener) startCamera( context, lifecycleOwner, CameraHelper.CameraFacing.BACK, externalTextureConverter.surfaceTexture, Size(resolution.height, resolution.width) ) } ExternalTextureConverter : カメラの GL_EXTERNAL_OES テクスチャを MediaPipe が処理できる標準テクスチャに変換 setFlipY(true) : カメラ映像の上下反転を補正 setDestinationSize(resolution.width, resolution.height) : パイプラインの処理サイズはポートレート座標(例: 960×1280 )で指定 CameraXPreviewHelper : CameraX でバックカメラを起動し、Converter の SurfaceTexture に出力 startCamera(targetSize = Size(resolution.height, resolution.width)) : CameraX はセンサー座標(ランドスケープ)を期待するため、width と height を入れ替えて渡す 公式サンプルでは CameraXPreviewHelper をそのまま使用し、内部で CameraManager からカメラ特性を取得します。 https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java#L558-L560 本実装では getCameraCharacteristics をオーバーライドし、事前に取得済みの CameraCharacteristics を直接渡します。これにより FOV やアスペクト比の算出に使うカメラ情報を、アプリ側で一元管理できます。 2.3 出力先SurfaceViewの設定 SurfaceView(context).apply { holder.addCallback(object : SurfaceHolder.Callback { override fun surfaceCreated(holder: SurfaceHolder) { frameProcessor.videoSurfaceOutput.setSurface(holder.surface) } override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { val displaySize = cameraHelper.computeDisplaySizeFromViewSize(Size(width, height)) val (displayWidth, displayHeight) = if (cameraHelper.isCameraRotated) { displaySize.height to displaySize.width } else { displaySize.width to displaySize.height } externalTextureConverter.setDestinationSize(displayWidth, displayHeight) } override fun surfaceDestroyed(holder: SurfaceHolder) { frameProcessor.videoSurfaceOutput.setSurface(null) } }) } SurfaceHolder.Callback : SurfaceView のライフサイクルに応じて FrameProcessor の出力先を管理 surfaceCreated : FrameProcessor の出力先として Surface を設定 surfaceChanged : 画面回転・サイズ変更時に出力解像度を調整 surfaceDestroyed : リソース解放 3. 検出座標をグラフに渡す 物体検出(TensorFlow Lite 等)で得られた座標を MediaPipe グラフに渡し、AR コンテンツを配置します。 3.1 グラフから変換済み画像を取得 MediaPipe グラフ内で ImageTransformationCalculator と GpuBufferToImageFrameCalculator によって変換された画像を addPacketCallback で受け取り、物体検出に使用します。 frameProcessor.addPacketCallback("transformed_input_video_cpu") { packet -> packet ?: return@addPacketCallback // 変換済み画像を物体検出(TensorFlow Lite)に渡す val bitmap = PacketGetter.getBitmapFromRgba(packet) objectDetector.detect(bitmap) { detections -> // 検出結果を処理 } } transformed_input_video_cpu : 変換後の画像を出力するストリーム名 3.2 座標の正規化 物体検出結果のピクセル座標を、MediaPipe が期待する正規化座標に変換します。 // ピクセル座標 → 正規化座標 (0.0〜1.0) val normalizedX = pixelX / imageWidth.toFloat() val normalizedY = pixelY / imageHeight.toFloat() 3.3 Sticker Proto の構造 Instant Motion Tracking では、AR オブジェクトの位置情報を Protocol Buffers 形式で定義します。 message Sticker { int32 id = 1; // ユニークID float x = 2; // 正規化X座標 (0.0〜1.0) float y = 3; // 正規化Y座標 (0.0〜1.0) float rotation = 4; // 回転角度 float scale = 5; // スケール int32 render_id = 6; // レンダリングID } message StickerRoll { repeated Sticker sticker = 1; } 3.4 フレームごとにパケットを送信 setOnWillAddFrameListener を使用して、各フレーム処理前に検出座標をグラフへ送信します。 frameProcessor.setOnWillAddFrameListener { timestamp -> with(frameProcessor.graph) { // 検出された物体の座標情報をパケットとして送信 val stickerRoll = StickerRoll.newBuilder() .addAllSticker(detectedObjects.map { detection -> Sticker.newBuilder() .setId(detection.id) .setX(detection.normalizedX) // 0.0〜1.0 .setY(detection.normalizedY) // 0.0〜1.0 .setScale(detection.scale) .build() }) .build() val stickersPacket = packetCreator.createSerializedProto(stickerRoll) addPacketToInputStream("sticker_proto_string", stickersPacket, timestamp) } } FrameProcessor.setOnWillAddFrameListener : 各フレームがグラフに送られる直前に呼ばれるコールバック FrameProcessor.graph.addPacketToInputStream : 入力ストリームにパケットを追加 sticker_proto_string : グラフ定義で指定された入力ストリーム名 4. テクスチャ(Bitmap)の描画と送信 位置情報と同時に、AR コンテンツとして描画する Bitmap テクスチャもグラフに渡します。 4.1 Bitmap テクスチャの生成 検出された各スイッチに対して、丸アイコンとラベルテキストを含む Bitmap を生成します。 val bitmap = createBitmap(width.toInt(), height.toInt()).apply { with(Canvas(this)) { concat(Matrix().apply { preScale(-1.0f, 1.0f, width / 2f, height / 2f) // X軸を反転して描画 }) drawCircle(circleX, circleY, CIRCLE_RADIUS, circlePaint) drawRect(rectLeft, rectTop, rectRight, rectBottom, backgroundPaint) } } Matrix().preScale(-1.0f, 1.0f) で Bitmap を左右反転しています。以下の IMU 行列に合わせるためです。 float imu_matrix[9] = { -1.0f, 0.0f, 0.0f, // X軸 → 反転(-X) 0.0f, 0.0f, 1.0f, // Y軸 → Z軸へ 0.0f, 1.0f, 0.0f // Z軸 → Y軸へ }; この行列は OpenGL モデル行列(4x4)の回転成分として使われ、Y/Z 軸の入れ替えと X 軸反転でテクスチャをカメラ平面に平行に固定します。 本来はデバイスの IMU センサーから回転行列を受け取り、端末の傾きに追従させます。 https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L218-L220 本実装では固定値にすることで 常にカメラ正面を向く (ビルボード効果)ようにし、 (0,0) の -1.0 による X 軸反転を Bitmap 側の preScale(-1.0f, 1.0f) で打ち消します。 4.2 テクスチャの送信 // テクスチャ画像(Bitmap配列) val texturesPacket = packetCreator.createRgbaImageFrameVector( renderStickers.map { it.bitmap }.toTypedArray() ) addPacketToInputStream("gif_textures", texturesPacket, timestamp) // アスペクト比(テクスチャの縦横比) val aspectRatiosPacket = packetCreator.createFloat32Vector( renderStickers.map { it.aspectRatio }.toFloatArray() ) addPacketToInputStream("gif_aspect_ratios", aspectRatiosPacket, timestamp) PacketCreator.createRgbaImageFrameVector : 複数の Bitmap を RGBA 形式のパケットに変換 gif_textures : テクスチャ画像の入力ストリーム gif_aspect_ratios : 各テクスチャのアスペクト比(正しいスケーリングに必要) 公式サンプルでは createRgbaImageFrame を使用して 単一のテクスチャ をグラフに渡します。 https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L608-L610 本実装では、複数の検出オブジェクトに対応するため createRgbaImageFrameVector で 複数テクスチャを同時に送信 し、 gif_aspect_ratios も createFloat32Vector で 各テクスチャに対応するアスペクト比の配列 を渡すよう拡張します。これにより、検出された各スイッチに異なるラベル(テキスト付きBitmap)を正しい縦横比で表示できます。 ここまでで AR コンテンツをカメラ上に表示できました。 5. 座標の更新 トラッキング中のステッカー座標を更新するには、新しい座標を持つ sticker_proto_string と、リセット対象の ID を含む sticker_sentinels を同一 timestamp で送信します。 TrackedAnchorManagerCalculator が該当 ID のトラッキングボックスを破棄し、新しい座標でトラッキングを再開します。 // 更新した座標で Sticker Proto を再構築 val stickersPacket = packetCreator.createSerializedProto(stickerRoll) addPacketToInputStream("sticker_proto_string", stickersPacket, timestamp) // リセット対象のステッカー ID を送信 val stickerSentinels = packetCreator.createInt32Vector(updateIds) addPacketToInputStream("sticker_sentinels", stickerSentinels, timestamp) 公式サンプルでは sticker_sentinel で 単一のステッカー ID を送信します。 https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L342-L344 本実装では sticker_sentinels として createInt32Vector で 複数のステッカー ID を配列 で渡すよう拡張し、物体検出で座標が更新された複数のステッカーを同時にリセットできるようにします。 最後に 以上が MediaPipe Instant Motion Tracking を用いた技術的な実装解説でした。決して容易に導入できる手法ではありませんが、本機能の要件に対して Android に最も適した解決策だと考えています。 以前に ARCore の検証も行いましたが、ARCore は SLAM 技術による事前の 3D マッピングに時間を要し、 素早くかつ安定した AR エフェクトの実現には適さなかったため、検証を断念しました。 両フレームワークの違いを以下にまとめます。AR 技術の検討で参考になれば幸いです。 項目 Instant Motion Tracking ARCore 仕組み 2D ボックストラッキング + OpenGL 描画 環境マッピング + 平面検出(SLAM) デバイス要件 OpenGL ES 対応であれば動作 ARCore 対応デバイスのみ(Google 認定必須) 安定性 検出座標に依存するため補正が必要 空間認識が高精度で安定 導入コスト Bazel ビルド・C++ Calculator のカスタマイズが必要 SDK 導入のみで比較的容易 オープンソース あり(Apache 2.0) なし(プロプライエタリ) カスタマイズ性 Calculator の追加・変更で柔軟に拡張可能 SDK の API 範囲内に限定 パフォーマンス 軽量(2D トラッキングベースのため CPU/GPU 負荷が低い) 高負荷(環境の 3D 空間マッピングを常時実行) 学習コスト 高い(Bazel・C++・OpenGL・Protocol Buffers の知識が必要) 低い(Android SDK の知見で導入可能)
本日、 Amazon Quick が AWS アジアパシフィック (東京) リージョンで利用可能になったことをお知らせします。 このローンチにより、日本のお客様は 日本国内でホストされるデータと機械学習モデルを活用しながら Amazon Quick のエージェント型 AI 機能を利用できるようになりました。(注: 本日時点では Web 検索機能のみ日本国外のモデルを利用しています。) Amazon Quick は、一つのUIからデータから迅速に回答を取得し、そのインサイトを業務アクションへとつなげることができる AI ベースのデジタルワークスペース です。 東京リージョンでの提供開始により、日本のお客様は 低レイテンシー、国内データレジデンシー、エンタープライズレベルの AI 機能 を活用できるようになります。 Amazon Quick の概要 Amazon Quick は、 リサーチ、ビジネスインテリジェンス、そして自動化機能を統合したエージェンティック AI ワークスペース を提供します。 従来、ユーザーはデータ収集、分析、業務実行のために複数のアプリケーションを切り替える必要がありました。Quick を利用することで、これらの機能を 1 つのプラットフォーム上で統合的に利用可能 です。 ユーザーは自然言語で質問するだけで、 データからインサイトを取得 ダッシュボードやレポートを生成 アプリケーションや業務システムを横断してワークフローを自動化 することが可能になります。 Amazon Quick の主な機能 Amazon Quick は、次の 5 つの主要機能で構成されています。 Quick Index 組織内のドキュメント、ファイル、アプリケーションデータを横断的に統合し、検索可能なナレッジベースを構築します。 Quick Research 組織内のデータに加え、Web や外部データソースを組み合わせて分析し、インサイトやレポートを生成する AI リサーチエージェントです。 Quick Sight データをダッシュボードや可視化、自然言語による要約へと変換する AI ビジネスインテリジェンス機能です。 Quick Flows 自然言語で日常業務のワークフローを作成し、Web アプリケーションやサービス全体に渡るステップを自動化できる機能です。 Quick Automate 複数の専門的な AI エージェントを活用し、ガバナンスコントロールと人間による承認ステップを備え、複雑な業務プロセスを自動化してエンタープライズシステム全体の高度なオーケストレーションを実現する機能です。 これらの機能により、Amazon Quick は ビジネスユーザーのための AI アシスタント として活用できます。 例えば Quick を使用すると、 社内データと外部情報を横断検索 データから可視化やサマリーを生成 チケット作成や承認ワークフローなどの業務アクションを実行 これらすべてを会話型インターフェースで実現し、組織全体でのAI導入の障壁を下げます。 エンタープライズグレードのセキュリティとデータプライバシー Amazon Quick は、エンタープライズ利用を前提に セキュリティとデータプライバシーを重視して設計 されています。 お客様のデータやクエリは 基盤モデルのトレーニングに使用されることはありません 。組織は、AWS マネジメントコンソールを通じて、 データ、権限、統合を完全に管理が可能 です。 また、東京リージョンで利用することで、 データを日本国内の AWS インフラストラクチャ内に保持することが可能 になります。 日本国内でのサービス提供によるメリット Amazon Quick が東京リージョンで利用可能になったことで、日本のお客様は データを日本国内にとどめつつ、低レイテンシ―で AI 分析や自動化機能を利用可能 です。 国内データレジデンシー データを日本国内の AWS インフラストラクチャに留めることができ、組織が規制や内部ガバナンスの要件を満たすのに役立ちます。 低レイテンシー 日本国内ユーザーのネットワークレイテンシーが改善され、AI 応答やダッシュボード表示、ワークフローの応答などのパフォーマンスが向上します。 高可用性 東京リージョンの複数のアベイラビリティーゾーンをに展開され、高い可用性とレジリエンシ―を提供します。 まとめ Amazon Quick が東京リージョンで利用可能になったことで、日本のお客様は AI を活用した分析、リサーチ、自動化機能を国内インフラ上で利用できるようになりました。 Amazon Quick は、質問からインサイトの取得、そして業務アクションの実行までを 1 つのワークスペースで実現 します。 ぜひ東京リージョンで Amazon Quick をお試しください。 Amazon Quick の開始方法 お客様は、AWS マネジメントコンソールを通じて、 東京リージョン (ap-northeast-1) で今すぐ Amazon Quick を使い始めることが可能です。 始めるには: AWS アカウントで Amazon Quick を有効にします Amazon S3 や Microsoft SharePoint、Slack、その他のビジネスアプリケーションなどのデータソースを接続します Quick Index を設定してナレッジベースを構築します Quick Research 、 Quick Sight 、 Quick Flows を使って洞察を生み出し、ワークフローを自動化します 新規のお客様は、 最大 25 ユーザーまでの 30 日間の無料トライアル も利用可能です。 筆者について 溝渕 匡 (Masa Mizobuchi) AWS Japan のソリューションアーキテクトとして、様々なお客様に日々クラウド活用の技術支援を行なっています。ソフトウェアの設計、開発、データ分析、Scrum などが好きで、Scrum は好きが高じて導入プログラムを開発して複数企業への導入実績があります。趣味はゲームで遊んだりゲームを作ったりすることです。
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp デザイナーとプロダクトマネージャー(PdM)が同じ組織になってもうすぐ1年が経ちますので、その挑戦、苦労、変化について書こうと思います。ラクスは3月末決算のため4月には来期に向けて取り組みを書こうと思いますが本記事は厚めに振り返ります。 組織図を書き換え、デザインを解放する なぜ統合が必要だったのか:「上流×一次情報×検証」が欠けると、協働は痩せていく デザインの再定義:それは「問題を解決するための設計」である 壁を壊すだけでは足りない:一次情報・意思決定・効果検証を「仕組み」にする プロセスの磨き込みが、思考の余白を生む(AIは黒子) PdMとデザイナーの共鳴:信頼が戦略を加速させる UX志向のガードレール:越境は「自由」ではなく「責任」 リアルな壁:未経験の荒野と「固定観念」との戦い(アンケートで見えた“本質”) おわりに:各自が「+1」の越境を止めない 組織図を書き換え、デザインを解放する 「デザイナーとプロダクトマネージャー(PdM)を、同じ組織にする。」 この決断は、単なる管理上の変更ではありませんでした。 それは、プロダクト開発における「役割」という名の聖域を取り払い、全員が「ユーザー価値」という一点に向き合うための、静かな、けれど野心的な挑戦の始まりでした。 これまでのラクスの開発現場では、「仕様を作る人(PdM)」と「形にする人(デザイナー)」という分断が、あたかも効率的な分業であるかのように受け入れられてきました。 しかし、その境界線はときに、情報の劣化や責任の押し付け合いを生む「見えない壁」となって立ちはだかります。 この1年で私たちが目指したのは、デザインを「UI(見た目)を整える業務」から、「事業課題を解決するための設計・計画全般」へと解放すること。 その決断が、いかにしてプロダクトの質と、チームの魂を変えていったのか。今、私たちが感じている「統合の真の価値」を言語化してみたいと思います。 なぜ統合が必要だったのか:「上流×一次情報×検証」が欠けると、協働は痩せていく 先に結論を言うと、統合は“仲良くするため”ではありません。 プロダクト開発に必要な情報と責任が、上流から検証まで一本で繋がる状態 をつくるためでした。 私たちが日々向き合うプロダクト開発は、不確実性の塊です。だからこそ、チームが手応えを感じる瞬間は、完成した成果物そのものよりも、 不確実性が下がった 意思決定が前に進んだ 手戻りや無駄が減った リリース後の反応が見えた ……といった「前に進んだ感覚」に宿ります。 逆に言えば、一次情報に触れられず、上流での仮説設計に関われず、リリース後の効果検証も回らない状態だと、仕事は“調整と吸収”に寄り、協働は痩せていく。 これは個人の能力の問題というより、フィードバック周期が長すぎる設計のサインでした。 だから私たちは、役割の境界線を越える前に、まず 「上流×一次情報×検証」を“チームの標準装備”にする方向へ舵を切りました。 デザインの再定義:それは「問題を解決するための設計」である かつて、デザイナーの仕事の主戦場は「画面の中(UI)」でした。 しかし今、私たちの組織ではその定義が劇的に変わりつつあります。 デザインとは、ユーザーの深層にある痛みを見つけ出し、それを解決するための「設計・計画」そのものです。 この定義の変化を象徴するのが、デザイナーによる「UX領域への染み出し」です。 デザイナーが自らユーザーインタビューの場を設計し、実査を主導する。そこで得た生々しい一次情報をもとに、PdMと肩を並べて「この機能は本当にユーザーのためになるのか?」という本質的な議論を戦わせる。 デザイナーが「体験の責任者」として上流から関わることで、プロダクトの骨格は以前よりもずっと強固になりました。単に「使いやすい画面」を作るのではなく、「なぜそれを作るのか」という問いに対して、デザインの観点から明確な解を持つようになったのです。 そして何より、ここで重要なのは“美しい理想論”ではなく、 顧客価値に直結する現実 です。 インタビューで課題設定がズレていることが分かり、手戻りを未然に防げた。あるいは、現場の声を根拠に提案が通り、商談上の不安材料を消せた。そういう小さな勝利の積み重ねが、プロダクトの勝率を静かに上げていきました。 壁を壊すだけでは足りない:一次情報・意思決定・効果検証を「仕組み」にする 統合して気づいたのは、組織図を変えただけでは、人は簡単に変わらないということです。 必要なのは「気合」ではなく、 情報と責任の流れを変える仕組み だと思っています。 私たちが意識して整えているのは、大きく3つです。 1) 一次情報を“取りに行く”のを、当たり前にする 顧客・営業・CS・現場の声は、伝言ゲームを経由した瞬間に痩せていきます。だからこそ、一次情報に触れることを「一部の職種の仕事」から、「チームの呼吸」へ。デザイナーもPdMも、必要なら自分で取りに行く。その姿勢をチームの文化として育てています。 2) 上流から入る(要求の背景と検討経緯を共有する) 「要求が遅い・曖昧」を開発側が吸収して納期を守る構造は、短期的には優しい。でも長期的には、品質・士気・優先順位付けを劣化させます。そこで、要求の背景(なぜ今これが必要なのか)や検討経緯(なぜこの案を選んだのか)を、できるだけチームに残す。意思決定の“証跡”を共有する。これだけで、同じ議論を何度も繰り返す無駄が減り、腹落ちの深さが変わっていきます。 3) リリース後の効果検証を“デフォルト”にする 作って終わりにしない。 KPIの推移だけでなく、VOCや現場の反応も含めて、どんな変化が起きたのかを回収し、次の意思決定に繋げる。 ここが回り始めると、チームは「前に進んだ感覚」を取り戻し、協働の質が一段上がります。(ここはまだまだできていないことが多い) プロセスの磨き込みが、思考の余白を生む(AIは黒子) もちろん、デザイナーが上流工程に染み出すためには、これまでの業務をどこかで効率化しなければなりません。そこで私たちは、制作プロセスに工夫を凝らそうとしています。 UI制作における細かなルーチンワークや共通パーツの管理、さらにはインタビュー後の膨大な文字起こしや情報の整理といった「作業」の部分に、最新のテクノロジーやAIを黒子として組み込もうとしています。 目的は、デザイナーを「作業」から解放し、その脳を「思考」へとシフトさせることです。 この実現のために「デザインガイドライン」の策定と浸透は重要な役割を担っています。(デザインガイドラインの苦労は以下のnoteをご覧ください) note.com note.com 浮いた時間で、デザイナーはユーザーの隣に座り、感情の機微を読み取る。そして、そのインサイトを戦略へと昇華させる。技術によって生み出された「余白」が、皮肉にも私たちの開発プロセスをより人間中心(ヒューマンセンタード)なものへと変えていければよいなと思います。 PdMとデザイナーの共鳴:信頼が戦略を加速させる この体制が生んだ最大の資産は、PdMとデザイナーの間に生まれた「深い信頼の土壌」です。 デザイナーがUXの解像度を究極まで高め、現場をリードしてくれる。その確信があるからこそ、PdMは現場の細部をデザイナーに「預ける」ことができるように少しずつなってきています。 これはPdMにとって、単なるタスクの委譲ではありません。UXの重責をパートナーに託したことで、PdMは自身の専門領域である「プロダクト戦略」や「GTM(市場投入戦略)」など、事業成長に直結するマクロな動きに全神経を注げるようにするためです。 この「相互の越境」は、目に見える実績としても現れ始めています。 複数サービス利用推進の加速:デザイナーが横断組織になったことで、サービス間の垣根を超えたデザインガイドラインが浸透 コミュニケーションの高速化:別組織ゆえの「合意形成のための儀式」が消え、価値を「ツクル→伝える」までのスピードが上がった ここで大事なのは、スピードそのものではなく、スピードが生む“余白”です。余白があるから、仮説検証ができる。一次情報に触れられる。効果検証まで責任を持てる。結果として、プロダクトが「説明できる意思」を持ちはじめます。 UX志向のガードレール:越境は「自由」ではなく「責任」 一方で、越境は万能薬でもありません。 境界線を溶かすほど、意思決定が曖昧になったり、声の大きい人が勝ったりする危険もあります。だからこそ、私たちは「UX志向」を 行動原則(ガードレール) として明文化し、何度もすり合わせました。たとえば、 製品の本質的価値から逆算して行動する 事実(ファクト)に基づき、仮説検証を繰り返す 提供したUXへのフィードバックを収集し、改善に繋げる 短期と中長期をORにせず、ANDで両立する道を探す ステークホルダーに敬意を払い、説明責任を果たす そして同時に、「やらないこと」も決めます。憶測だけで決めない。フィードバックを放置しない。政治や保身を優先しない。 この“当たり前”を言語化しておくことで、越境は「自由」ではなく「責任」として機能し始めます。 リアルな壁:未経験の荒野と「固定観念」との戦い(アンケートで見えた“本質”) しかし、理想ばかりを語るわけにはいきません。現在進行形の課題も山積みです。 まず、UXを深く担った経験のあるデザイナーはまだ少数派です。UIという安全圏を抜け出し、正解のない「戦略や設計」に踏み込むのは、想像以上に怖いし、体力も要ります。 ここで半期ごとに実施している「製品貢献実感」のアンケートが、壁の正体をはっきり映しました。貢献実感が強い瞬間は、成果物そのものよりも「前に進めた感覚」——認識齟齬を潰して手戻りを減らせた、意思決定が進んだ、リリース後の反応が見えた、といった場面に集まりやすい。逆に言うと、 一次情報に触れられない/上流に入れない/効果検証まで見えない 状態が続くほど、貢献実感は痩せていきます。これは個人の能力ではなく、フィードバック周期が長い(設計されていない)ことが原因になりがちです。 さらに厄介なのが固定観念です。「デザイナー=UIを作る人」「PdM=要望を捌く人」という見え方が残ると、一次情報や検証に踏み込もうとした瞬間に、善意の期待で引き戻されます——「いいから早く画面を」「まずは間に合わせて」。 だから私たちは、気合ではなく仕組みで解くことにしました。一次情報・意思決定の背景・効果検証を“標準装備”にし、小さくても「前に進んだ証拠」が返ってくるループを短く回す。固定観念は議論で倒すより、 実績で上書き するほうが早い。未経験の荒野と戦う鍵は、個人の頑張りではなく「前進が可視化される設計」にあると感じています。 おわりに:各自が「+1」の越境を止めない 私たちが目指すのは、職能というラベルに縛られない、真のプロフェッショナル集団です。 デザイナーはより戦略的に、PdMはより体験に寄り添う。それぞれが自分の領域から「+1」の越境を続けることで、プロダクトには強い「意志」が宿ります。 組織図の壁を壊し、デザインを解放した先に見える景色。それは、作り手が誰よりもプロダクトの可能性を信じ、楽しみながら価値を生み出し続ける、そんな組織の姿です。私たちの挑戦は、まだ始まったばかりです。 記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 プロダクトマネージャー career-recruit.rakus.co.jp デザイナー https://career-recruit.rakus.co.jp/engineer_jobs/uidesigner_tokyo/ career-recruit.rakus.co.jp

.jpg)





















