Photoruction工事中!

Photoructionの開発ブログです!

カスタマーサポートのお仕事と醍醐味

こんにちは!

フォトラクションでカスタマーサポートを担当している勝木(かつき)と申します。

いつもは弊社のTS部の(弊社ではTechnology Service部=通称:TS部と呼んでいます)の皆さんが記事を投稿していますが、

今回は趣旨を変えて、弊社が開発、提供しているフォトラクションというサービスが実際にどうやって使われているのか??

僕を含めカスタマーサポートというポジションがどういった仕事に取り組んでいるのか?

という部分を中心にお話しできたらなと思っています!

カスタマーサポートって何している人たち??

カスタマーサポート(以下、長いので「サポート」と略します)って何?というところから簡単に紹介しますと、

読んで字のごとく、「お客様を支援する」ことが僕たちサポートのお仕事です。

よく電話対応する人たちでしょ?と思われがちですが、

そしてそれもまったくもってお仕事の一つではありますがざっくりとこんな感じです↓

  • 電話やメールでのフォトラクション利用に関しての問い合わせ対応
  • ヘルプページやマニュアルなどの作成
  • 導入初期のユーザーに対しての運用方法のすり合わせと操作説明会の実施
  • 導入後の運用支援とモニタリング
  • ユーザーからの要望の集約と社内展開

こんな感じで、サービスの活用を促すポジションがフォトラクションのサポートの業務となってまして、

そのためユーザーと日々接する機会が非常に多いポジションになっています。

サポートとしての醍醐味

建設現場では、実際の工事を行うだけでなく、その工事過程の写真で記録をしたり、報告書として書類にまとめたり、設計図と実際の現場を見比べて相違がないかをチェックしたり、工程を管理したりなど、現場に従事されている方は日々一つの現場でさまざまな業務をする必要があります。

フォトラクションというサービスはそんな建設業のお仕事を支援するためのツールです。

僕たちサポートは前述したように、フォトラクションを利用いただいているユーザーの方々から操作に関するお問い合わせや、操作説明の場でユーザーと接することが多いのですが、

フォトラクションを利用いただくなかで、

  • 工事写真の撮影から書類の作成までの作業時間が半分以下になった
  • 現場の情報共有が格段に楽になった
  • フォトラクションを見ながら会議ができるようになって紙いらず

と嬉しいお声をいただくことがあります。

これらのお声はしっかりと価値を提供できている何よりの証ですし、ユーザーとの距離が一番近いサポートだからこそ受け取れる声だと思っています。

詳しくはフォトラクションの導入事例をみていただけると、実際に利用されている方々のお声が掲載されていますのでご興味ある方はぜひ!

こうした嬉しいお声をいただくと同時に「もっとこうして欲しい」「こういった機能があればもっと楽になるのに」だったりとサービスへのフィードバックや機能要望をいただくことも増えていきます。

こういったお声もサービスをより良いものへと進化させていくのに必要なものなので、

一つ一つの意見を集約して、社内に展開しサービスの改善に反映されていくのもサポートの醍醐味だなあと日々の仕事のなかで感じます。

ユーザーからの声を受け止め、TS含め社内へしっかりと声を届けサービスを強化していく。

このサイクルを続けることで建設業全体に大きなインパクトを与えるものとなっていくと信じて、僕たちは今日もユーザーの一番近くでサポートを続けていくのでした。

株式会社フォトラクションでは一緒に働く仲間を募集しています

建設業特化のBPO機能について

こんにちは!株式会社フォトラクションでプロダクトマネージャーをしています南風原はえばる)です。

Photoruction Advent Calendar 2022の22日目の記事になります。

はじめに

Webエンジニアからプロダクトマネージャーに転身して早4ヶ月…

開発者だった時以上にプロダクトに向き合い、プロダクトの成長を目指して日々チーム一丸となってプロダクト開発に取り組んでいます。

私が主に携わっているプロダクトである「BPO機能」は、今年10月頃に新規リリースした機能になります。

今回は、たまに夢に出てくるくらい愛してやまない「BPO機能」について紹介していこうと思います😘

BPO機能とは?

BPO機能とは、工事の事前準備や、書類作成、データ入力などをサポートをするアウトソーシングサービスである「建設BPO」を画面上から簡単に利用できる機能です。

工事現場からノンコア業務を解放し、生産性向上を実現するとともに、生産データの蓄積を促進を目指しています。

また、Next SaaSである「BPaaS」の考え方に基づいて開発しており、SaaS利用以上の生産性向上を可能とするサービスとなっています。

BPaaSに関してはCEO中島さんが詳しく説明してるのでぜひ読んでみてください!

note.com

BPO機能の具体的な機能説明をする前に、建設BPOをどういったタイミングで利用するのかを施工管理業務の一つである「配筋検査」業務を例に説明していこうと思います。

建設BPOを利用しない従来の配筋検査業務のフローとしては以下のような手順で業務が進んでいきます。

  1. 図面準備
    1. 配筋検査を行う対象物件の図面を用意する
  2. 検査準備
    1. 工事写真の撮影時に用いる黒板の作成
    2. 図面上の検査を行う箇所にピンを設置(検査項目を事前に登録したりする)
  3. 検査
    1. 図面と現場を比較し検査していく
    2. 検査結果を記録していく
  4. 写真撮影
    1. 黒板を用いて工事写真を撮っていく
  5. 台帳化
    1. 検査結果や工事写真をまとめた台帳を作成し関係者各位に報告する

建設BPOを利用すると、「検査準備」をまるっと当社で対応することができるため工事の事前準備をする手間が省け、コアの業務に集中することができるメリットがあります。

また建設BPOは、当社で開発しているaoz cloudという建設業界特化型AIとオペレーターをかけ合わせて低コスト・高スピード・高品質で必要なデータを作成し納品しています。

建設BPOは以前から提供しているBPOサービスでしたが、BPO機能がリリースされる前は直接ユーザーとメールのやり取りをして運用していました。

ただ、以前の運用では以下のような課題がありました。

  • メニューの標準化がされず拡販がしにくい状態
  • BPOの利用状況が見えにくい状態

今回、建設業向けのクラウドサービスPhotoructionから、建設BPOを簡単に利用することができるBPO機能をリリースしたことにより、今後建設BPOを多くのユーザーに利用していただくことが可能になったのと、建設業の生産性向上により一層貢献していける可能性が広がりました!

BPO機能でできること

現時点でのBPO機能でできることを簡単に紹介していきます。

主にできることとしては以下のとおりです。

  • BPOサービスの見積・発注
  • 利用状況の確認
    • 見積金額の確認
    • 納品データの確認など

BPO機能で利用できるサービスメニューの一覧・詳細を確認することができます。

利用したいサービスメニューの見積依頼と利用状況の確認ができます。

また、ユーザー向けの画面とは別で社内メンバーが利用する管理画面の開発も行っています。

社内向け管理画面の詳細については内緒ということで🤫

技術スタックについて

最後に、BPO機能の開発で用いている技術スタックの紹介になります。

新規でゼロから開発していることもあり、割とモダンな開発環境なのかなと思います。

まとめ

簡単にですがBPO機能の紹介をさせていただきました!

少しでも興味持っていただけましたでしょうか??

今後もより一層ユーザーに価値提供していくためにいろいろ作りたい機能があります。

今年から新しく提供している機能でもあるため、プロダクトとしてもまだまだ未熟です。

ぜひ、この記事を読んで興味を持っていただいた方や、新規プロダクトの開発、BPaaSの開発をしてみたい方がいらっしゃいましたらぜひご応募もしくはカジュアルにお話しましょう〜!!!🤝

株式会社フォトラクションでは一緒に働く仲間を募集しています

JavaScriptでアニメーションを行う

こんにちは!株式会社フォトラクションウェブエンジニアのジョンです!

今回はウェブアニメーションAPIの簡単な使い方を紹介したいと思います!

ウェブアニメーションAPIとは

ウェブアニメーションのドラフトを初めて作成されたのは2013年の6月で決して新しい機能ではありません。基本的にはウェブアニメーションAPICSSアニメーションをJavaScriptにコントロールするためのAPIです。

アニメーションをJavaScriptにコントロールさせることでいくつかのメリットがありますが個人的に一番重要なのはアニメーションの開始と終了時のわかりやすさです。

普通はCSSでアニメーションを行う時にアニメーションの間隔と合わせてJavaScriptの処理を遅らせることが多いです。ここで普段はsetTimeoutがよく使われています。

ブラウザーサポート

ウェブアニメーションは新しい機能ではないため、ほとんどの最新版ブラウザーにはすでに対応されているはずです。

でも、なんとなくブラウザーウェブアニメーションを対応していない場合はPolyfillを使ってウェブアニメーションを使うことができます。

animateファンクション

ウェブアニメーションの一番簡単な使い方は.animate()ファンクションを使うことです。.animate()ファンクションは2つの引数を受け取っています。

第一引数

第一引数にはキーフレームを指定します。値はオブジェクトかオブジェクトの配列かになります。オブジェクトの内容はCSSと同じプロパーティとなります。フォーマットはキャメルケースになります。

// 方法1
const keyframes = {
    backgroundColor: ['red', 'blue'], // 'red'は開始値で'blue'は終了値になります
        left: [0, '500px'] // '0'は開始値で'500px'は終了値になります
};

// 方法2
const keyframes = [
        {
            backgroundColor: 'red', // 'red'は開始値
                left: 0 // '0'は開始値
        },
        {
            backgroundColor: 'blue', // blue'は終了値
                left: '500px' // '500px'は終了値
        }
];

第二引数

第二引数にはアニメーションのオプションを指定します。値はミリ秒単位でのアニメーションの再生時間となる数字またはKeyframeEffect()のオプションの引数になります。

// 方法1
document.querySelector('#box').animate(keyframes, 1000); // アニメーションは1秒走ります

// 方法2
const options = {
    delay: 300, // アニメーションを遅らせる(ミリ秒単位)
        duration: 1000, // アニメーションの再生時間(ミリ秒単位)
        easing: 'ease', // アニメーションの[イージング](https://ics.media/entry/18730/)(動きの加減速)
    fill: 'forwards' // アニメーション終了した際にリセットしない
};
document.querySelector('#box').animate(keyframes, options);

合わせますと

上記を合わせますと下記の通りにCSSなしでもアニメーションを動かせます。

Animationオブジェクト

.animate()ファンクションは[Animation](https://developer.mozilla.org/ja/docs/Web/API/Animation)オブジェクトを返しています。Animationオブジェクトには3つイベントがあります。

  • cancel - [Animation.cancel()](https://developer.mozilla.org/ja/docs/Web/API/Animation/cancel)メソッドが呼び出されるか、アニメーションの再生状態が他の状態から "idle"へ遷移した場合に発行されます。
  • finish - アニメーションの再生が終了した時に発行されます。
  • remove - アニメーションが取り除かれた時 (すなわち、 active置換状態に遷移した時)に発行されます。

finish イベントを待つことでsetTimeoutを使わずにアニメーションが終わったとたん処理を実行することができます。

最後に

ウェブアニメーションAPIにはこちらにカバーできない機能が結構あります。もっとアドバンスなアニメーションなどもできますが、ほとんどのUIアニメーションなら上記のやり方でカバーできています。ウェブアニメーションAPIに関してもっと知りたいのであれば、この記事を書いているところに参照したサイトや記事などを書きに提供いたしますので是非読んでみてください。

References

PyMuPDFでPDFを操作する⓶

こんにちは!株式会社Photoructionでエンジニアとしてインターンをしている渡邉圭太郎です。 Photoruction Advent Calendar 202220日目の記事です。

はじめに

自分たちのチームでは、PDFを扱って画像解析を行うことが多いです。以前まではpdfminerを使用していましたが、日本語ドキュメントの豊富さなどから現在はPyMuPDFというライブラリを使用しています。本記事はPyMuPDFを使ったPDFのベクターファイルの取得方法を紹介します。

PDFのベクターファイルとは

PDFのファイル形式には大きわけてラスターファイルとベクターファイルの二つが存在します。それぞれの特徴としては以下の通りです。

ラスターファイル

ラスターファイルとは、色のついた小さい正方形であるピクセル(画素)を大量に組み合わせた画像で、写真などの高精細な画像を形成できます。ピクセル(画素)数が多いほど高画質になり、少ないほど低画質になります。画像のピクセル数は、ファイル形式によって異なります。(https://www.adobe.com/jp/creativecloud/file-types/image/comparison/raster-vs-vector.htmlから引用)

ベクターファイル

ベクターファイルは、数式、直線、曲線を使用して、グリッド上の固定点により画像を表示します。ベクターファイルにはピクセルはありません。ベクターファイルは、数式によってシェイプ、境界線、塗りの色を表現し、画像を構築します。 ベクター画像はサイズが変わっても数式により再計算できるため、品質に影響を及ぼすことなく拡大縮小できます。(https://www.adobe.com/jp/creativecloud/file-types/image/comparison/raster-vs-vector.htmlから引用)

要するにベクターファイルは矩形や文字情報が画像としてではなく格納されています。これはPyMuPDFを使用すると簡単に取得できるのです。

PDFの読み込み

PyMuPDFの読み込みについては、11日目の酒井さんの記事をぜひ参考にしてください!

図形の取得

# PDFファイルパスの読み込み
pdf_path = input()

# 1ページ目の要素を取得
document = fitz.open(pdf_path)
page = document[0]

# 図形の取得
paths = page.get_cdrawings()
line_list, rect_list, quad_list, curve_list = [], [], [], []
for path in paths:
    items = path['items']
      for item in items:
          if item[0] == 'l':
              line_list.append([item[1], item[2]])
              # pdfにlineを追加する場合
              # page.draw_line(line[0], line[1], color=(1, 0, 0))
          elif item[0] == 're':
              rect_list.append(item[1])
          elif item[0] == 'qu':
              quad_list.append(item[1])
                    elif item[0] == 'c':
                            curve_list.append(item[1])

テキストの取得

# PDFファイルパスの読み込み
pdf_path = input()

# 1ページ目の要素を取得
document = fitz.open(pdf_path)
page = document[0]

# テキストの取得
words = page.get_text_words()

text_info = []
coordinates_list = []
for w in words:
    x1, y1, x2, y2 = w[:4]
    x1, y1 = fitz.Point(x1, y1) * page.rotation_matrix
    x2, y2 = fitz.Point(x2, y2) * page.rotation_matrix
    x1, y1, x2, y2 = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)
    if x1 == x2:
        x1 -= 0.1
        x2 += 0.1
    if y1 == y2:
        y1 -= 0.1
        y2 += 0.1
  
    text_info.append({'text': w[4], 'coordinates': [x1, y1, x2, y2]})

おわりに

PyMuPDFでは他にもPDFを編集したりする操作も可能となっています。もし気になる方はチェックしてみてください!

株式会社フォトラクションでは一緒に働く仲間を募集しています

Datadogを触ってみた

はじめに

  • Photoructionでエンジニアをしております。北原です。

前提

  • 環境は、EC2です

目的

  • そろそろ弊社も監視しないとやばいよね?という流れからsentryかdatadogどちらにするかってなりdatadogになりました

以下個人的な考え

  • できる範囲で言うとsentryでできることはdatadogでも大概できます。(大概ね大概)
    • じゃ最初からdatadog一択じゃんてなるかもしれませんがまぁ目的として何を監視したいかで変わると思います。
    • アプリケーションモニタリング&エラートラッキングだけでいいやってなるならsentryだけで問題なく、またメトリクスはawsを使用しているならaws wayで行くぜ!でもいいと思います
    • datadogはできることが膨大にあります。お金もたくさんかかります。あくまで手段なので何をどのように把握したいのかas is / to beなので理想状態(目的)が何かをちゃんと把握してから手段として正しいか判断した上で導入することをお勧めします

環境

  • 前提にも書きましたがEC2です。
    • 詳細
      • PHP/Laravelを使用しているのでこれらの設定をしていく

やったこと

datadogの作業開始前に

  • EC2をコマンド操作できる状態にしておいてください
  • datadogにLoginしてください。
    • 14日間は、無料で試せるのでアカウントをお持ちじゃない方はぜひにtryしてみてはいかがでしょうか?

まずは下記ページに遷移します(Integrations > agent > Amazon Linux)

SaaSはよくUIが変わったりしてどこに何があったっけとなるのでこちらの画像は、あくまで参考程度にとどめてください

  • 遷移しましたら画像にも映っていますSelect API Keyをクリックして今回使用するAPI Keyを選択しましょう

①に書いてあります Use our easy one-step install. の箇所の下記コマンドをコピペしましょ

DD_API_KEY="ここに選択したAPI keyが入ります" DD_SITE="datadoghq.com" bash -c "$(curl -L <https://s3.amazonaws.com/dd-agent/scripts/install_script_agent7.sh>)"

EC2上でコマンドを実行しますとinstallが始まりますので終わるまで待機です。

設定fileを書いていきます

  • こちらもdatadogにLoginしている状態ですと基本的には、何を用意すればいいのか書かれてます。
    • Logs > Get started > server > phpにページ遷移しましょう
    • ここに書かれている内容をそのままEC2に内に写経するだけ終わっちゃいます
  • datadog-agent以下にあるdatadog-agent.yamlの該当箇所のコメントをコメントインする
logs_enabled: true
  • これでdatadogは、loggingしてくれるようになります。phpに限ったことではないですが例えばnginxのlogも見たいんよなーって要望があった際にもここをtrueにしておけばあとは、各middleware用の設定fileをよしなにするだけですね
  • laravel.logをdatadogに送ってあげましょ
    • datadogは、 /etc/datadog-agent/conf.d/***.d/以下に設定されます。***の箇所は、middlewareの名称で存在しています
      • 設定file名は、conf.yamlで書くといいでしょう。(多分)
    • datadogは、Laravel用のディレクトリを用意してくれているわけではありませんので今回は/etc/datadog-agent/conf.d/php.d/conf.yamlとしてディレクトリとファイルを作成しましょう

作成したら以下のように記述します

logs:
  - type: file
    path: "/path/to/laravel.log"
    service: php
    source: php
    sourcecategory: sourcecode
        log_processing_rules:
       - type: multi_line
         name: new_log_start_with_date
         pattern: \\[\\d{4}\\-(0?[1-9]|1[012])\\-(0?[1-9]|[12][0-9]|3[01])
  • 上記のように設定してあげればひとまずは問題ないです
    • type: multi_line 以下に関して
      • これを書かないとスタックトレースやら何やらが複数行にわたってdatadog上で表示されて全くもって見づらい状態になりますので書いた方がいいかなと思います。

動作確認

  • 設定fileを書き終えたらdatadog-agentを再起動します。
sudo systemctl restart datadog-agent
  • 再起動終わりましたら、datadogのlogsで確認することが可能になります。
    • 補足しておきますとLaravelのlogの設定次第で表示される内容が異なりますから、スタックトレース見たいなーって思う方は、意図的に発生させてみてください。

参照

株式会社フォトラクションでは一緒に働く仲間を募集しています

ゆるラム始めました

Photoruction Advent Calender 2022の18日目の記事です!

こんにちは!CREチームリーダーの田中です。

前回、スクラムマスターとしての半年間の経験の備忘録を書きました。

今回はその後半に当たる記事です。

突然ですが、「ゆるラム」という言葉はご存知でしょうか?

ま、知るわけはないですよね。僕が勝手に作った造語ですから。。

経緯としては、どうせスクラムの勉強をしたのだから、

スクラム開発を行わないチームに、スクラムのエッセンスを取り入れられないかな〜と思い立ちました。

そして、ガッツリしたスクラムフレームワークの文脈には添えない状況で、いかに、

スクラムの良いとこどりをするか、「ゆるラム」を真剣に考えてみようと思います!

前提の違いを考える

まず、スクラム開発の基本的な考えをおさらいしてみましょう。

スクラムは、不確実性をヘッジしていくのが基本思想。

タスクの割り込みが発生する場合は、基本的には次回以降のスプリント(期間内に対応するタスク)に組み込まれます。

安定した進捗を出すためです。(ただ、勿論緊急度が高いタスクが発生した場合は、他のタスクを次回のスプリントに回す形で、スプリント内で対応するようにします。)

一方、弊社のCREチームだと、不具合の一時切り分けがメインタスクになります。

カスタマーサポートのチーム顧客から不具合の報告を受け、CREチームに原因究明を依頼します。

当然、顧客に対して不具合に関しての説明が求められますので、それまで取り組んでいるタスクよりも優先度を高めて、一時調査

をし→原因突き止めたら、報告。そして、対応の優先順位を決め、改修。

こんなフローになっています。

スクラム開発に比べたら、割り込みタスクを翌週翌々週に調整していくことが難しいのが現状かなと思います。

つまり差分は

  • スクラムチーム:不確実性をいかに増やさず、減らすか
  • CREチーム:不確実性が増えていく可能性がある中で、いかに減らすか

を考えていかねばなりません。

表現として、矛盾してるような気もしますが、それでも向き合っていかないといけません笑

スクラム開発から転用できそうな考え方

  • こまめなコミュニケーションを取り合えるようにする
  • 検査→適応できる(要するにPDCAを回せる)
  • スコープを区切るのは大事

転用できるイベント

  • デイリースクラム(進捗の共有、悩みの解消、コミュニケーションの促進)
  • レビュー(検査や知財共有)
  • レトロスペクティブ(振り返りの場づくり)

逆に転用できないこと

  • プランニング(後述の通り、差し込み計画を立てても意味がない。だって予測できないから)
  • リファインメント(仕様の検討をメインで行わないため)

具体的なアクション

  • デイリースクラム
    • 週一の定例→デイリーを毎日に変更
    • 細かい改善ができるようにということと、コミュニケーション量増進が目的
  • レトロスペクティブ
    • 隔週でKPTを設ける
    • 短期的なタスクの追われる中でも、中長期的な視野を持って日々の業務に取り組むことを忘れないために重要
  • レビュー
    • 日々対応した不具合隔週でレビュー
      • それを題材に仕様理解、コード理解を深める
    • レビューというより勉強会の意味合いが強い

まとめ

以上が、スクラムチームのスクラムマスターから、

CREリーダー就任2ヶ月ほどで着手している「ゆるラム」施策です。

上記に限らず、良さげなイベントに関しては、積極的に取り組んでいこうと思います

(アドカレ1日目の南風原さんのこの記事とかとか)

CREはもぐら叩きのようなもん

出てきたもぐらをすぐ叩くのもそうだし、

次に出てきた時に、すぐボコボコにできるように、可視化できる状態にしなければならない

結局大事なこと

ゴールとしては、

  • いかに早く、調査対応やデリバリーをするか
  • チームとしての生産性を高めること
  • 学びのサイクルを構築すること

になるかなと

まだ、試行錯誤の段階ですが、こんな感じで、「ゆるラム」を進めていきます!!

P.S.

ここまで書いてやっと気づいたけど、スクラムの語源から考えると、

「ゆるラム」ってめっちゃ弱そうよね。。

CameraXのmlkit-visionを使ってみた

はじめに

Photoructionでは工事現場の写真撮影をメイン機能としてさまざまな機能があり、その写真撮影機能では現在Camera2を使ってカメラの制御をしています。

昨今では、Camera2の代わりにCameraXがstableで出始め、Camera2と比べ出来ることにもメリットデメリットがありますが、そろそろPhotoructionでもCameraXへの移行を進めようと考えています。

通常の写真撮影とは別の機能として、QRコードリーダーを実装する案件があったため、これを気にQRコードリーダーをCameraXで実装することになりました。

実装はcameraXの1.1.0をベースに実装しましたが、本記事執筆段階では1.2.0がstableとなり、1.3.0はalphaが出始めています。

今回は1.2.0で追加されるmlkit-visionへの置き換えを見ていこうと思います。

結果

今回1.1.0から1.2.0になることでMlKitAnalyzerを利用でき、そこにscannerをセットするだけでMlKitを使えるのでとても簡単になったと感じました。

ただし、1.1.0以前で既にImageAnalysis.Analyzerを作成済みで、特にそこのロジックをいじる予定もない場合は1.2.0への移行を急ぐ必要はないのかなと感じました。

それよりもCameraControllerを使用することで1.1.0でもカメラ周りの設定がより簡潔に行えることがわかりました。

準備

以下のライブラリ設定が現在です。

// camerax
implementation 'androidx.camera:camera-core:1.1.0'
implementation 'androidx.camera:camera-camera2:1.1.0'
implementation 'androidx.camera:camera-lifecycle:1.1.0'
implementation 'androidx.camera:camera-view:1.1.0'
// mlkit barcode scanning
implementation("com.google.mlkit:barcode-scanning:17.0.2")

まずbuild.gradleのcameraxライブラリのアップデートを行ない、mlkit-visionを追加します。公式ドキュメントはこちらです。

// camerax
implementation 'androidx.camera:camera-core:1.2.0'
implementation 'androidx.camera:camera-camera2:1.2.0'
implementation 'androidx.camera:camera-lifecycle:1.2.0'
implementation 'androidx.camera:camera-view:1.2.0'
implementation 'androidx.camera:camera-mlkit-vision:1.2.0-beta02'
// mlkit barcode scanning
implementation("com.google.mlkit:barcode-scanning:17.0.2")

camerax系は全て1.2.0にします。 mlkit-visionのみ1.2.0-beta02までしかでていません。 barcode-scanningはmlkit-visionがあっても必要です。

cameraX1.1.0の実装

startCamera()

プレビュー画面の設定からバーコード解析の設定、カメラの起動を行います。

private var cameraProvider: ProcessCameraProvider? = null
private fun startCamera() {
    val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
    val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
    if (cameraProvider != null) {
        cameraProvider?.unbindAll()
    }
    cameraProvider = cameraProviderFuture.get()
    val surfaceProvider = binding.previewView.surfaceProvider
    val preview = Preview.Builder().build().apply {
        setSurfaceProvider(surfaceProvider)
    }
    val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
    val imageAnalysis = ImageAnalysis.Builder().build().apply {
        setAnalyzer(cameraExecutor, BarcodeAnalyzer())
    }
    cameraProviderFuture.addListener(
        {
            cameraProvider?.unbindAll()
            cameraProvider?.bindToLifecycle(
                viewLifecycleOwner,
                cameraSelector,
                preview,
                imageAnalysis
            )
        },
        mainThreadExecutor()
    )
}

ImageAnalysis.Analyzer

このクラスでバーコード解析を行います。

バーコード以外のMLKitも同じようにAnalyzerを実装するだけで、解析ができます。

一度に読み込んだバーコードをList\<Barcode>で取得し、Barcodeクラスにはフォーマットやデータ、PreviewView上のバーコードの位置情報などさまざまな情報が入っています。

今回は取得したBarcode情報をOverlayViewなどのカスタムビューへの出力の実装はしていませんが、実装してあげることでプレビュー上で認識したバーコードに被せて枠を表示したりできます。

class BarcodeAnalyzer :
    ImageAnalysis.Analyzer {
    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
            BarcodeScanning.getClient().process(image)
                .addOnSuccessListener { barcodes ->
                    barcodes.forEach { it.format }
                    // バーコード情報の受け取り
                    // barcodesからデータを取り出して使用
                }
                .addOnCompleteListener {
                    imageProxy.close()
                }
        }
    }
}

cameraX1.2.0(mlkit-vision:1.2.0-beta02)

startCamera()

1.2.0(mlkit-vision:1.2.0-beta02)ではCameraControllerを使用し、mlkit-visionのMLKitAnalyzerを使用することで簡潔に実装できます。

なお、CameraControllerは以前から使用できます。

private var barcodeScanner: BarcodeScanner? = null
private lateinit var cameraController: LifecycleCameraController
private fun startCamera() {
    cameraController = LifecycleCameraController(requireContext())
    cameraController.cameraSelector = DEFAULT_BACK_CAMERA
    cameraController.bindToLifecycle(viewLifecycleOwner)
    binding.previewView.controller = cameraController
    barcodeScanner?.close()
    barcodeScanner = BarcodeScanning.getClient()
    cameraController.clearImageAnalysisAnalyzer()
    cameraController.setImageAnalysisAnalyzer(
        mainThreadExecutor(),
        MlKitAnalyzer(
            listOf(barcodeScanner),
            CameraController.COORDINATE_SYSTEM_VIEW_REFERENCED,
            mainThreadExecutor()
        ) { result ->
            // バーコード情報の受け取り
            val barcodes = result.getValue(barcodeScanner!!)
            // barcodesからデータを取り出して使用
        })
}

MlKitAnalyzerにlist型でbarcodeScannerを渡しているので、他のScannerを複数セットし、結果のresultのgetValueでそれぞれのScannerを元にデータを取得することができ、より短い記述で複数のMlKitを利用できるようです。

余談

ちなみに1.1.0で使用しているBarcodeAnalyzerはImageAnalysis.Analyzerであり、1.2.0-beta02で使用しているMlKitAnalyzerもImageAnalysis.Analyzerなので、 1.1.0でもCameraControllerを使用することで以下のようにstartCameraを実装できるようです。

private lateinit var cameraController: LifecycleCameraController
private fun startCamera() {
    cameraController = LifecycleCameraController(requireContext())
    cameraController.cameraSelector = DEFAULT_BACK_CAMERA
    cameraController.bindToLifecycle(viewLifecycleOwner)
    binding.previewView.controller = cameraController
    cameraController.clearImageAnalysisAnalyzer()
    cameraController.setImageAnalysisAnalyzer(
        mainThreadExecutor(),
        BarcodeAnalyzer()
    )
}

最後に

CameraXは1.0.0から1.1.0になったタイミングでPreviewViewが追加され、プレビュー画面の生成やTextureViewでの実装の場合onSurfaceTextureAvailableなどを考慮しなくて済むようになったため、更にカメラ機能の実装が容易になりました。

ただ、Camera2と比べ超高解像度が扱えないなどまだ不足している点はあるようなので、今後1.3.0やそれ以上になると更にカメラを扱いやすい機能が追加されることに期待したいです。