あるアプリを作るのに近似画像を探す処理が必要になった。
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 アプリでやりたいんですけどね。
