暗い格子の背景に、青から紫の Futoji と「Scan sheet music into one book」。スマホが楽譜を読み取り、矢印の先で手書きの入った楽譜が1冊に重なる

音楽教室の楽譜・プリントをスマホでスキャンして1冊にするアプリ「Futoji」を出した

Kojiro Tanaka

紙の楽譜を、スマホにかざす。四隅が出て、斜めでも正面の形に直して、1冊のブックに入る。五線が反って写っていれば、線を手がかりに縦の曲がりも伸ばす。手書きは画像に焼かない。PDF にすると、線はベクトルのまま載る。名前は Futoji。譜を綴じる、から。いまの版は v1.0.2、ビルド番号 3002。Flutter 3.47、Dart 3.13、Material 3。MIT。

リポジトリは kjranyone/futoji。配ってある実行ファイルは GitHub Releases の Android APK。ストアに出した、とはリポジトリに書いてない。

で、おにーさん。レッスンのプリント、まだカメラロールの IMG_4388 で寝てない? お昼の写真と3ページ目が同じ列。よっわ♡。フォルダ、作った顔だけして。あははっ。

Futoji のホーム。生成した楽譜の表紙が2列のブックになって並び、右下に追加の丸ボタン

画面の楽譜は、training_data/paper_photos/ の生成写真。ストア用の見本ブックに入れて撮った。実写の教師データには市販楽譜が写るので、掲載画像には使わない、と docs/RELEASE.md にある。カメラと切り抜きの掲載画像は、実機の UI に生成写真を差し込んだ合成。ホームと手書きは、その見本ブックを実機で開いた画面。

綴じるのは、紙

プレビューのあいだ、常駐 isolate が約 150ms ごとに四隅を出して、枠をライブで描く。自動撮影は、紙の面積が正規化座標で 18% 以上、角の移動が 1.2% 以内、それが 3 回続いたらカウントダウン。画面の数字は 3、2、1。待ちは 1500ms。保存する範囲は、そのカウントダウン中に出ていた枠。v1.0.1 のコミットが、そう直してある。撮ったあとは 3 秒のクールダウン。新しい紙とみなすのは、移動量 0.15 が 2 回続いたとき。紙が一度も画面から外れないまま 3 回を超えて撮ろうとしたら、自動は自分で止まる。README の実装メモは「8% 以上動くと再武装」のまま。コードの rearmMovement は 0.15。メモのほうが古い。

手動のシャッターは切り抜き画面。四隅はドラッグ。再検出と回転もある。横持ちだと、センサー向きで保存が 90 度回ることがある、と README。縦持ちでは通常出ない。直すボタンは切り抜きの「回転」。

台形は 4 点の対応からホモグラフィを解いて、逆射影とバイリニア補間。斜めから撮ると奥行きが潰れるので、辺の長さの平均では縦横比を決めない。透視から元の長方形の比を推定する。コードのコメントは Zhang & He 2006。焦点距離は JPEG の EXIF にある 35mm 換算。無ければ 28mm として計算する。推定した比が A 判・B 判の √2 から 5% 以内なら、√2 ちょうどにスナップする。解けなければ、辺の長さのまま。

デコードの長辺上限は 2600px。純 Dart で 4160×3120 を全部開くと、Lenovo TB-J716F で約 11 秒だった、と fast_decode.dart。端末のデコーダで縮小しながら開く。補正後の長辺は outputSizeForQuad の既定 2400px。WebP の本体は品質 84。サムネイルは長辺 420px、品質 80。減色はカメラのパレットで、プロセスのあいだだけのフラグ。Hive には書いてない。既定はオフ。彩度の割合が 0.04 未満なら 8 色、その上が 16 色、カラフルなら 32 色。ユニーク色が 32 以下なら、その数のまま残す。README の表は 8 と 32 だけ。

「AI でくっきり」も既定オフ。Real-ESRGAN の realesr-animevideov3(BSD-3)を int8 にした ONNX で、半分に縮小して 4 倍に戻し、元の大きさへ平均する。ファイルは assets/models/sr_anime_x4v3_int8_t256.onnx、681,972 バイト。調査メモの書き方は 666KB。HiPad(MT6785)で 1794×2288 の 1 ページが、カメラを止めた状態で 9.3 秒。設定画面の文言は「10 秒ほど」。1/4 まで縮小すると速い。小さい文字が別の字形に崩れる。「23:00」が「2S:00」になった、と docs/IMAGE_RECOGNITION_RESEARCH.md の 6c。QR みたいな細かい図は迷路に描き直す。楽譜と文字は、1/2 縮小なら崩れない、とそこにある。汎用版は音符の周りに白い縁が出るので、線画向けの 16 層を採った。

五線が定規

台形補正は、平面の傾きしか直さない。本のノドで紙が反ると、線が弧になる。楽譜には「五線は本来まっすぐで、等間隔」がある。それを定規にした。学習モデルは載せてない。DocTr++ も UVDoc も DewarpNet も、調査メモは不採用。学習データ Doc3D の利用条件と、重みのライセンス確認が要る。それに、文字の文書で学習されたモデルだ、と書いてある。

増やすのは誰でもできる。載せるモデルを剃った。オッカム、とか口にした瞬間に自分で笑った。やってることは、五線を櫛で拾って、縦にずらしてるだけ。

純 Dart の staff_dewarp.dart。幅 1200px の補正済みグレースケールで、縦の短冊ごとに傾きを振って横投影する。「間隔 sp で 5 本そろって、間と外側が空いている」櫛を五線にする。スラーや連桁は櫛にならない。表の罫線は外側が空いてないので、大半は落ちる。変位は D(x, y)。x は 6 次、y は 2 次。補正後の画素は、補正前の (x, y+D) から取る。推定は 2 回。補正し直して曲がりが 2 割以上減らなければ、その回は捨てる。五線が 5 本未満、変位がページ高さの 12% 超、みたいな安全策で怪しいときは何もしない。五線の無いページは、台形補正だけで保存する。既定は ON。カメラ右上と、設定 → 画質。

横方向の縮みは、扱わない。ノドの近くで文字が横に潰れる分は、残る。

評価は eval_hard の楽譜。Dart 移植後のハーネスで、実写 13 枚は全部補正して曲がり 22.5px から 1.2px。合成のノド反り 26 枚のうち 24 枚を補正して、28.1px から 1.6px。幅 1200px での中央値。見送った 2 枚は安全策で元のまま。壊してはいない、とメモ。

速さは、分けようとして遅くなった。短冊の走査を isolate に 2 本、4 本と割ると、実機(Cortex-A76 が 2、A55 が 6。HiPad の MT6785 はこの形)では 1 本 約 0.75 秒、2 本 約 1.1 秒、4 本 約 1.4 秒。2MB の画像を渡すコピーが、検出の回数ぶん乗る。既定は 1 本。そのあと計算量を削って、同じ実機の eval_hard 13 枚で、曲がり推定を含む保存全体の中央値が 2.10 秒から 1.71 秒。この 13 枚の元写真は 750×1000 と小さい。実寸に近い別の計測(調査メモは出力 約 2600×1950)では、WebP のエンコード自体が約 0.6〜0.7 秒。Android の Bitmap.compress(WEBP) は method もスレッドも触れない、と書いてある。いまの保存長辺の既定は 2400px なので、この秒数を今の出力サイズの実測だとは読まない。

386k で、紙を塗る

検出の本体は自前の TinyUNet。学習スクリプトのコメントは、約 386k パラメータ、fp32 で約 1.5MB。README も ONNX 1.5MB。リポジトリに入っている assets/models/paper_seg.onnx は 4,359,359 バイト。入力も出力も 1×1×256×256 の float32。1.5MB は設計メモの数字で、今のファイルはそれより大きい。大きい理由は、リポジトリが説明してない。

推論は dart:ffi で ONNX Runtime の C API。flutter_onnxruntime の Java/JNI は、リリースビルドで落ちるので使ってない。ネイティブの .so は onnxruntime-android を gradle 依存で同梱。iOS 側に書いてあるのは、CocoaPods の onnxruntime-c 1.20.0 を静的リンクして、AppDelegate.swift が API のアドレスを MethodChannel futoji/ort で渡す経路。モデルが読めなければ、ライブ検出のワーカーは大津の二値化へ戻る。明るい紙、暗い背景、が前提。白い机の上の白い紙は、自動が失敗する。そのときは全面が初期選択で、四隅を手で動かす。

学習は合成(ホモグラフィ、照明ムラ、白背景、影、擬似文字)と、生成写真 200 枚。擬似マスクは BiRefNet(MIT)。実データは 180 枚を学習、20 枚を検証、合成と 50:50。README の精度は、合成検証 IoU 0.926、実 200 枚の対教師で中央値 0.906。重なりと強い折れは苦手、と書いてある。

採用してあるのは調査メモの v4。辺を直線で当てはめたあと、実写・横の角誤差中央値 0.76%、IoU 0.956。難例 13 枚の IoU は 0.936、成功@5% は 100%。eval_web(アプリの撮り方以外の Web 画像、紙あり 5 枚)はクリーン成功 0。ネガティブ 3 枚は 3 枚とも拒否。汎化してない、と自分で書いてある。v5 は eval_web を少し救って、実写・横の角が 0.76% から 1.70% に落ちたので未採用。v7 から v13 のハードネガティブループでは、合成の誤検出 0% に届いた v12 がある。実写の正例を 1 枚ずつ落とすので、v4 のまま。v14 も、保存してある合成を混ぜたら実写が落ちて未採用。同じ机に戻ってる。

ニーチェなら、これに永劫とか名前を付けたがる。……名前は置かない。戻したのは、実写の角が太らなかった v4。候補を厚くするたびに、アプリの机から離れた。それだけ。

eval_web の正解は、画像を見た推定で ±2〜3%。細かい増減より、全滅と数枚、の差を見る用。

training_data/ は git に入ってない。サイクルは docs/DATASET.md。アプリの開発者向けで切り抜きを保存し、./dev.ps1 pull、直した四隅だけ promote、train、eval。eval_* は学習に戻さない。2026-10 に、eval_heldout の 10 枚が学習側の real_captures/ とバイト一致していて、隔離した、と書いてある。

線は、画像に焼かない

手書きはストローク。色は黒 0xFF212121、赤 0xFFE53935、青 0xFF1E88E5。太さは細 0.004、中 0.007、太 0.012。座標は正規化なので、ズームしても同じ位置。既定のペンは赤の中。消しゴム、元に戻す、表示のオンオフ。黒い線には白い縁を足して、暗い紙の上でも見える。画像を再圧縮しても、線はベクトルのまま。PDF は pdf(Apache-2.0)で全ページを 1 冊にして、手書きもベクトルで載せる。渡すのは OS の共有シート。share_plus は BSD-3。

楽譜の全画面ビューア。五線の上に赤と青の手書き、右下に黒・赤・青のペンと太さ

検索が当たるのは、ブックのタイトルとタグとページメモ。空白区切りの AND で、大文字小文字は無視。一致した理由をカードに出す。音取りのメモはページメモに書く、と調査メモ。手書きの線は、文字認識の対象に入ってない。文字認識そのものは未実装。設計だけが調査メモの 6b にある。PaddleOCR v4 mobile を ONNX にして、保存したページのテキストを検索に足す、というパス。ML Kit は導入が楽だが SDK が OSS じゃないので、MIT を守るなら非採用、と書いてある。音符の認識(OMR)はスコープの外。MusicXML の表示も再生も、無い。

ページの順番は、ブックのメニュー「ページを並び替え」でドラッグ。タイルの長押しは前後へ 1 枚。README の機能表は長押しのほうだけ書いて、末尾の「今後」にはドラッグ&ドロップがまだアイデアとして残ってる。画面には、もうある。

音の練習のほうは、バンド練習のパイプライン に置いてある。あっちは曲を手元で分けて、クラウドに上げない。こっちは紙を手元で綴じて、撮影画像を外に送らない。机の上では隣り合う。中身は混ぜない。混ぜた瞬間、譜面のアプリが拍を取り始める。取ってない。

落とすなら、Releases

配ってある APK は GitHub Releases の最新版。/releases/latest は、いま v1.0.2 のタグへ転送される。冒頭に書いたタグと、同じリリースに着く。ABI は arm64-v8a、armeabi-v7a、x86_64。スマホのカメラでこの記事を開くなら、QR の中身も同じ URL。

GitHub Releases の最新版(https://github.com/kjranyone/futoji/releases/latest)を開くQRコード

おにーさん、タブ、またどこかに埋めたでしょ。QR なら指一本。レンズ、撮る前に拭きな。

入れるだけなら、APK で足りる。ソースから置く人は、この下。

Android に置いて、紙をかざす

要るのは Flutter SDK と、Android の実機。カメラの話を、エミュレータでは書いてない。README の手元は C:\lib\flutter。PATH が通っていれば、場所はそこじゃなくていい。状態は provider の ChangeNotifier。ブックとページのメタデータは hive_ce。画像はアプリ領域の futoji/images/ と futoji/thumbs/、ページ ID の WebP。

flutter pub get
flutter analyze
flutter test
flutter build apk --release

flutter test の評価ハーネスは、training_data/eval_hard が無いとスキップする。リリース APK の README 上の目安は、全 ABI で約 55MB、ABI 分割で約 20MB。GitHub に付いている v1.0.2 の実ファイルはそれより大きい。arm64-v8a が 43,733,114 バイト、armeabi-v7a が 36,391,660 バイト、x86_64 が 48,009,333 バイト。CI は .github/workflows/android.yml。main への push で解析・テストと APK、v* タグで Release に添付。署名の Secrets が無い環境では使い捨て鍵になり、上書き更新はできない。

この APK は --dart-define=FUTOJI_DIST=github 付き。起動時に 1 日 1 回、それと設定の「アップデートを確認」で、api.github.com の最新リリースを見る。送るのはその通信の分で、撮影画像もブックも送らない。ストア提出用の appbundle と iOS には、この定義を付けない。ストア版は通信しない、が プライバシーポリシー の書き方。Play の公開後は、Play が別の鍵で再署名するので、GitHub の APK とは上書きし合えない、と RELEASE.md。公開した日付は、リポジトリに無い。

実機で通した記録は 2026-09-24。Lenovo TB-J716F、Android 13、arm64。起動、撮影、自動検出、台形補正、WebP、グリッド、ビューア、赤ペン、消しゴムの部分消去、1600×1200 のフレームを約 150ms でライブ検出。TB-J716F は、联想のユーザーガイド だと小新 Pad Pro 2021 の型番。このサイトでは そのタブレットのレビュー を 2021 年に書いてある。あれは買った直後の機体の話。曲がり補正の秒数と「AI でくっきり」の 9.3 秒は、別の機体の計測。HiPad、MT6785。バックアップの書き出しは 2026-10-04、同じ HiPad で Download 経由を確認。その端末に Drive アプリが無くて、Google ドライブ保存は未確認。

設定 → バックアップは、全部のブックを zip に出す。Android は保存先を選ぶか共有シート。iOS の分岐は共有シートだけ、と settings_screen.dart。同じブックは zip の中身で置き換え、手元にしか無いブックは残す。アンインストールすればアプリ領域は消える。Android の OS バックアップが有効なら、OS の仕組みでアプリデータが運ばれることがある、とプライバシーポリシー。

権限はカメラ。パッケージ名は com.kjranyone.futoji。開発者向け(切り抜きの保存、自己診断)は既定オフ。オンにしても、外へは送らない。

iOS は、2026-10-04 時点で未検証、と RELEASE.md。実装は Windows 上でやって、Mac でのビルドも実機も見てない。確認項目は pod install、flutter build ios --release、起動ログに PaperNet: model unavailable が出ないこと、カメラの説明文。NSCameraUsageDescription は Info.plist に入ってる。動いた、とはどこにも書いてない。未検証。

ツールチェーンの記録(2026-09)は AGP 9.3.3、Gradle 9.5.0、compileSdk 37。permission_handler_android 14 が 37 を要求した、と README。

v1.0.0 は 2026-10-10。v1.0.1(2026-10-15)は、カウントダウン中の枠を保存範囲にしたのと、透視からの縦横比。v1.0.2(2026-10-18)は、GitHub 版の更新確認、削除確認に消えるページ数を出す、「教材」という仮の名前を「ブック」に揃えた。Issues は、開いているものも閉じたものも、リポジトリ上は空。

入ってないものは、入ってない

README の末尾に、今後のアイデアが 3 つ残ってる。ギャラリーからの取り込み。ページ並べ替えのドラッグ&ドロップ。自動連写(ページめくり検出)。2 番目は、上に書いたとおり画面にはある。1 番目と 3 番目は、コードに無い。カメラロールの写真を開く経路は、バックアップ zip を選ぶ file_picker だけで、画像のピッカーは pubspec に無い。ページをめくったことを見て連写する機能も無い。いまの自動撮影は、紙が止まるのを見てる。

調査メモの「次」も、未実装のまま書いてある。切り抜き画面に辺の中点ハンドルを足して、手動で曲線を直す(Coons パッチ)。五線の検出を、半ページめくりに流用する。どちらも「できる」と書いてあるだけで、画面には無い。

簡易 OCR も OMR も、上のとおり無い。モデルが読めないときの大津は、ある。曲がり補正を学習モデルに差し替える話は、不採用の記録があるだけ。

vFlat のほうが、本を平らにする

本をスマホでスキャンするアプリとして、名前が出るのは vFlat。2026-10-19 に公式を見た範囲で、向こうにあるもので、こっちに無いものを書く。

曲面。vFlat は、開いた本の 3D の曲率を 1 枚の写真から推定して、専用の台もガラスも無しで平面にする、と書いてある。指は消す。見開きは左右 2 ページに割る。自動撮影もある。Futoji の曲がり補正は、五線が 5 本以上あるページの、縦の変位だけ。横の縮みは未対応。押さえた指は、写ったまま残る。見開きを 2 ページに割る機能は無い。五線の無いプリントは、台形まで。

文字。vFlat の OCR は 100 以上の言語で、英語、韓国語、日本語、中国語を含む、と公式。ページ内の語句を検索できる。無料プランは、PDF 書き出しとテキスト認識がそれぞれ月 10 ページ、見開きモードと自動認識モードがそれぞれ月 5 回。プレミアムで OCR は月 3000 ページ、とそこにある。最新の数字はアプリ内、とも書いてある。Futoji の文字認識は未実装。検索できるのは、自分で打ったタイトルとタグとページメモ。

場所。vFlat は iPhone と iPad と Android。公式は 3,000 万ダウンロード超、と書いている。撮影、フチ、平面化、指の除去は端末内。OCR と「PC に送る」を自分で実行したときだけ、その画像を暗号化して送り、処理後 24 時間以内に消す。学習には使わない、と公式。Futoji の実機確認は Android。iOS は未検証。ストア版は通信しない設計で、GitHub の APK だけが更新確認で GitHub の API を叩く。画像は送らない。OCR が無いので、文字認識のために送る経路も無い。無いものを、隠してるとは呼ばない。

Adobe Scan も、端を見つけて傾きを直し、OCR で検索できる PDF にする、と Adobe が書いている。保存先は Adobe のクラウド、と Android の手順。2026 年 2 月から 4 月の Android 版リリースノートでは、四辺が全部写っていなくても曲面補正できる、と Straighten の追記 がある。Futoji は四隅が検出できなければ、手で角を置く。写真ライブラリから取り込む機能は、Adobe の説明にある。ギャラリー取り込みは、アイデアの行のまま。

楽譜を「読む」側まで行くなら、forScore。iPhone、iPad、Mac、Apple Vision、と App Store の説明。注釈はレイヤー、記号のスタンプ、テキスト。セットリストで曲をまたいでページをめくる。Bluetooth のページターナー、メトロノーム、チューナー、ピアノ、とそこにある。Futoji のペンは 3 色、太さ 3 段、レイヤーは 1 枚。再生も、拍も、ページターナーも無い。forScore は、持ってきた PDF を読むアプリ。Futoji は、その PDF を紙から作る側で止まってる。

広いスキャナが欲しいなら、vFlat か Adobe Scan。譜面台のアプリが欲しいなら、forScore。Android の手元で、教室の紙をブックにして、五線の反りだけ縦に伸ばして、外に画像を出さないなら、Futoji が残る。速度の表は作らない。測った機体が違う。

ソクラテスをここに出すと、知らない自分を飾れる。飾りは今ので終わり。残ってるのは、検証してない iPhone と、開かないカメラロールと、読めないページの中の文字。

白い机で撮った顔、見えてる

冒頭で、かざせば 1 冊になる、と書いた。なるのは、暗い背景の上の紙を、Android のカメラで、アプリの中から撮ったとき。

白い机に白い紙を置くと、自動検出は失敗する。全面が選択になって、角は手。かざしただけで終わる、は、机の色が条件だった。……むきー! 机が白い。iPhone は、検証した日が無い。カメラロールは、開く関数が無い。

既に撮ってある写真は、開かない。ギャラリー取り込みは README の今後。今後、と書いた行を、入ってる機能みたいに読んだのは、読んだ側。謝りは半分まで舌に乗って、机の色にすり替えた。未実装は未実装。経路が無い。

文字で探せない。タイトルは、自分で打つ。打たないブックは「無題のブック」。v1.0.2 で、昔の仮名「無題の教材」は読み込み時にそっちへ寄る。検索は、打った文字列しか見ない。

暗い机に置く。Android で、アプリから撮る。名前は打つ。Mac を持ってるなら、RELEASE.md の iOS のチェックを埋めなさい。埋めないなら、iPhone で動く顔をしないこと。感想は リポジトリ へ持ってきなさい。未検証の行は、あたしが先に書いてある。

シェア

関連記事

新着記事

← 記事一覧へ