なでしこさんで画像ピクセルシャッフル
今週もあれよあれよという間に日曜日。
ボーっと生きてたら、すぐ時間切れ。
いい加減にそろそろアレを書いておきたいのになあ……
もう明日からアドベントカレンダーも始まるし……
鬱だ氏のう……
というわけで、大変遺憾ながら、今日も元気 (?????) に適当なプログラムを書いてごまかすことに。
……と思ったが、ここで筆者の致命的な頭の悪さが炸裂。
前にも事故を起こしており、危ないなあ、気をつけないとなあとは思いつつ、愚かにも寝落ち。
気がついたときには日付が変わる数秒前で、助からず。
オワタ。しょせん自分はこの程度のカスでクズで無能でバカでアホでドジでマヌケで愚かで頭が悪いということだ。
ばーかばーか。
鬱だ氏のう。
このまま1週飛ばしてしれっと次の日曜日に投稿する選択肢もあるが……まあせっかく作ったし、でき次第公開しよう。新しい「今週」1週分実質飛ばせることには変わりないしな。(本当に飛ばしたのは「先週」だがな)
今回やったこと
なでしこで、画像のピクセルをシャッフルする。
実行例
例として、適当な風景写真を入力してみる。
すると、このようにピクセルがごちゃ混ぜになり、何の画像かわからなくなった。
しかし、なんとなくの色の雰囲気は残っているようだ。
プログラム
画像ピクセルシャッフル (プログラム貯蔵庫)
ポイント
UIを作成する
ファイル欄は「INPUT」のDOM部品作成。
それの「type」に「file」をDOM属性設定。
改行作成。
画像キャンバスは[1,1]のキャンバス作成。ファイル選択用の入力欄と、画像のデータ取得および出力用のキャンバスを用意する。
なお、「キャンバス作成」により、キャンバスを操作する命令の操作対象が自動で作成したキャンバスに設定される。
画像を読み込む
●(FILEの)画像シャッフルとは
定数の画像URLは「URL.createObjectURL」を[FILE]でJS関数実行。
定数の画像オブジェクトは画像URLを画像読み待つ。
「URL.revokeObjectURL」を[画像URL]でJS関数実行。
定数の画像幅は画像オブジェクト$width。
定数の画像高さは画像オブジェクト$height。
画像キャンバスの「width」に画像幅をDOM属性設定。
画像キャンバスの「height」に画像高さをDOM属性設定。
描画中コンテキスト$globalCompositeOperationは「copy」。
画像オブジェクトを[0,0]に画像描画。渡されたファイルオブジェクトに createObjectURL() で URL を割り当て、「画像読待」命令で画像を読み込む。
「画像読待」命令は、画像を HTML の img 要素として返す。
画像を読み込み終えたら、revokeObjectURL() で、用意した (一時的な) URL を捨てる。
その後、画像の大きさ (高さと幅) を取得し、キャンバスの大きさをそれに合わせて設定する。
そして、globalCompositeOperation を用いて「画像をそのままキャンバスに書き込む」設定にし、画像をキャンバスにそのまま乗せられるようにし、実際に読み込んだ画像をキャンバスに乗せる操作を行う。
画像のピクセルデータを取得し、シャッフルを行う
定数の画像データは描画中コンテキストの「getImageData」を[0,0,画像幅,画像高さ]でJSメソッド実行。
定数の画像配列は画像データ$data。
定数のピクセル数は画像配列の要素数を4で割る。
位置を0から(ピクセル数-1)まで繰り返す
定数の候補数はピクセル数から位置を引く。
定数の選択要素は候補数の乱数に位置を足す。
オフセットを0から3まで繰り返す
定数の退避データは画像配列@(位置×4+オフセット)。
画像配列@(位置×4+オフセット)は画像配列@(選択要素×4+オフセット)。
画像配列@(選択要素×4+オフセット)は退避データ。
ここまで。
ここまで。
描画中コンテキストの「putImageData」を[画像データ,0,0]でJSメソッド実行。
ここまで。getImageData() 関数でキャンバス上の画像データ (ImageData) を取得し、そこから画像データの本体である型付き配列を取得する。
乱数でどのピクセルと入れ替えるかを決め、この画像データは4要素で一組なので、4要素セットで交換を行う。
シャッフルが完了したら、putImageData() 関数で画像データをキャンバスに書き戻す。
シャッフル実行用のイベントハンドラを設定する
ファイル欄を変更した時には
定数のファイルリストは対象$files。
定数のファイルはファイルリスト@0。
もし、ファイルならば
ファイルの画像シャッフル。
ここまで。
ここまで。ファイル欄で選択されているファイルが変化したとき、選択されたファイルの情報を取得し、画像の読み込みとシャッフルの処理を呼び出す。
NG集
関数とメソッドの間違い
定数の画像URLは「URL.createObjectURL」を[FILE]でJS関数実行。のかわりに
定数の画像URLは「URL」の「createObjectURL」を[FILE]でJSメソッド実行。とすると、実行時に開発者ツールのコンソールに
Uncaught (in promise) Error: JSオブジェクトを取得できませんでした。
というエラーが出た。
コンソールにしかエラーが出ず、わかりにくいという面はある。
とはいえ、URL.createObjectURL() は静的メソッドであり、このような静的メソッド (Math.random()) を呼び出す例は「JS関数実行」の説明にあるため、これは使い方をきちんと把握していなかった方が悪いだろう。
「配列入替」は型付き配列では使用不可
定数の退避データは画像配列@(位置×4+オフセット)。
画像配列@(位置×4+オフセット)は画像配列@(選択要素×4+オフセット)。
画像配列@(選択要素×4+オフセット)は退避データ。のかわりに
画像配列の(位置×4+オフセット)と(選択要素×4+オフセット)を配列入れ替え。としたところ、実行時に開発者ツールのコンソールに
Uncaught (in promise) Error: 『配列入替』の第1引数には配列を指定してください。
というエラーが出た。
「配列入替」(および他の多くの配列操作命令) は Array 以外への適用を拒否する仕様になっており、今回用いている画像配列 (ImageData.data) は Uint8ClampedArray なので、拒否されてしまったようである。
エラーがページに出ず、開発者ツールのコンソールにしか出ないというのは、特定のエラーの性質ではなくイベントハンドラの性質のようだ。
おわりに
今回は、画像のピクセルをシャッフルしてみた。
シャッフルの結果は、(当然) ピクセルが混ざり、よくわからない画像になった。
それでも、元の画像を知っていれば、元の画像の面影を感じることはできるかもしれない。
今回は手抜きなので、あると良さそうな以下の要素は実装していない。
画像読み込み時、失敗したらエラーメッセージを表示する
キャンバスの大きさを画面に合わせ、大きくなりすぎないようにする
ファイル欄だけでなく、ページ全体のどこにファイルをドラッグ&ドロップしても読み込む処理を開始できるようにする


コメント