あるアプリを作るのに近似画像を探す処理が必要になった。
Lab色空間 – Wikipedia
を得て元画像の全体ピクセル近似値を調べればいいと解った。
[PHP]似た画像を検索して近い順番に並べる(類似画像検索) | PHP Archive
手段を見つけた、PHP だけど計算方法はどの言語でも同じだろう。
ただ GD を利用しているので他言語では別の画像解析手段が必要。
ということで、同様な処理を Gjs(Linux) でやってみる。
後で osascript(Mac) 版や Web 版を作りたくなった場合に流用できるので。
Gjs だから画像は当然 GdkPixbuf を GD の代わりに使う。
とはいえ GD と同じ関数があるはずもなく違う手段で同様な処理を作る。
画像処理のサンプル一覧
imagecolorat は gdk_pixbuf_get_pixels からオフセット。
imagecolorsforindex は rowstride 値を使って取得。
実は随分前からコレで詰まっていたおかげでブログの更新が…
正直上手く変換できた自信は無いけどなんとかなったっぽいコード。
#!/usr/bin/gjs const GdkPixbuf = imports.gi.GdkPixbuf; // 比較元ファイル let sample = GdkPixbuf.Pixbuf.new_from_file("nae.jpg"); let sample_lab = getImageLab(sample); // 比較対象ファイル名の配列 let datas = ["nae01.jpg","nae02.jpg","nae03.jpg","nae04.jpg","nae05.jpg"]; let diff = []; datas.forEach(function(element, index) { let data = GdkPixbuf.Pixbuf.new_from_file(element); let lab = getImageLab(data); // 比較 let distance = 0; sample_lab.forEach(function(element, index) { distance += getLabDistance(element, lab[index]); }); diff.push({n:distance, name:datas[index]}); }); // sort diff.sort(function(a, b) { return a.n > b.n; }); // 確認 diff.forEach(function(element, index) { print(element.name, element.n); }); // 画像をリサイズしピクセルごとのLab色空間上の座標を取得する function getImageLab(image){ let thumb = image.scale_simple(4, 4, GdkPixbuf.InterpType.BILINEAR) let pixeldata = thumb.get_pixels(); let rowstride = thumb.get_rowstride(); let lab = []; for(let x=0; x<4; x++){ for(let y=0; y<4; y++){ let i = y * rowstride + x * 3; lab.push(rgb2lab([pixeldata[i], pixeldata[i+1], pixeldata[i+2]])); } } return lab; } // xyz色空間上の座標をlab色空間上の座標に変換する function xyz2lab(xyz) { let threshold = 0.008856; //Chromatic Adaptation Matrices // D50 let ref_x = 0.96422; let ref_y = 1.0000; let ref_z = 0.82521; let var_x = xyz[0] / (ref_x * 100); let var_y = xyz[1] / (ref_y * 100); let var_z = xyz[2] / (ref_z * 100); var_x = (var_x > threshold) ? var_x = Math.pow(var_x, 1/3 ) : (7.787 * var_x) + (16 / 116); var_y = (var_y > threshold) ? var_y = Math.pow(var_y, 1/3 ) : (7.787 * var_y) + (16 / 116); var_z = (var_z > threshold) ? var_z = Math.pow(var_z, 1/3 ) : (7.787 * var_z) + (16 / 116); let l = ( 116 * var_y ) - 16; let a = 500 * ( var_x - var_y ); let b = 200 * ( var_y - var_z ); return [l, a, b]; } // rgb値をxyz色空間上の座標に変換する function rgb2xyz(rgb) { let r = rgb[0] / 255; let g = rgb[1] / 255; let b = rgb[2] / 255; r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92; g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92; b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92; r = r * 100; g = g * 100; b = b * 100; let xyz = new Array(); //sRGB D50 xyz.push(r * 0.4360747 + g * 0.3850649 + b * 0.1430804); xyz.push(r * 0.2225045 + g * 0.7168786 + b * 0.0606169); xyz.push(r * 0.0139322 + g * 0.0971045 + b * 0.7141733); return xyz; } // rgb値をlab色空間上の座標に変換する function rgb2lab(rgb) { let xyz = rgb2xyz(rgb); let lab = xyz2lab(xyz); return lab; } // 2つの座標を比較し距離を返す function getLabDistance(p1, p2){ let dist = Math.sqrt( Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2) + Math.pow(p2[2] - p1[2], 2) ); return dist; }
苗ちゃんがカワイイのは当然でどうでもよくて。
同一画像なら完全にゼロ、後は色が似た順に取得できた。
GdkPixbuf なので C や Python でも同じことができるはず。
後は osascript はともかく Web で画像解析はどうするかだ。
というか Web アプリでやりたいんですけどね。