https://mathematica.stackexchange.com/a/39049に見られるように、Mathematica スイートを使用して他の人が成功したと思われる効果を Javascript (特に p5.js) で再現しようとしています。
私は Mathematica について 100% 無知ですGradientOrientationFilter
が、画像のグラデーションの方向に沿ってストロークのパターンを作成するというメソッドを使用していることがわかります。
私の結果はまだ満足のいくものではありません。
私が試みているロジック
- 方向付けられた勾配のヒストグラムを作成し、輝度値を評価してから、水平方向と垂直方向の勾配、およびその方向と大きさを見つけます。
- 各ピクセルに線を描画して、グラデーションの方向をランダムなグレースケール カラーで表します。後でこれらの線を実際の写真とブレンドして使用します。
コード:
var img, vectors;
var pixelsToSkip = 2; // for faster rendering we can stroke less lines
var linesLength = 20;
var strokeThickness = 1;
function preload() {
img = loadImage('http://lorempixel.com/300/400/people/1');
img2 = loadImage('http://lorempixel.com/300/400/people/1');
/* you can test in local if the directions are correct using a simple gradient as image
img = loadImage('http://fornace.io/jstests/img/gradient.jpg');
img2 = loadImage('http://fornace.io/jstests/img/gradient.jpg');
*/
}
function setup() {
createCanvas(img.width, img.height);
noLoop();
img.loadPixels();
makeLumas();
makeGradients();
makeVectors();
for ( var xx = 0; xx < img.width; xx = xx + pixelsToSkip) {
for ( var yy = 0; yy < img.height; yy = yy + pixelsToSkip) {
push();
stroke(random(255)); // to color with pixel color change to stroke(img.get(xx, yy));
strokeWeight(strokeThickness);
translate(xx,yy);
rotate( vectors[yy][xx].dir ); // here we use the rotation of the gradient
line(-linesLength/2, 0, linesLength/2, 0);
pop();
}
}
// adding the image in overlay to evaluate if the map is good
// tint(255, 255, 255, 100);
// image(img2,0,0);
}
function draw() {
}
function makeLumas() {
// calculate the luma for each pixel to get a map of dark/light areas ("Rec. 601") https://en.wikipedia.org/wiki/Luma_(video)
lumas = new Array(img.height);
for (var y = 0; y < img.height; y++) {
lumas[y] = new Array(img.width);
for (var x = 0; x < img.height; x++) {
var i = x * 4 + y * 4 * img.width;
var r = img.pixels[i],
g = img.pixels[i + 1],
b = img.pixels[i + 2],
a = img.pixels[i + 3];
var luma = a == 0 ? 1 : (r * 299/1000 + g * 587/1000
+ b * 114/1000) / 255;
lumas[y][x] = luma;
}
}
}
function makeGradients() {
// calculate the gradients (kernel [-1, 0, 1])
var horizontalGradient = verticalGradient = [];
for (var y = 0; y < img.height; y++) {
horizontalGradient[y] = new Array(img.width);
verticalGradient[y] = new Array(img.width);
var row = lumas[y];
for (var x = 0; x < img.width; x++) {
var prevX = x == 0 ? 0 : lumas[y][x - 1];
var nextX = x == img.width - 1 ? 0 : lumas[y][x + 1];
var prevY = y == 0 ? 0 : lumas[y - 1][x];
var nextY = y == img.height - 1 ? 0 : lumas[y + 1][x];
horizontalGradient[y][x] = -prevX + nextX;
verticalGradient[y][x] = -prevY + nextY;
}
}
}
function makeVectors() {
// calculate direction and magnitude
vectors = new Array(img.height);
for (var y = 0; y < img.height; y++) {
vectors[y] = new Array(img.width);
for (var x = 0; x < img.width; x++) {
var prevX = x == 0 ? 0 : lumas[y][x - 1];
var nextX = x == img.width - 1 ? 0 : lumas[y][x + 1];
var prevY = y == 0 ? 0 : lumas[y - 1][x];
var nextY = y == img.height - 1 ? 0 : lumas[y + 1][x];
var gradientX = -prevX + nextX;
var gradientY = -prevY + nextY;
vectors[y][x] = {
mag: Math.sqrt(Math.pow(gradientX, 2) + Math.pow(gradientY, 2)),
dir: Math.atan2(gradientY, gradientX)
}
}
}
}
画像の結果
JavaScript で作成したフィールドは、Mathematica で作成したフィールドよりもノイズが多いです。
http://jsfiddle.net/frapporti/b4zxkcmL/
最後の注意事項
私はp5.jsを初めて使用します。おそらく、いくつかの節で車輪を再発明しています。これについてもお気軽に修正してください。