marmooo's blog
フィード
画像を SVG に変換する image2svg を作った
marmooo's blog
以前作った @marmooo/imagetracer のフロントエンドアプリとして image2svg を作りました。image2svg の名前で利用したかったので分離しただけです。image2svgたとえばこんな感じの変換ができます。デフォルトで使いやすいようにオプションは設定していますが、画像サイズに合わせて多少の調整は必要な気がします。画像サイズが小さくなると lineTolerance/splineTolerance が大きくなったときに穴ができてしまうので、閾値を小さくすると良いです。といって小さくしすぎると圧縮率が下がるので strokeWidth で調整すると良いというのがだいたいのイメージです。しかし改めてオプションを弄ってみても、ほとんど出力が変わらないケースが多いです。それだけ洗練されたとも言えるのですが、なかなか難しい。
18日前
画像を SVG に変換する @marmooo/imagetracer を作った
marmooo's blog
画像を SVG に変換する @marmooo/imagetracer を作りました。他の有名なツールとしては vtracer, potrace, SVGcode, imagetracerjs があります。これは imagetracerjs の改良・高速化版です。OpenCV の限界次に dilate で詳細な輪郭を抽出して 2値化し、RETR_TREE の findContours して、輪郭ごとに平均色を算出する方法を考えました。しかしこれは処理時間の大半を平均色の算出に持っていかれて、ボツとなりました。findContours の処理で生成される内部配列を使えれば、平均色の算出は理論上はかなり早くできるのですが、現実では使えないので駄目でした。輪郭数は数万程度には抑えられる利点はありましたが、自作しないと早いものは厳しそうでした。詰まるところ、findContours の実装などに色々と課題があるのですよね。OpenCV はデファクトでしょうが、こういう問題は他にも結構あると思っています。imagetracerjs の改良imagetracerjs を見つけました。ラスター画像をベクター画像に変換することを bitmap tracing ということを、いまさら知りました。普通は image2svg だと思うじゃん…。先に述べたライブラリもその後見つかったのですが、imagetracerjs は他より高速・高精度に動作するように見えます。ただいかんせん実装が古くて使いにくいことが気になりました。そこで、(1) 一部の不要な実装を削除して API を簡素化し、(2) 減色と blur を外部化して汎用性を持たせ、(3) Deno で使いやすいように ESM 化し、(4) テストをたくさん書いて完璧な移植をし、(5) ベンチマークをたくさん書いて高速化し、(6) 減色処理をライブラリ化して複数選択可能にし、(7) 生成 SVG を minify して出力するようにしたものを、@marmooo/imagetracer として公開しました。オプションの改善layering 廃止mergePaths 追加pathomit/linefilter 廃止 → filterHoles 追加TODO にも書いてあった課題です。issue #15 などでも報告されていました。ただ no
21日前
様々な言語で作った Wasm をベンチマークした (2)
marmooo's blog
以前作ったお手軽ベンチマークを高度化してベンチマークの種類を増やしました。減色処理では色のカウントアップの後に色のリストアップを行うのですが、そのリストアップ処理までのベンチマークです。前回 (countColors) は色のカウントアップ、そのリストアップをして返す getColors、リストアップした結果は返却せず内部に保持する initColors の 3種類にしました。getColors は動的配列は使わなくても実装できるのですが、面倒なのでアルゴリズム上の変更はしません。クラスや構造体や動的配列に対応していない言語、明らかに遅いとわかっている言語は実装しませんでした。実装は @marmooo/wasm-bench にあります。getColors ベンチマークの追加 CPU | Intel(R) Core(TM) i5-6200U CPU @ 2.30GHzRuntime | Deno 1.46.3 (x86_64-unknown-linux-gnu)benchmark time/iter (avg) iter/s (min … max) p75 p99 p995------------------------------------------- ----------------------------- --------------------- --------------------------JavaScript, Deno 1.46.3 192.0 ms 5.2 (189.7 ms … 201.7 ms) 192.0 ms 201.7 ms 201.7 msAssemblyScript 0.27.30 (Number) 286.1 ms 3.5 (233.6 ms … 322.7 ms) 307.5 ms 322.7 ms 322.7 msAssemblyScript 0.27.30 (Class) 319.3 ms 3.1 (279.6 ms … 370.7 ms) 334.2 ms 370.7 ms 370.7 msRust 1.81.0, wasm-bindgen 0.2.93 (Simple) 2.2 s 0.5 ( 2.2 s … 2.2 s) 2.2 s 2.2 s 2.2 sRust 1.81.0, wasm-bindgen 0.2
1ヶ月前
様々な言語で作った Wasm をベンチマークした (1)
marmooo's blog
Wasm は様々な言語から作ることができますが、その実行速度が気になったので調査しました。計測に使ったのは減色処理をする時に必要な、画像内にある色のカウントアップ処理です。実用的です。WasmGC を使うとまた特性が変わるような気もするのですが、ひとまず現状チェックです。実装は @marmooo/wasm-bench にあります。独断と偏見により AssemblyScript, C/C++, Rust を調査しました。本当は Go も確認はしていて、int 処理くらいならできたのですが、Uint8Array を引数として渡すことができなくて諦めました。最近は TinyGo がかなり小さな Wasm を生成できるようになっていて (70KB〜)、速度が出るなら十分候補になるような気がします。しかし Uint8Array などの型変換がまったくわからない…。ベンチマーク結果 CPU | Intel(R) Core(TM) i5-6200U CPU @ 2.30GHzRuntime | Deno 1.46.3 (x86_64-unknown-linux-gnu)benchmark time/iter (avg) iter/s (min … max) p75 p99 p995----------------------------------- ----------------------------- --------------------- --------------------------JavaScript, Deno 1.46.3 166.8 ms 6.0 (164.1 ms … 171.8 ms) 169.6 ms 171.8 ms 171.8 msAssemblyScript 0.27.29 (Wrap) 153.8 ms 6.5 (150.1 ms … 155.0 ms) 154.4 ms 155.0 ms 155.0 msAssemblyScript 0.27.29 (Shift) 175.2 ms 5.7 (174.1 ms … 175.8 ms) 175.4 ms 175.8 ms 175.8 msAssemblyScript 0.27.29 (DataView) 156.5 ms 6.4 (156.0 ms … 157.8 ms) 156.5 m
2ヶ月前
画像減色ツール Color Reducer を作った
marmooo's blog
減色ツール Color Reducer を作りました。Color Reducerたとえばこんな感じの減色ができます。 特にひねりのないツールですが、D&D やクリップボードからコピー (Ctrl+V) など、結構使いやすいようには作っているので、普段使いのツールとして重宝します。opencv.js を使うと手軽に高速な実装ができるかなと思って作り始めたのですが、今なら porffor に期待したほうがビルドサイズを抑えられて良いかも知れません。減色アルゴリズムは 均等量子化、k-means、Median cut、Octree などがあるらしいです。意外と少ない。良い感じのソフトで使っているのは Median cut が多いようです。Median cut は細分化量子化法 (Tapered quantization) の一種で他にも色々あるようですが、最初に雑に考えて作ったものは均等量子化 (uniform quantization) でした。Median cut も最適化すると結構早いできる限り高速化してみると、たいていの画像は細分化量子化法と遜色ないレベルで動作しました。Median cut や Octree の JS 実装としては鬼のように早いと思う。JavaScript もきちんと書けばできる子です。一番の気付きは、RGB ずつカウント処理するより同時に Uint32Array で処理するほうが圧倒的に早いということです。ただ RGB の平均色を求めようとしたりソートするときは変換コストが高いのか Array のほうが早かったりします。難しい。結構色々な高速化を試したのでインパクトが大きかったものをまとめると以下です。 String Map → int Map (x3)int Map → 2^24 Array (x2)2^24 Array → 2^24 Uint32Array (x2)Array.sort() → bucket sort (x1.5)Object → Array (+30%)色数のキャッシュ (+30%)一番難しいと思ったのは、要素数が少ない時には通常の配列のほうが TypedArray の数倍くらい早いので、小さなデータは多少のコストを支払ってでも通常の配列にしたほうが早いということです。たとえば Uint32Array に入っている RGB
3ヶ月前
画像を線画に変換する Lineart Converter を作った
marmooo's blog
AIを使わないで写真を線画へ変換するアプリ Lineart Converter を公開しました。絵柄が変わることなく高速・省メモリに動作します。線画以外も生成できます。素材生成にご利用ください。Lineart Converter何も設定しないでもこれくらいの変換はできます。設定するともっと色々できます。 最近の AI は高性能なのでみんなが色々なことをやっていますが、私はメインマシンのメモリが 4GB なので GPU ゴリゴリの話はあんまりなあと思っています。ただ AI を使って色々やっている人のを見ていて、画像から線画を作るくらいなら OpenCV で十分じゃないのと思ったので、お手軽な線画変換ツールを作ってみました。色塗りで遊ぶくらいならこれで十分そうです。アルゴリズムCanny計算過程を可視化した良いエントリがありました。線画に適したものは初期段階で十分抽出できていますが、途中で大量に drop することがわかります。線画には使えそうにないかな。adaptiveThresholddilateTODO作ってみて改めて思ったのは、AI を使わないで漫画風やアニメ風の画像を用意するのが、すごく大変ということでした。雑にどこかの著作物である画像をサンプルに載せるのは簡単なのですが、きちんと権利関係を処理しながら載せようとすると、サンプルを用意するだけでも大変でした。AI を使うとそのへんの問題がサクッとしてしまうので、うーんという気分です。
4ヶ月前
簡易的な暗視カメラ Nocto Camera を作った
marmooo's blog
OpenCV の練習で簡易的な暗視カメラ Nocto Camera を作りました。暗視カメラと書いていますが、露出不足の環境でも綺麗に撮影ができるカメラアプリです。画像のコントラストを補正する画像アプリとしても使えます。暗視カメラとか使ったことないし、使う人もそんなにいない気がするのですが、今回は OpenCV のアルゴリズムの精度と速度を確認する勉強目的で作ったので、まあ良いです。Nocto Cameraたとえばこんな感じになります。 アルゴリズムは CLAHE ヒストグラム平均化を利用しています。類似アルゴリズムとしては ToneMap を使った HDR (High Dynamic Range Imaging) があります。TonemapWeb 上だとカメラ撮影でも複数の撮影データを自動で用意するのはなかなか難しいです。iPhone/Android の 自動 HDR はおそらく ToneMap を利用していますが、カメラが 1つしかない iPhone 7 時代から HDR 機能はあるので、露出時間を変えたものを同時に撮影して ToneMap をしていると予想できます。ただこれと同じようなことを Web 上で実現しようとすると、まずは exposureTime を設定してカメラを起動する必要があります。iOS では exposureTime の設定自体が存在しないので、現状できません。iOS 以外だとカメラの設定変更と撮影を繰り返せば実現はできそうですが、切替に多少時間が掛かるため撮影に時間が掛かりそうです。CLAHEただ表示の綺麗さを求めなければ、clip limit を 0 にしたとき良い感じの暗視カメラとして動作します。これは輝度値が 0/1 の差しかなくても反応するモードなのでノイズが非常に多いですが、物体も発見しやすくなります。パソコンの黒画面程度の光しかない環境でも、近場にあるものは非常に綺麗にカラー判別できることがわかりました。ちなみにパソコンの白画面程度の光があれば、数メートル離れたところにあるものは何となく判別できる、くらいの精度では動作します。equalizeHist, Gamma correctionTODO
4ヶ月前
難読漢字一覧を作った
marmooo's blog
難読漢字辞書を作りました。念のため書いておくと、地名・人名などの固有名詞を含まない辞書です。それらを含むとキラキラネームで酷いことになるからね…。まず問題意識としては、漢検準1級からは表外読みもテストに出題される仕組みがあります。このとき準1級と1級のどちらにその漢字を載せるかの問題があります。準1級に載せると数が多くなり過ぎてしまうので、レベル別に分けた難読漢字辞書があると良いなと思って作りました。他にはない基準で辞書を作っていて、小学生にとっての難読漢字、中学生にとっての難読漢字、高校生にとっての難読漢字、大人にとっての難読漢字の 4種類で分けています。このほうがわかりやすいでしょ?難読漢字一覧作り方納得(ナットク) 格子(コウシ)手綱(タヅナ) 金物(カナモノ)音頭(オンド) 夫婦(フウフ)順応(ジュンノウ)因縁(インネン)春雨(ハルサメ)上例で難しいのは音韻変化のカナモノ、音韻添加のハルサメです。このような読み方の変化は予測できないので難読漢字です。他にも転音・音便・音韻脱落・音韻融合などは変化が不明瞭で、たぶん正確には予測できません。他に難しいのは「取引、入口、場合、組合、立場、引換」などの送り仮名を省略した語句です。この処理はたぶん形態素解析の知識を入れないと無理だと思うのですよね。yomi-dict を使えば漢字一字の特殊読みを取得できるので一応は対処できましたが、わかりやすいルールで処理できないので、日本語は本当に難しいなあと感じさせられました。まとめまた思ったより難読漢字は数が少ないこともわかりました。先の条件だと 8,000 くらい。Unihan Database に登録されている音訓を表内と見なした場合は 3,000 くらい。Unihan Database の音訓もまあまあ使えるんだなとわかります。
5ヶ月前
Photo Scanner を大幅に改善した
marmooo's blog
スマホをスキャナーにできるアプリ Photo Scanner を大幅に改善しました。気付けばスキャンアプリも色々できていたので、改めて色々考え直してすべて再実装しました。もはやコンセプト以外は原形を留めてませんが、まあ仕方ない。Photo Scanner1. 自動矩形探索の廃止→手動設定の追加2. 矩形予測機能を追加他の方法では Canny Edge Detecttion と HoughLines を組み合わせる方法を見つけました。未確認ですが複数のプリントを読み込む場合には良いかも知れません。3. Wasm SIMD/Threads 対応速度面ではまったく不満がなくなりましたが、現状ではファイルサイズの課題があります。おそらくその原因は一つの関数に大量のオプション実装が含まれているからです。cv.findContours() → 最大領域を cv.minAreaRect() するだけでも 1.4MB 必要です。OpenCV.js を使うのが癪で JavaScript での実現も一瞬だけ考えましたが、画像処理は Wasm のほうが明らかに高速なのでつらい。今回は諦めポイントの 1つですが、この記事を参考にして C/C++ でコードを書いて Wasm 化すると、コンパイラが非常に強力なこともあって、インライン展開による最適化が期待できそうです。4. Deep Denoise の廃止→様々な手動加工に対応たとえば cv.adaptiveThreshold() は C が超重要なことも今さら知りました。その結果を活かして矩形探索の処理も安定化させたので、何事も作って見るものです。それ以外のフィルター処理は glfx.js を利用しているだけなので、実のところ実装は簡単でした。glfx.js は前から知ってはいたのですが、こんなに便利だとは気付いていなかった。深層学習による影の除去はまた取り組んでみたい気持ちはありますが、みんな脳死で深層学習なので、最近は深層学習しないほうが面白そうと思ってます。5. Modal 廃止→画像編集ソフトっぽく他の細かな改善点を上げるとキリがないですが、実装をコンポーネント化したり、高速化したり、細かなバグフィックスをしたり、設定項目を削減して代わりに手動設定できるようにしたり、かなり色々やりました。実装もほとんどやり直して、わかりやすいものに
5ヶ月前
ぬりえもじを作った
marmooo's blog
ぬりえもじ を作りました。塗り絵+絵文字でぬりえもじ。シンプルな塗り絵教材はそれなりにあるので、完成品を横に置いて色を見比べながら色を塗ってもらうアプリにしました。つまり色の感性を鍛えるアプリになっています。ぬりえもじ色彩アプリと色覚異常だいたい完成してから遊んでみると、色数が 8色以上になるとキツイと感じました。8色以上になる頻度はそんなに多くないですが、私も色に強くないので…。また、えもじパズルの時と同様に面積が小さすぎる部分がたまにあるので、面積が全体の 5% 未満なら最初から色を塗っておくことにしました。面積 5% 未満にあらかじめ色を塗っておくと、複雑な問題の均一化ができて、8色以上になる確率もかなり減って、なかなか良い感じです。作り方採点の時にも完全に隠されているノードは問題になります。具体的には完全に合っているのに 100点が取れなくなります。ノードごとに色を採点するのが簡単なので、比較すると冗長な処理になってしまいますが、やはりピクセル単位で採点するのが一番安全と思います。直感的な得点にはなるので、そこは良いところです。
6ヶ月前
Ubuntu デスクトップ比較: 24.04 は波乱の予感
marmooo's blog
Ubuntu 24.04 がリリースされたので、いつも通り Ubuntuデスクトップ比較をしてみました。比較条件は 22.04 のときと一緒で、以下。22.04 の時と条件は一緒ですが、言語は明示的に英語で固定しています。これはインストーラで日本語を選べるものと、選べないものがあるからです。普通は日本語の環境を比較したい人のほうが多いと思いますが、英語表示のほうがメモリ使用量には優しいため、一部のフレーバーが有利になってしまいます。たとえば Lubuntu は日本語表示にすると 100MB くらいメモリ使用量が増えます。 VirtualBox でクリーン環境を比較検証時の最大メモリサイズは 2GB起動→安定後のメモリ使用量を free --mega で比較言語は英語悲しいお知らせとしてどのフレーバーもメモリ使用量がかなり増えました。どのフレーバーも 300MB ほど確実に増加し、MATE, Kubuntu の増加量はさらに大きいです。MATE くらいのメモリ使用量だと、最適化した Windows 11 と大差ない気がします。メモリ 4GB の PC だとそろそろ辛そうなので、これからは 8GB のほうが良いかも知れません。今のところ Lubuntu, Xubuntu が鉄板であることは変わらないですが、新しく登場した Unity が Xubuntu 並で健闘しています。 Lubuntu: 644MB〜650MB (最軽量)Xubuntu: 842MB〜857MBUbuntu Unity: 890MB〜911MBKubuntu: 980MB〜1021MBUbuntu Budgie: 1043MB〜1059MBUbuntu MATE: 1115MB〜1197MBUbuntu: 1135MB〜1262MBUbuntu Cinnamon: 1221MB〜1228MB (最重量)Ubuntu 24.04 で一番の更新点は、おそらくインストーラの更新です。最初はなんか意味あるのか? と思いましたが、Lubuntu などを日本語でインストールできるのが大きいかも。まだ初期画面が英語のほうが多いですけど、シェア獲得のために本気を出し始めたのかも知れません。GUI に不満が見つからないのも、そういった理由から細かな改善を行ったのかも知れません。 Lubuntu Xubuntu Ubu
6ヶ月前
fontconv で ligatures font をサポートした
marmooo's blog
fontconv を大幅に更新して、ligatures font をサポートしました。ligatures font の解析の仕方は Read ligature from a font #384 が参考になります。他にも Ligatures #194 が参考になるのですが、getLigatures() は low level 実装のため、応用的な解析には使いにくい問題があります。addLigature() は参考になります。svg2ttf は空だとバグるので、その修正で多少時間が掛かりました。ligature 文字列の中でまだ登録されていない Glyph を空の Glyph として登録する利用する Glyph をリストアップして登録するGlyph に適用する ligature 文字列をリストアップするligature 文字列のインデックスを利用して ligature を登録するttf2svg の実装が ligatures font を想定していなかったので改良しました。そして機能を使いやすくするために --remove-ligatures オプションを付けました。オプションなしだと ligatures は残しますが、付けるとファイルサイズを考慮して削除します。ついでなので、さらなる最適化を考慮して以下のオプションを加えました。--ligature <str> (ligature を利用したサブセット化)--ligature-file <path> (ligature を利用したサブセット化)--name <str> (glyph-name を利用したサブセット化)--name-file <path> (glyph-name を利用したサブセット化)
6ヶ月前
フォントを様々な形式に変換する fontconv を作った
marmooo's blog
フォントを様々な形式に変換する fontconv を作りました。.ttf, .otf, .svg, .woff, .woff2 の相互変換とサブセット化に対応しています。.eot も出力だけは対応しています。なぜ作ったかというと、フォントを Web 上で利用するための woff2 最適化に苦労したからです。私の場合は以前 ttf2svg を自作したので、ttf2svg -> svg2woff2 の2回で woff2 は最適化できるようにしていたのですが、巷のアイコンは woff2 形式でしか配布されていないことがあることに気付きました。これがなかなか強敵で、woff2 をロードして最適化した後、woff2 へ再変換できるライブラリが見つかりませんでした。具体例を上げれば Material Icons の最適化は大変で、びっくりしました。さすがにこれはおかしいなと思い、様々な形式に対応したフォントコンバーターを作りました。pyftsubset は動きますが、Python でインストールがやや面倒で、異体字に対応していない問題があります。 fontminfont-carrierfont-extractorweb-font-converter pyftsubsettdewolff/font が出てきました。拡張子ごとにパーサをフル実装しているようで凄いですが、私のおきらく実装でも選択肢が増えるのは良いんじゃないかな…。OTF からの変換が難しすぎる!!!opentype.js を使っていますが、代替になるライブラリは現状存在しないので、OTF をベースに実装することは絶対条件です。最初は、唯一きちんとした otf2ttf 実装に見えた fonteditor-core を試してみました。しかし glyph-name の情報をバッサリ切り捨ててしまう実装だったので、とてもフォント最適化には使えないとわかりました。ttf2svg を使って font→svg→ttf で変換するのが、一番仕様がわかりやすくて良いとわかりました。ただ ttf2svg の実装も色々と問題があるのがわかっていたので、良い機会なのでバッサリ書き直し、fontconv と同じオプションで動くようにしました。EOT 形式についてfonteditor-core に eot2ttf が付いていたので試してはみまし
7ヶ月前
えもじパズルを作った
marmooo's blog
えもじパズル を作りました。完成図と見比べながらパーツを並び替えて、福笑いみたいに絵文字を作るゲームです。細部まで見比べる力を鍛えるので、特に美術や算数や漢字の学習へ応用しやすいかな。えもじパズル最近は アイコン点描写、漢字点描写 を作っていて、絵文字点描写も作っていたのですが、ボツ案となりました。絵文字は (1) 複雑すぎる、(2) 直感的な描画ではないことが多い、の2つが厳しかったです。1 は左右に絵文字を表示することで自然と表示サイズが小さくなるため、思っていたより点が密集してしまい、複雑に感じることがわかりました。スキップアルゴリズムを付ければ複雑性は緩和できますが、パーツの重なりを活かして作成されていることが多いので点の直感性とズレていることが多く、本質的な厳しさを感じました。描画の直感性は点つなぎだと誤魔化せるけど、点描写は誤魔化せないからなあ。作り方パズル部分の作り方は簡単なのですが、様々な絵文字で動くようにするのが一苦労でした。たとえば drag & drop のためにはピースごとに transform 属性を除去して新たな transform 属性を付与する必要があります。ブラウザだと階層的な transform を getCTM() で解析できるので良いですが、非ブラウザだと解析はなかなか大変で対応ライブラリはないと思います。getCTM() を使っても元々の path データに transform: matrix() があると移動用の transform 属性と混じってしまうので、まだ面倒なことあります。これは元の transform の matrix を逆変換しながら座標変換を行い (参考1、参考2)、正しい移動距離を算出し、matrix を更新する必要があります。意外と難しい。DOMMatrix や SVGMatrix、getCTM() は初めて使いましたが、なかなか便利です。やはりブラウザの API は最強です。
7ヶ月前
drop-inline-css でインライン化レベルを最適化する
marmooo's blog
CSS 最適化ツール drop-inline-css を更新し、drop-inline-css でインライン化レベルを最適化できるようにしました。一部分だけ不要 CSS をチェックしたり、一部分だけはチェックをせずそのまま inline 化できたりします。めちゃくちゃ便利な気がしますが、まあ私だけかな…。どんなことができるなったかというと、こんな感じの HTML を、<html> <head> <link class="drop-inline-css" rel="stylesheet" href="inefficient.css"></link> <link class="inline-css" rel="stylesheet" href="efficient.css"></link> <link rel="stylesheet" href="keep.css"></link> </head> <body> <p>styled</p> </body></html><html> <head> <style>p { text-decoration: underline; }</style> <style>pre { color: red; }</style> <link rel="stylesheet" href="keep.css"></link> </head> <body> <p>styled</p> </body></html>静的な HTML から要不要が判断できる CSS動的な HTML ではあるものの事前記述で要不要が判断できる CSSJavaScript をよく見ないと要不要が判断しにくかったり設定が面倒な CSSBootstarp の CSS だけで動く大部分のコードBootstrap の JavaScript が必要なコンポーネントautocompletr や simple-keyboard などのライブラリまた上記 2-3 の CSS は遅延ロードが可能で、その最適化は 3種類に分けられます。 <link rel="stylesheet" href="base.css" media="print" onload="this.media='all';this.onload=null;">template タグ内で style を loadtempla
8ヶ月前
アイコン点描写と漢字点描写を作った
marmooo's blog
アイコン点描写 と 漢字点描写 を作りました。アイコン点描写漢字点描写作り方上記が作り方の基本ですが、実際はもう少し必要です。たとえば点と点の隣接状態は Z/z/M/m コマンドを解析しながら行なう必要があり、重複する座標に点があったときのイベント処理を考慮する必要があります。この 2つがかなり面倒でした。アイコンの調整アイコン点つなぎ や 漢字点つなぎ と同じものが使えます。ただアイコンのほうはやや苦労しました。具体的には アイコン点描写 は解答を先に表示しているので、プロットするときと差があると面白くありません。このとき、透明な外枠を付けている Tabler Icon の違和感が大きい問題が起きました。透明な枠はプロットする必要ないですからね。アイコンセットを変えることも検討して結構色々なアイコンセットを試したのですが、透明な外枠を付けているアイコンが意外と多かったので、Tabler Icon は中身を少し弄ることで対処しました。こういう細かな調整が必要な時もあるので、アイコンセットはライセンスが緩くないと難しいなと感じました。今のところ MIT / Apache-2.0 / CC-BY / SIL-OFL-1.1 に限定しています。アイコン点つなぎ では線を引く仕組みを作るのは困難でしたが、これなら線を引く練習にも使えます。ただしスマホでプレイするのは難しいので、タブレットか PC でプレイするほうが良さそう。またほとんど同じ位置に点がある時は二回なぞらないといけないのは注意点でしょうか。こういうのをどこまでチェックするかは割と難しい問題です。
8ヶ月前
えもじ点つなぎを作った
marmooo's blog
えもじ点つなぎ を作りました。アイコン点つなぎ はアイコンを使っているので、モノクロでシンプルなものがほとんどでした。逆に 漢字点つなぎ は難しすぎる子もいるでしょう。絵文字ならカラフルで難易度はほどほどのものが多いので、新鮮なものになります。プレイ感覚は絵を書いている感覚に近くなり、デジタルデータの仕組みを学ぶのにも使えます。えもじ点つなぎ今のところ、カラフル版は Noto Emoji, Twemoji, Fluent Emoji (Color)、Blobmoji、Emoji Two (Color)、モノクロ版は Noto Emoji Twotone, Fluent Emoji (High Contrast)、Emoji Two (Twotone) をサポートしています。初期リリースでのサポート数は 2万個 ほどです。作り方アイコン点つなぎ、漢字点つなぎ では数字の再配置アルゴリズムだけで十分に遊べるものになりましたが、絵文字は絵の複雑性に一貫性がないため、問題によっては再配置で対処できないケースがたまにあります。同じ地点を何度も通ったり、局所的に複雑なケースがあり得るということですね。再配置で対処できない点を一時的に消すのが楽そうと思いましたが、局所的に問題が発生したときにわかりにくいし、紙に印刷できないのが気になりました。そこで密集度の高すぎる領域をスキップする実装を取り入れました。
10ヶ月前
漢字点つなぎを作った
marmooo's blog
漢字点つなぎを作りました。書き順は考慮する必要がないのでいい感じのフォントさえあれば作れます。とはいえそこがハードルで、思ったより考えることがありました。漢字点つなぎフォント選びNoto Serif JP 以外に良いフォントがないか、改めて色々なフォントを見ました。シンプルで良いなあと思うのはゴシック形式の Noto Sans JP で、これはサポートしておきました。ただ Noto 以外で点つなぎに使えそうなもの、と考えるとなかなかちょうど良いものはない気がしています。ttf2svg@marmooo/ttf2svg を使っていますが、細かな調整が必要になったので、ついでに改良しておきました。具体的には、文字を仕様上表示され得る領域にSVG化するのと、ぴったり拡大して最大領域に SVG 化するのは微妙に違い、対応しにくい問題があったので、細かな調整ができるようにしました。たいていの絵文字やフォントは advancedWidth と linespace = ascent - descent + linegap を見れば、SVG の viewport を決定できます。ただ漢字にこの条件を当てはめると、特に descent の影響が大きすぎて不自然な余白が生まれます。とはいえ descent だけを無視する訳にもいきません。なぜならフォントの作者によって、ascent, descent はバラバラの意味を持っているからです。そこで units-per-em を使うと、ぴったりになることが多いです。ただ units-per-em も壊れたデータが入力されていたり、縦横比が壊れていると意味をなさなかったりします。仕方ないのでオプションで数字調整できるようにしました。そんな訳でサポートしているのは約 1万1000種類×2=2万2000個のグリフです。これだけあれば量で困ることはないでしょう。アイコン点つなぎと違ってそれなりに歯ごたえがあります。
10ヶ月前
アイコン点つなぎを作った
marmooo's blog
アイコン点つなぎ を作りました。アイコン点つなぎ幼児教育では点つなぎやグリッド点つなぎが有名です。それらは線を書く練習や、簡易的に認識力を向上させる練習として便利ですが、題材を作ったり探すのが大変です。そこで自動生成してゲーム化すると面白そうだなと思って作りました。難しいところ作り方svgpath を使って配列の座標にします。path 配列から text で数字を表示します。あとは text を順番にタッチしたときに path の線を復元するようなコードを書けば完成です。アイコンにも色々あるアイコンセットによっては、稀に外枠と内枠を一つの線で書いているもの (Line Icon?) があります。これが世の中の点つなぎに一番近いもので重要と考えています。ただそのような書き方をしているアイコンセットは多くありません。また地道にソースコードを見る以外で見分けが付かないので確認が大変です。今のところ Line Icon として使えて数が多いものは Iconoir、Solar icon set、Majesticons、Lucide くらいと思っています。これらは優先的にサポートしました。Material Line Icons も使えそうでしたが、animateMotion タグの解析が大変そうだったので見送りました。
10ヶ月前
Unicode の部首を一意にする
marmooo's blog
@marmooo/kanji の部首データを Unicode 全漢字に対応させました。@marmooo/kanji では画数データは一意になるように設計しているのですが、Unihan Database の部首は Unicode 15.1 では 183 件の重複登録があり、この解決に苦労が伴ったのでメモを残しておきます。ちなみに Unihan Database では画数の管理で int を諦めて string を使っているのですが、情報が不確かな漢字のために int を放棄するのは嫌なので int にしています。具体的なコードはこちらを参照してください。漢字データベースプロジェクトのようなチェック が必要になるのでしょうね。将来的にはやってみるかも知れませんが、IDS も更新しないといけないので時間が掛かります。同じように中国では簡体字を作ったときのことをよく考えないといけない漢字が多々あります。たとえば日本だと「卧」はそのままの字形で理解されていて Unihan Database では「卜部」です。ただ中国だと「臥」が元字で、簡化によって「卧」になったので、康煕字典に合わせた部首は「臣部」なんですよね。康煕字典に記載があるからといって、合わせすぎるのも難しそうです。新しい漢字と古い漢字は分けて考えないといけません。「厼」のように康熙字典網にも文字情報基盤にも登録されていない漢字も、データがどこから来たのかよくわからないで困ります。ネットで調べてもほぼ情報がなく、なぜか一番詳しいのが 日本語の Wiktionary みたいなケースがチラホラあります。当然ながら部首がわかるはずもないので、Unicode に従うしか方法はないでしょう。ちなみに「厼」の本字は「彌」のようで、そこから様々な異体字と新字体が生まれているようです。異体字はどれも部首がバラバラでちょっと笑えます。文字を簡単にしようとすると部首が崩壊するんだなあ。つまり本字以外の部首は深く考えたら負けで、特徴的なパーツを選び、Unicode 配列に従っておけば良さそうです。また本字以外は部首より、異体字をたどったり、IDS で検索するほうが良さそう。追記: Unicode® Standard Annex #38 の画数の説明 を読んでいると、一意にしようとはしているみたいですね。あまり手を出さないようにしつつなるべ
1年前
SVG の描画要素を path に変換する shape2path を作った
marmooo's blog
SVG の描画要素 (rectangle, circle, ellipse, line, polyline, polygon) を path に変換する shape2path を作りました。以下の類似ライブラリが存在することはわかっているのですが、Web 上で動かなかったり、実装不足があったり、XML パーサが巨大すぎるので、依存なしでシンプルに動くものを作りました。依存がキツかったり fs module 依存が邪魔なときに毎回ゼロから作り直しているのですが、どうするのが良いのかなあ。たぶんどうしようもないので、Node.js / Deno / Bun に依存するコードはなるべく使わないのが鉄則でしょうね。 svgo convertPathSVGPathConvertersvg-flattenブラウザとオフラインの実装を分けるのが難しいすべてを諦めて DOM を再現するライブラリを使うのが楽ではあるのですが、バンドルサイズは大きくなります。この路線でいくなら linkeDOM は互換性が高く、速度もまあまあなので良さそうです。とはいえ 300KB くらい増えてしまうので依存性をゼロにしたいなと思い、HTMLDocument は無理やり使わないようにして、関連するオブジェクトとメソッドを引数で渡す実装にしました。これなら依存性が引数として与えられる外部の HTMLElement だけになり、かなり動かしやすくなります。ただこの路線での実装は、ブラウザと完璧な互換性を持っているライブラリがほとんどない問題があります。たとえば私がよく使っている node-html-parser は HTMLElement.cloneNode() をサポートしていないので、多少ながら違和感が残る実装になりました。ただこういう違和感はブラウザに慣れ過ぎているからで、ESM 的な観点から考えると普通なのかも知れません。DOM の API を使ったライブラリは作り方が難しいですね。円/楕円の変換方式SVG の <circle> を <path> で描く を利用しています。描画パターンを選択できると嬉しいと思ったので、すべてサポートしています。私のお気に入りは「8本の2次ベジェ曲線で描画」です。描画精度は低い反面、応用性が高いです。
1年前
都道府県タイピングを作った
marmooo's blog
都道府県タイピングを作りました。似たようなものはいくつかありますが、やはり自分用のものが欲しかったので車輪の再発明をしました。都道府県タイピング何分くらいでできるかなと思って試してみたところ、5.0 key/sec だと 1分くらいでクリアできるみたいです。考慮時間を含めると 100秒くらいでしょうか。小学 4 年生〜小学 6年生の子が遊ぶことを想定すると、早い子が 3.0 key/sec くらいと予想できます。1.5〜3.0 key/sec の子ならクリアできるように 5分間のゲームとして作りました。1.0 key/sec 以下はもう少しタイピングの練習をする必要があります。遊んでみると、意外と文字数が少ないので初期の学習には良いかも。ただこの手のタイピング、昔からどうにも HARD モードは苦手なんだよなあ。たぶん私は言語中枢をメインに使って都道府県を覚えている訳じゃないんだろうな。
1年前
漢字の情報取得ライブラリ kanji を作った
marmooo's blog
今さらですが、欲しくなったので漢字の情報取得ライブラリ @marmooo/kanji を作りました。漢字の情報と言っても色々ある訳ですが、レベルごとの漢字のリストを取得し、文章のレベルを取得するライブラリです。特に教育指導要領をチェックするコードは何十回も書いているので、そろそろライブラリが欲しいと思って自作しました。ついでにかなり色々な機能を付けたので、つよつよです。漢字の処理は非常に難しいので、このような誰も作らない基盤ライブラリの整備が必要です。import { Kanji, JKAT } from "@marmooo/kanji";const kanji = new Kanji(list);const jkat = new Kanji(JKAT); // 教育指導要領 / 日本漢字能力検定jkat.getGrade("学校"); // --> 0jkat.getGrade("漢"); // --> 2jkat.getGrade("うどん"); // --> -1JISCode: JIS 漢字コード (第 1 水準〜第 4 水準)Jinmei: 人名用漢字 (常用漢字, 常用漢字の異体字, 人名用漢字)JKAT: 教育指導要領 / 日本漢字能力検定 (10級〜1級)Unicode1Radical: Unicode 1.1 の康熙字典 214 部首コードUnicode1RadicalStrokes: Unicode 1.1 の康熙字典 214 部首の画数データJoyoRadical: 常用漢字の康熙字典 214 部首コードJoyoRadicalStrokes: 常用漢字の康熙字典 214 部首の画数データJoyoStrokes: 常用漢字の画数データJIS4UnihanStrokes: JIS 第 4 水準の Unihan_IRGSources.txt に基づく画数データ漢字の画数を調査した漢字の部首を調査したライブラリ化するときにはとにかく扱いの難しい漢字として「𠮟」があります。「𠮟」は新しい常用漢字ですが、[音訓の小・中・高等学校段階別割り振り表(平成29年3月)]を見ると、「𠮟」じゃなくて「叱」で書かれているんですよねえ。漢字検定の漢字一覧も表記は「𠮟」なのですがコピペすると「叱」になったりして困ります。他にも間違いがいくつかあって紛らわしい。そう...
1年前
漢字の画数を調査した
marmooo's blog
こども漢字辞書 を改善するために、漢字の画数を調査しました。使えるデータの中でどれが一番信用できるか常用漢字のリストをチェックしたところ、以下になりました。特に酷いのが UnihanXML で、2136 文字中、2052 文字ずれています。もはや合っているほうが少ない。これはひどい。$ deno run -A check-stroks.jscheck 2136 kanjis:diff animCJK: 1diff joyoKanji: 5diff mojikiban: 5diff cjkvi: 145diff unihanIRGSources: 177diff unihanXML: 2052KanjiVG と animCJK が一番正確です。joyoKanji は古い常用漢字しか対応していないため 4つ間違いが増えていますが、データとしては完璧です。MJ 文字 も良い精度ですが、フォーマットが地獄という問題があります。それ以外の画数データは使えません。ちなみに KanjiVG, animCJK, joyoKanji で画数が異なる文字は一つだけしかありません。それが「衷」で、9画と 10画のどちらが正しいのかという問題があります。結論から言えば どちらでもいい ようですが、学校を基準にするなら 9画が正しいようです。@marmooo/kanji にも常用漢字の画数データを追加しました。以下のように画数のレベル情報を取得できます。import { Kanji, JoyoStrokes } from "@marmooo/kanji";const joyoStrokes = new Kanji(JoyoStrokes);joyoStrokes.getGrade("学"); // --> 8joyoStrokes.getGrade("校"); // --> 10joyoStrokes.getGrade("学校"); // --> 10常用外漢字kanji_kakusuu は人名用漢字もサポートしているみたいです。人名用漢字以上を無理やりサポートするなら unihan_IRGSources.txt を改良した cjkvi が良さそうですが、古いですしミスは必ずあります。@marmooo/kanji でサポートしています。unihan_IRGSources.txt を使えば J
1年前
漢字の部首を調査した
marmooo's blog
こども漢字辞書 を改善するために、漢字の部首を調査しました。MJ 文字、KanjiVG、JoyoKanji の 3つがありますが、厳密なチェックをしたことはありませんでした。部首は表記の仕方がバラバラなのでチェックをするのが思ったより大変でした。部首の表記ゆれ他にも黒部を示す部首に「黑 黒」のどちらを使うかという問題もあります。これも 日本の新字体は「黒」のように囲いの内部を「十」としている。なお、表外漢字においては康熙字典に従い「黑」を用いる。 のが正解です。あと「内」くらいの簡単な漢字でさえ「入部」と「冂部」のどちらもあり得たりして、部首そのものが適当すぎるので困ります。そんなとき、たいていのネット辞書は文献が不明であまり信頼性がないという問題が起きるのですが、KanjiVG は JIS漢字字典を利用しているという明確な信頼性があります。やはり少なくともネットの部首情報よりは KanjiVG のほうが信頼できるというのが現状ではないでしょうか。部首の読みの表記ゆれ偏旁 を考慮して自動で名前を付けられないかと一瞬思いましたが、例外が多すぎてたぶん無理です。特にいい方法はないので、康熙字典 214 部首に位置情報を付けたデータを用意し、読みを返す関数を作りました。実際に作ってみると、やはり「人部」などのコンポーネント情報だけだと、部首名を決めるのは難しいことがわかりました。たとえば「从」は部首の位置自体は左にあるはずですが、どう見ても「亻 にんべん」ではないので、「ひと」が正しいと思います。つまり「人 亻」をきちんと分けて登録していないと、分類できないとわかります。KanjiVG 以外の部首データは不完全ということになります。これは「沖」から派生した漢字で、kvg:element「冫」の意味する氷とは関連がないので、kvg:originalが「水」になっているようです。英語のほうが日本語より正確というね…。しかしこの登録は紛らわしいだけのような気もします。とりあえず部首を考えるだけなら kvg:element だけを見るのが安全そうで、kvg:original は見ないほうが良さそうです。月部と肉部の見分けくらいには使えますが、間違いが多すぎる問題があります。難しスギィ!CJK 漢字の部首情報CJK 統合漢字を漢字配列だけで康熙字典 214 部首に変換している人もい
1年前