memorandums

日々の生活で問題解決したこと、知ってよかったことなどを自分が思い出すために記録しています。

P5.jsでHSV Pickerを作ろうとしてハマりました

ある作業のため画像中のHSV値が欲しくなりまして。

それくらいのアプリなら転がっているだろうな。。。と思ったんですが、ちょいと自作しようと思ってハマりました。完成まで2時間くらいかかったような。。。

一応、完成したアプリは以下のような感じです。ローカルにあるファイルを選択してアップロードしまして、あとは画像上でマウスカーソルを動かすとその場所にあるHSVを取得して表示してくれます。なんか動作がおかしいのですが。。。とりあえずHUEは取れていそう。

https://editor.p5js.org/keiichi.takahashi@gmail.com/present/6OqOih90j

ちなみにコードは以下です。p5.dom.jsを使っています。

let img;
var h, s, v;

function handleFile(file) {
  if (file.type === 'image') {
    img = loadImage(file.data, imageLoaded); //★1
  } else {
    img = null;
  }
}

function setup() {
  div_h = createDiv();
  div_s = createDiv();
  div_v = createDiv();
  input = createFileInput(handleFile);
  //  input.position(0, 0);
}

function draw() {
  colorMode(HSB, 255);
  pixel = get(mouseX, mouseY);
  h = hue(pixel);
  s = saturation(pixel);
  v = brightness(pixel);
  div_h.html("H: " + h); 
  div_s.html("S: " + s); 
  div_v.html("V: " + v); 
  stroke(h, s, v);
  strokeWeight(3);
  noFill();
  rect(0, 0, width-1, height-1);
}

function imageLoaded() { //★2
  resizeCanvas(img.width*2, img.height*2);
  scale(2);
  image(img, 0, 0);
}

はまった場所は★1でした。

ページ中にFile選択用の要素を追加するのはcreateFileInputでできるのですが、このサンプルには以下のように書かれているんですね。

let img;

function setup() {
  input = createFileInput(handleFile);
  input.position(0, 0);
}

function draw() {
  background(255);
  if (img) {
    image(img, 0, 0, width, height);
  }
}

function handleFile(file) {
  print(file);
  if (file.type === 'image') {
    img = createImg(file.data); //★3
    img.hide();
  } else {
    img = null;
  }
}

この★3のcreateImageが曲者?のようで、P5やP55経験者からするとPImageのオブジェクトが得られると思うんですが、どうやらimageタグのポインタのようなものが得られるだけでPImageのような操作ができないんですね。。。いったいどうすればいいんだ。。。と調べたり試したりすること1時間くらい。loadImagを使えばいいってところまではすぐに行ったんです。でも、なぜか、loadImageで取得したimgオブジェクトのwidthとheightが1と返ってきて画像が表示されへん。。。という状況に。

ここから調べること1時間。やっと見つけたのが以下の書込み。ほんま助かりますわぁ〜。えせ関西弁すんまへん。さすが頼りになるのはstackoverflowさん(というかその利用者)です。

javascript - correct way to use loadImage in p5.js? - Stack Overflow

imageLoaded(←別に名前はなんでもよか)というコールバック関数をloadImage関数の第2引数に与えてやると、ブラウザ(というかdomか?)に画像ファイルをローディング完了した時点でその関数が呼ばれるようで、そこにimage関数を指定してやるとうまくいきました。上記コードの★2の部分ですね。

本題の作業は全然進んでいませんが。。。とりあえず1歩前進ってところでした。