2025年9月7日日曜日

謎のエフェクトライブラリ emoji-particle を作った

謎のエフェクトライブラリ emoji-particle を作りました。 イメージ図のように、絵文字で花火を打ち上げたり、ポップコーンっぽいエフェクトを表示できるライブラリです。 柔軟なエフェクト設定ができるため様々な用途に応用できます。


Demo でおおまかな動作を確認できますし、 marmooo/emoji-particle から MIT ライセンスでコードを利用できます。

fontconv

前からずっと似たようなものは欲しかったのですが、 真面目に実装すると大変で放置していたのですが、 既存アプリが増えてきていよいよ欲しくなってきたので作りました。 Web Worker + OffscreenCanvas + 効率的な更新処理で実装しているので、 無駄に高速・高機能なライブラリとなっています。 内部の実装もかなり色々な最適化をしているので、大量に絵文字を飛ばしても重いと感じることはほぼないでしょう。

Worker を使ったライブラリを何も考えずに公開すると同一ドメインの制約があって使いにくいので、 インライン化することで簡単に使えるようにしています。 import { createWorker } from "emoji-particle"; するだけで使えるようになっています。 Worker 系のライブラリだとあるあるなんでしょうが、ビルドはみんなどうしてるんでしょう。 私はもちろん esbuild でオレオレビルドです。

こういったエフェクトは、良いものを作ろうとすると画像が必要ですが、 たくさん画像を使い始めると取り扱いが急に面倒になります。 RPG ツクールのホコグラっぽく管理すれば多少は…とは思うのですが、そういうことを考えるのも嫌です。 画像を扱い出すとと、ファイルサイズも大きくなるし、画像ファイルの管理も面倒だし、 ライブラリのロードも面倒になります。 絵文字はどんな環境でも使えるカラフルな画像集なので、 このようなときに最も扱いやすいです。

シンプルな割にはあらゆる場面で使えて便利なライブラリだと思っています。 これまで作ったアプリたちにもこれから組み込んでいきます。 それっぽいエフェクトを付けたい時には最も重宝するライブラリになりそう。

2025年8月26日火曜日

頻出順の英和辞書 mGSL を更新した

頻出順の英和辞書 mGSL を更新しました。 以前開発した mGSL は頻出順データ自体は最強でしたが、 既存の辞書を利用しているため和訳の粒度にバラツキがあり、英和辞書としては不安定でした。

mGSL

そこで生成 AI を活用して簡易的な和訳を作り、最強の和英辞書に作り変えました。 生成 AI を使って詳細な辞書を作るのはかなり苦しいと思いますが、 英単語学習では典型的な意味表現のみを知っていれば十分です。 なるべく覚える言葉自体が少なくなるように和訳の粒度を調整した辞書として mGSL を更新しました。 このような用途では生成 AI による文章生成が極めて有効です。 とはいえ既存の辞書データでさえ 3万語あったので、すぐにはできません。 AI の生成は頻繁に壊れるので完全には自動化できないため、 サボれる時間を見つけてちまちまと文章生成を繰り返すことで構築しました。

まずは既存の辞書に登録されていた 3万語まで和訳を付けました。 しかし既存の辞書のデータは頻度データをきちんと考慮していないので、 実際には頻度7000語くらいまでしか安定して登録されていません。 それ以降は歯抜けが多いので、ちまちまと翻訳を作っていく必要があります。 lemmatization されたデータは 6万件あります。 厳密な頻度で 3万語に到達する頃には、3万5000語くらいになってそうです。 これはさすがに果てしない…ということで、頻度 1万語までは完璧なものにしてこの記事を書きました。 それ以上はニーズの低さからやる気があまり出ないですが、ぼちぼちやりたいところ。 1万語あれば海外の大学生くらいの語彙数になるので、たいていのニーズは満たせるでしょう…。

生成された訳は目視で気になる点はチェックしており、 機械的に処理できるようにフォーマットを整えたりしています。 訳が不安定なところも微修正しています。 ドイツ語やイタリア語など、英語以外の語彙も含まれているので、英語学習には不向きなものも多々あります。 これらは機械的に除外できるようにしました。 AI さんに頼んでもすぐ崩壊するのでこのへんは手動で直すしかありません。 手動は手間ですが、和訳を作る部分が一番時間が掛かるので、90% は時間を削減できているでしょう。 それでも結構な時間は掛かりました。単調すぎて眠いのが地味に厳しかった。 ただその甲斐もあってか、以前は見つからなかったアラも多少修正できました。 依存ライブラリをがっつり減らすことができて、だいぶ気楽になりました。

英単語学習の辞書データとしては、より詳細なものを作る以外だと、 これ以上のものを作るのは難しいんじゃないかな。 あるとしたら lemmatization をちょっと改良できるくらい。これはいずれ検証したい。 あとはいよいよ接尾辞や現在分詞、過去分詞をもっと考慮して語彙数を減らしていくほうが、辞書としては質が高くなるのかもなあ。

Vocabee など既存の英単語アプリ、 graded-enja-corpus などの派生ライブラリには反映済みですが、 新規アプリも今後作っていきます。 やはり和訳の粒度を調整できているのはあまりにも大きい。 以前と変わらず CC-BY-SA で使えるので、使いたい人はどうぞ。

2025年7月6日日曜日

圧倒的インフレゲームの億千万タイピングを作った

圧倒的インフレゲームの億千万タイピングを作りました。 タイピングは色々なものを作っていますが、タイピングを最も学ぶのは小3〜小4なので、 その年代でより面白いものが作れればと前々から思っていました。 という訳で作ったのがこれで、学習指導要領に合わせて万・億・兆などの桁を学びながらタイピングができます。 さらに京・垓・秭・穣・・・無量大数まで対応しているので、圧倒的インフレのタイピングを楽しめます。



タイピングとしてはすこし難しいけど、面白いかもなという感じです。 私は小さな頃、家に転がっていた参考書を読んでいたら無量大数まで桁があることを知ったのですが、 今の子はどうやって覚えるんでしょう。兆の先を知る機会ってあんまりないんじゃないかな。 こんな感じの神ゲー(笑) で遊んでみるとすぐに覚えられます。

いわゆるインフレゲーはたくさんありますが、無量大数がすぐに出てくるゲームはなかったと思う。 ゲームの終盤になってやっと出てくるのが限度でしょう。学習用途で使うのはなかなか難しい。 その点、億千万タイピングはスタート時点から無量大数に親しめるので、効率的に勉強できます。

億の桁を超えると、ローマ字を表示しきれなくなるのでどうするかで悩みましたが、 表示しきれないものは見えないようにしてしまって、 桁が変わるごとにテキストを削減することで対応しました。 何回か遊べば、その後は違和感なくプレイできると思います。

神ゲーというかネタゲーですが、たまにはこういうのも良いと思う。 無量大数の得点をゲットできるので、友達と争うのにいいかも。 打鍵速度が十分に早くなってくると引き運ゲー感があるけど、そこはまあ仕方ない。

2025年6月15日日曜日

シームレスなエフェクトを適用するアプリ CV-Masker を作った

シームレスなエフェクトを適用するアプリ CV-Masker を作りました。 マスクを手書きで設定して、そのマスクに対してグラデーションをかけつつシームレスなエフェクトを適用できます。



色々なエフェクトを用意しているので画像はあくまでイメージですが、こんな感じの画像をサクッと作れます。



最初は cv.colorChange, cv.illuminationChange, textureFlattening などの シームレスなエフェクトを与える関数の確認のために作っていたのですが、 いくつかボツ案があったので、マスクにエフェクトを掛けて遊ぶアプリに変えました。

当初の目的のシームレスなエフェクトの関数は、良い感じの画像を作れるのですが、 へぼい CPU だと処理速度がちょっと遅かったです。 コードを見ると SIMD + Threads の最適化は甘そうなので、将来の改善に期待です。 やはり OpenCV と言えども、コア部分以外は遅い時もあるとわかりました。 あと textureFlattening は実行するたびに結果が異なるので元の実装がバグってると思う。

OpenCV の cv.colorChange などの関数は高度なシームレス処理を行っていますが、 たいていそこまで精度は必要ありません。 そこで自作の軽量な汎用局所シームレス関数を作って遊べるアプリにしました。 0/255 で書いたマスクを boxFilter でグラデーション化して、 グラデーション化したマスクに任意のエフェクトを掛け合わせます。 処理は非常に軽いですが、十分なシームレス感があります。 どんなエフェクトでもシームレスに適用できますが、 一般的かつシンプルなエフェクトをいくつか利用できるようにしておきました。 局所モザイク、局所シャープ化、局所色調補正などが利用できます。 いざ作り始めたら追加したいエフェクトが多すぎて困ってきましたが、 応用的なエフェクトは別のアプリで作ります。 アルゴリズム多すぎのものはすぐに組み込むのが難しいです。

シームレスなエフェクトを加える関数は、メモリリークが直らず苦労しました。 やはり C++ は行数が増えてくると、ハマったときになかなか厳しい。 結局少し前のバージョンでは直らず、すべて書き直したら直りました。 メモリリークそのものは input event で大量に処理させるとすぐに見つかります。 たぶん MatVector の扱いが一番難しいのですが、 push_back() はコピーらしいので、すぐに delete() するのが綺麗だと思う。 参照がどうなってるか熟知してないとできないのが厳しい。
const resultVec = new cv.MatVector();
for (let i = 0; i < 4; i++) {
  const ch = srcChannels.get(i);
  resultVec.push_back(ch);
  ch.delete();
}
手軽にいい感じのエフェクトが作れるので、結構使いやすい気がします。

2025年5月12日月曜日

様々な非写実的レンダリングを適用するアプリ CV-NPR を作った

様々な非写実的レンダリング (Non-Photorealistic Rendering) を実現するアプリ CV-NPR を作りました。 最近は生成 AI で画像がすごく簡単に作れるようになっているので、そちらとは技術がズレている感はありますが、 最近 Web 上で手軽に使えるようになった技術を地道にアプリ化しています。



色々なエフェクトを用意しているので画像はあくまでイメージですが、こんな感じの画像をサクッと作れます。



このアプリでは OpenCV の cv.detailEnhance, cv.edgePreservingFilter, cv.pencilSketch, cv.stylization、cv.oilPainting などの面白エフェクトが利用できます。 上記はそれなりに有名でいろいろな記事がネットでも見つかりますが、もっと色々なエフェクトが欲しかったので、 モザイク、色鉛筆化、cv.applyColorMap, cv.anisotropicDiffusion などをさらにサポートしました。 色鉛筆化は Lineart Converter を作ったときにできた副産物です。 cv::anisotropicDiffusion はうまく使うと迷路画像や、味のあるスムージングができます。 たいして設定項目がなく使えるエフェクトはこれくらいでした。 他に何かあるかな?

GIMP や Photoshop で使うようなエフェクトが Web 上で実現できれば、割と便利かもなと個人的には思っています。 とはいえまだまだエフェクトが足りないのが現状です。 ただあまり時間は掛けたくないし、今回は OpenCV だけを使って実現できることを実装しています。 OpenCV を使えば 1日で根幹部分は作れるので…。 なんらか実装が必要なものは、他のアプリで作る予定です。

実装はしないように心掛けたアプリなので、正直このアプリでは開発期間の大半はビルド時間だったりします。 公式にはまだサポートされていないように見える機能を色々使っているので、チェックするたびにビルド時間が掛かりました。 色々なモジュールを触り始めたことによってビルドが苦痛でした。 wasm を作るだけで 1時間近く掛かります。 またアプリごと・ビルド種類にビルド用のディレクトリを持ってキャッシュすると 1GB 以上容量がいるし、 キャッシュしてもオプションを少し変えただけで無意味化する問題などがあります。 ESM 並の tree shaking ができるなら依存関係をモリモリにしてビルドできるのですが、 依存関係をモリモリにすると不要な定数を大量に登録される問題もあります。 このままアプリを増やしていくとビルドだけで 1日掛かりそうな気がしたので、 依存関係を真面目に処理して簡単にビルドできるスクリプトを作りました。 ビルド時間が 1/100 になるので本家にも反映してほしい機能ですが、 デフォルトの Python 設定ファイルだとモジュールの情報がなくてできません。 OpenCV には裏コマンドとして JSON 形式の設定ファイルがあるのですが、そちらならできます。 というか Python 設定ファイル、JavaScript で言うところの eval 使っていて、 危ないしやめたほうが良い気がするけどなあ。 本体の改良もできるようになってきて、OpenCV のこともちょっとわかってきた気がします。

2025年4月23日水曜日

画像の部分修正アプリ Inpainter を作った

画像の部分修正アプリ Inpainter を作りました。 名前の通り Inpaint アルゴリズムを使っています (安直)。 OpenCV と opencv.js の勉強、AI を使わないアルゴリズムの性能確認のために作りました。



不要オブジェクトを削除したこんな画像がサクッと作れます。 注意点としては、分布を調整するだけのアルゴリズムなので、消しゴムマジックのようには使えません。 消しゴムマジックのアプリだと思って使うと、ただの雑コラになるでしょう。



画像にちょっとしたノイズが走っているときに、それをいい感じに消してくれるアプリと思えば、なかなかの精度です。 ただアルゴリズム的には周囲の分布を見ながら消すので、周囲の分布が安定していないとうまく行かない訳です。 周囲の分布をどれくらい考慮するかは radius パラメータで調整できる訳ですが、 普通に考えれば周囲の分布が安定しているかどうかのほうがよほど重要だとわかります。

とはいえ分布ガチャをうまく引けば良い訳ですから、消したい箇所を何度か指定しながら分布を安定化させると、割といい感じの画像になります。 機械学習を用いた場合は分布を既存の知識を使って綺麗にごまかすイメージですが、inpaint アルゴリズムはガチャで分布を綺麗にしてごまかすものだと開き直ると、割と使いやすい気はします。

inpaint のアルゴリズムは cv::photo と cv::xphoto に実装されていますが、 cv::xphoto の inpaint はまだ Wasm ビルドができない感じです。 ちょろっと定義を変えれば動く気はするんですが、こういうときなかなかツライ。 動くようになったら追加予定です。

2025年3月19日水曜日

画像の背景を削除する GrabCutter を作った

画像の背景を削除する GrabCutter を作りました。 名前の通り GrabCut アルゴリズムを使っています (安直)。 前景と後景をアノテーションできるようにしておいたので、 削除と復元の微調整しやすいのが利点です。



こんな感じの背景透過画像がサクッと作れます。



割と大きめの画像でも 初回実行は 1秒以内、微調整は一瞬という感じです。 巨大な画像に適用すると abort するのが課題そうです。 ROI を作って部分適用したほうが良いかも知れません。 他にも共有メモリで分散処理はどんなアルゴリズムでも検討したいところですが、今回はそこまで作ってないです。 AI を使わない時にどれくらい精度が出るのかの勉強用で作りましたが、 AI なしでも割と良い精度です。せいぜい 2-3回の微調整で十分な結果が得られるので、 これはこれでアリじゃないかなあ。

グラフカットの試行回数がパラメータで設定できるのですが、まったく必要ない気がします。 試行回数が増えても遅くなるだけなので、1回で固定して微調整で修正したほうが良いです。 alphamat をさらに考慮する方法などもあるっぽいことに後から気付きましたが、 ものすごい時間が掛かるみたいなので、現状はこれでいいかなと。