パイこね変換器を作りたい

スポンサーリンク
スポンサーリンク

パイこね変換器

シリコンに色を付ける動画

を見ていたら、パイこね変換

パイこね変換 - Wikipedia

という用語を知った。なんかやってみたいなーと思ったので、やってみることにした。

スポンサーリンク

どうやろう

ローカルだけでやってもいいんだけど、どうせなら公開してみたいなーと思った。でもサーバーのリソース使うようなやり方だと、処理しきれるのかなー?となったのでブラウザ上だけでやれるような方法を探したい。まぁなんかあるだろ。

スポンサーリンク

ファイルをアップロードして表示する

まずはこの部分を作って、パイ揉み処理は後で作ろう。

 HTML

 <input type="file" id="selectfile" name="selectfile" accept='image/*' required>

inputタグのtypeをファイルにするとファイル選択ボタンができる。accept=’image/*’でMinetype画像ファイルを限定できる。requiredで入力必須。

Javascript

const fileInput = document.getElementById('selectfile');

fileInput.addEventListener('change', func);

要素を取得して、リスナを設定する。changeっていうのが変更が確定したときに実行されるように設定するやつ。変更が発生するとfuncが実行されると。そんでfuncの中身を作ってく。この関数の書き方が色々あるから迷うんだよなー。

File

とりま

const loadImage = () => {
      const file = fileInput.files[0];
      console.log(file);
    }

てして

素材を用意しまして、ファイルを選択してみると

読めたっぽい。じゃあ今度はこれを表示する。

FileReader

<div id="preview"></div>

inputタグの下に追加しとく。

上のconsole.logの行の代わりに

const reader = new FileReader();
      reader.onload = function (e) {
        const imageUrl = e.target.result;
        const img = document.createElement("img");
        img.src = imageUrl;
        preview.appendChild(img);
      }

      reader.readAsDataURL(file);

これはまぁコピペです。fileをDataURLとして読み込んだ後に、imgタグを作成して、srcにDataURLを設定する。もっかい読み込んでみると

表示された!

スポンサーリンク

画像を加工できるようにする

画像を読み込めたので今度は画像を加工できるようにしてく。読み込んだ時点で加工の処理を走らすこともできるんだろうけど、読み込んでからボタンを押して処理を実行する感じにしたい。

ボタンを設置したり

  <h1>パイこね変換器</h1>
  <input type="file" id="selectfile" name="selectfile" accept='image/*' required><br>
  <button onclick="">実行</button>
  <h2>処理前</h2>
  <div id="preview"></div>

  <h2>処理後</h2>
  <div id="result"></div>

HTMLをこんな感じにした。

ボタンを押すと画像を表示する

要素を取得して、canvasに変換して、表示する。

function knead() {
        const preimg = document.getElementById("preimg");

        image = new Image();
        image.onload = () => {
          var canvas = document.getElementById("result");
          var ctx = canvas.getContext("2d");
          canvas.width = image.width;
          canvas.height = image.height;
          ctx.drawImage(image, 0, 0)

        }

        image.src = preimg.src;
        console.log(preimg);
      }

これなんか拡張機能作ったときやったな。

加工できるようにする

ImageData形式というものに変換すればいいみたい。context.getImageDataで取得。

var imagedata = ctx.getImageData(0,0,image.width,image.height);

ImageDataは画像を一次元の配列にしたものみたい。例えば4×4の画像の場合

0,00,10,20,3
0123456789101112131415
1,01,11,21,3
16171819202122232425262728293031
2,02,12,22,3
32333435363738394041424344454647
3,03,13,23,3
48495051525354555657585960616263

インデックスはこう振られてる感じか。グレスケ化を調べてみて

image.onload = () => {
          var canvas = document.getElementById("result");
          var ctx = canvas.getContext("2d");

          canvas.width = image.width;
          canvas.height = image.height;
          ctx.drawImage(image, 0, 0);

          var imagedata = ctx.getImageData(0, 0, image.width, image.height);

          for (let index = 0; index < imagedata.data.length; index += 4) {
            var r = imagedata.data[index];
            var g = imagedata.data[index + 1];
            var b = imagedata.data[index + 2];

            var gray = (r + g + b) / 3;

            imagedata.data[index] = gray;
            imagedata.data[index + 1] = gray;
            imagedata.data[index + 2] = gray;

          }

          ctx.putImageData(imagedata,0,0)

        }

こんな感じにしたら

できた!あとはいよいよパイ揉みすればいいんだな。

スポンサーリンク

パイこねをコードで表現したい

流れは、拡大縮小して、半分に分割して、上下に並び替えるって感じ。言うのは簡単だよね…。

拡大縮小

うんうん唸りながら

image.onload = () => {
          var canvas = document.getElementById("result");
          var ctx = canvas.getContext("2d");

          canvas.width = image.width;
          canvas.height = image.height;
          ctx.drawImage(image, 0, 0);

          var imagedata = ctx.getImageData(0, 0, image.width, image.height);
          var trnsdata = ctx.createImageData(image.width * 2, image.height / 2);

          for (let idxw = 0; idxw < trnsdata.width - 1; idxw += 2) {
            for (let idxh = 0; idxh < trnsdata.height - 1; idxh++) {
              var idxt = trnsdata.width * 4 * idxh + idxw * 4;
              var idxi = imagedata.width * 4 * (idxh * 2) + (idxw / 2) * 4;
              trnsdata.data[idxt] = imagedata.data[idxi];
              trnsdata.data[idxt + 1] = imagedata.data[idxi + 1];
              trnsdata.data[idxt + 2] = imagedata.data[idxi + 2];
              trnsdata.data[idxt + 3] = imagedata.data[idxi + 3];
              
            }
          }

          canvas.width = canvas.width * 2;
          canvas.height = canvas.height / 2;
          ctx.putImageData(trnsdata, 0, 0)

        }

縦方向は間引いて、横方向は伸長させた。半分で2倍なので考えることが少なくて済んだ。ただ横方向の補間をしてない。とりまここまでの結果

こうなった。そんで補間を効かすと

for (let index = 4; index < trnsdata.data.length; index += 8) {
            var r = (trnsdata.data[index - 4] + trnsdata.data[index + 4]) / 2;
            var g = (trnsdata.data[index - 4 + 1] + trnsdata.data[index + 4 + 1]) / 2;
            var b = (trnsdata.data[index - 4 + 2] + trnsdata.data[index + 4 + 2]) / 2;
            var a = (trnsdata.data[index - 4 + 3] + trnsdata.data[index + 4 + 3]) / 2;

            trnsdata.data[index] = r;
            trnsdata.data[index + 1] = g;
            trnsdata.data[index + 2] = b;
            trnsdata.data[index + 3] = a;

          }

こんなー

おー、いーんじゃない?

分割して上下に結合

計算が大変。なんとか

var halfwidth = trnsdata.width / 2;
          var rowsize = trnsdata.width * 4;
          var ti = 0;

          for (let ih = 1; ih <= trnsdata.height; ih++) {
            for (let i = 0; i < halfwidth * 4; i++) {
              var idxt = rowsize * ih - halfwidth * 4;
              tempdata.data
  • = trnsdata.data[idxt + i]; ti++; } } for (let ih = 1; ih <= trnsdata.height; ih++) { for (let i = 0; i < halfwidth * 4; i++) { var idxt = i + trnsdata.width * 4 * (ih - 1); tempdata.data
  • = trnsdata.data[idxt]; ti++; } }

    こんな感じに。実行すると

    やったー!とりあえず1回こねれた~

    スポンサーリンク

    複数回こねられるようにする

    いっぱいこねたい。

    回数を設定できるようにする

    <input type="range" id="slider" min="1" max="10" value="5"><label> 5回こねる</label><br><br>

    これなー。

    var range = document.getElementById("slider");
    
        function changeLabel() {
          var label = document.getElementsByTagName("label");
          label[0].innerText = " " + range.value + "回こねる";
        }
    
        range.addEventListener("input", changeLabel);

    これもー。あとはfor?全部括って

    for (let idxc = 0; idxc < tempdata.data.length; idxc++) {
                imagedata.data[idxc] = tempdata.data[idxc];
              }

    これも追加。結果

    右上になんか変なのがつくし水平中心が埋まんねーけどできた!

    スポンサーリンク

    できあがり

    パイこね変換

    まんぞく~^^

    スポンサーリンク

    参考

    input type="file"でファイルをアップロードする方法|HTMLリファレンス
    HTMLのinput type="file"の使い方を詳しく解説!JavaScriptで画像のプレビューを表示したり、アップロードの上限サイズを指定する方法、ドラッグ&ドロップで画像を選択する方法などをサンプルコード付きで紹介します。
    File APIとCanvasでローカルの画像をアップロード→加工→ダウンロードする | Tips Note by TAM
    TAM のテクニカルチームがお届けする WEB技術ブログ!
    403 Forbidden
    HTMLElement: change イベント - Web API | MDN
    change イベントは , , 要素において、ユーザーによる要素の値の変更が確定したときに発行されます。 input イベントとは異なり、 change イベントは要素の値 (value) が変更されるたびに発生するとは限りません。
    : ボタン要素 - HTML: HyperText Markup Language | MDN
    HTML の 要素はクリックできるボタンを表し、フォームや、文書で単純なボタン機能が必要なあらゆる場所で使用することができます。既定では、 HTML のボタンは ユーザーエージェント が実行されているホストのプラットフォームのスタイルと似ていますが、 CSS を使用してボタンの外見を変更することができます。
    HTMLのボタン(buttonタグ)の使い方
    HTMLのbuttonタグは、ボタンを作成するために使います。このページではbuttonの基本的な使い方から属性、aタグとの使い分け、CSSでデザインを変更する方法などまで分かりやすく解説します。
    Visual Studio Codeのhtmlフォーマッターのメモ - Qiita
    概要 Visual Studio Code(以降VSCodeと記述)のデフォルトのhtmlフォーマッターをカスタマイズしたときのメモです。 VSCodeのhtmlフォーマッターはjs-beautifyというパッケージをベースにし...
    ImageData - Web API | MDN
    ImageData インターフェイスは、 要素の領域の基礎をなすピクセルデータを表します。ImageData() (en-US) コンストラクターや、canvas に関連付けられた CanvasRenderingContext2D オブジェクトの createImageData() (en-US) メソッドおよび ge...
    キャンバスとピクセル操作 - Web API | MDN
    これまで、キャンバスの実際のピクセルは見てきませんでした。 ImageData オブジェクトを使用して、ピクセルデータを操作するためにデータ配列へ直接読み取りや書き込みを行うことが可能です。また、画像のスムージング(アンチエイリアシング)の制御方法やキャンバスの画像を保存する方法も見ていきます。
    - HTML: HyperText Markup Language | MDN
    要素の range 型は、ユーザーに特定の値より小さくなく、別な特定の値より大きくない数値を指定させるために使用します。しかし、厳密な値が重要であるとはされません。これは通常、 number 入力型のようなテキスト入力ボックスではなく、スライダーやダイアルコントロールを用いて表現されます。
    input type="range"でレンジスライダーを作る|HTMLリファレンス

    コメント

    タイトルとURLをコピーしました