Combining 3D LUTs with each other in javascript - javascript

I am working with 3D color LUTs (color lookup tables) in javascript and I was wondering is there a way to combine two or more 3D LUTs to export them in one single file.
Let me explain:
I get .cube (3D color lookup file). I parse it and store parsed color values into an array and apply it to existing image. After that I apply new 3D LUT onto existing (changed) image, and I apply new LUT once more. So now I have original image with 3 different 3D LUTs applied onto each other.
Now, I can successfully export every 3D LUT in separate file and download it, but I don't know how to combine them into a single .cube file.
I believe I need some algorithm for "combining" different LUTs into one file?
This is example how photoshop does it:
LUT1:
0.024536 0.000183 0.000244
0.049103 0.000336 0.000458
LUT2:
0.041260 0.021149 0.009125
0.067230 0.023804 0.009125
COMBINED LUT (result):
0.035034 0.020660 0.009308
0.054810 0.022766 0.009430
Thank you!

After some research I have found a solution.
Essentially, I needed to pipe the output of the first LUT into the input of the second LUT. This requires to have an interpolation function in-program (not just a 3D LUT shader).
Process goes something like this:
Create a new identity LUT of a chosen size (default LUT with no changes)
Iterate through every point of that 3D LUT and pipe the identity color of each point through the first LUT's ColorFromColor, and then through the second LUT's ColorFromColor. Store the final value in the new LUT.
Function looks something like this:
function mapColorsFast(out, image, clut, clutMix){
let od = out.data,
id = image.data,
w = out.width,
h = out.height,
cd = clut.data,
cl = Math.floor(Math.pow(clut.width, 1/3)+0.001),
cs = cl*cl,
cs1 = cs-1;
var x0 = 1 - clutMix, x1 = clutMix;
for(var y = 0; y < h; y++) {
for(var x = 0; x < w; x++) {
let i = (y*w+x)*4,
r = id[i]/255*cs1,
g = id[i+1]/255*cs1,
b = id[i+2]/255*cs1,
a = id[i+3]/255,
ci = (dither(b)*cs*cs+dither(g)*cs+dither(r))*4;
od[i] = id[i]*x0 + x1*cd[ci];
od[i+1] = id[i+1]*x0 + x1*cd[ci+1];
od[i+2] = id[i+2]*x0 + x1*cd[ci+2];
od[i+3] = a*255;
}
}
}
Function accepts few arguments:
out - buffer into which the result is written
image - a buffer containing the image in imageData format
clut - color LUT that we're applying to the image
clutMix - affecting the strength of the effect (0-1)
In this case, we needed to create identity LUT, save it as image and pass it as image argument to the function, and then apply new LUT onto it. Then we pipe the result again into same function and apply new LUT onto it. We do that for every LUT we want to mix with other LUTs.
I found this function on https://github.com/jwagner/analog-film-emulator/blob/master/src/image-processing.js - Javascript Film Emulation project.
There's a lot of interesting material to find if you're working with canvas 2d image processing, and there's also a working example included: https://29a.ch/film-emulator/
Hope it will help someone in the future!

Related

Photoshop scripting with JSX in for loop to change fill color of each layer

So, I am tinkering with changing fill colors of layers in a PSD file using JSX. I ultimately want to loop all layers, turning off visibility for all but one, edit the fill color of that layer, save as PNG, and then repeat for all layers and all colors in JSON file. I'm starting small as this is my first attempt, but if your solution can help preempt my downfalls with other tasks then it would be greatly appreciated. Here is what I have (alert properly prompts, but line 5 receives error 1302: no such element referencing line 5):
var layerNum = app.activeDocument.layers.length
alert(layerNum);
var i;
for (i=0;i<layerNum;i++){
var currentLayer = app.activeDocument.layers.index(i)
var myColor = new SolidColor();
//var RGB = HEXtoRGB(Y);
myColor.rgb.red = RGB[255];
myColor.rgb.green = RGB[0];
myColor.rgb.blue = RGB[0];
currentLayer.fill.color = myColor;
}
Is this because the collection of layers does not start at 0? Should I start with layers.index(layerNum) and use i-- to move down the collection? Any insight would be helpful. Thanks in advance to this always helpful community.
This var currentLayer = app.activeDocument.layers.index(i)
should be this: var currentLayer = app.activeDocument.layers[i]
Layers collections are pretty much the same as arrays, starting from 0, so you did it right
As KienT points out you need to use square brackets for layers.
Also it's useful to create a variable that's the app.activeDocument, so you don't have to type that out each time. Looping over the layers backwards (or up) is easy - just remember to take 1 off the layers length. You can adjust also make it ignore the background by making the loop start at 1 instead of 0. You can also adjust the layers visibility from true to false (on and off) as you go.
// call the source document
var srcDoc = app.activeDocument;
var layerNum = srcDoc.layers.length;
// alert(layerNum);
for (var i = layerNum -1; i >= 0; i--)
{
var currentLayer = srcDoc.layers[i];
var myColor = new SolidColor;
// Select the layers as you go
srcDoc.activeLayer = srcDoc.artLayers[i];
// switch layer visibility to on
srcDoc.visible = true;
myColor.rgb.red = 255;
myColor.rgb.green = 0;
myColor.rgb.blue = 0;
app.activeDocument.selection.fill(myColor, ColorBlendMode.NORMAL, 100, false);
}

javascript - 2D array initilisation speed

I'm making a 2D isometric game where the tile map is held in a 2D array. Here's what a 16*16 map looks like just so you get the idea:
Before the game loop begins the size and contents of the map array must be initialized and I have noticed that this is one of the biggest bottlenecks when loading the program. If the map is 5000*5000 it can take about 7 seconds on my relatively high end computer (which i figure seems pretty slow).
Is this more a problem with the speed of javascript or is my code doing something wildly inefficient?
The array which holds the tile contents is initialized like so:
// Create Array to represent tile contents
mapArray = new Array(mapWidth);
for (var i = 0; i < mapWidth; i++) {
mapArray[i] = new Array(mapHeight);
for (var j = 0; j < mapHeight; j++) {
mapArray[i][j] = new Tile("water", 0, false);
}
}
and the objects which that array hold is the tile class below:
// Tile class
function Tile(type, height, mouseOver) {
// Add object properties like this
this.type = type;
this.height = height;
this.mouseOver = mouseOver;
}
If you're registering individual event listeners to each tile, that could certainly slow down your program. Consider cutting down the number of listeners by only listening to mouseover/mousemove events on the container, and then deriving the tile from the (x, y) coordinates of the mouse.

Manipulating 2 image data objects together

Basically, I am trying to achieve a high-pass filter for my canvas application. The concept is simple: take the regular image pixels minus the pixels of the same image blurred with a radius of x.
Seems simple, right?
var d = pixel.data;
var blurdata = blur(amnt, pixel);
var bd = blurdata.data;
for (var i=0; i<d.length; i+=4) {
d[i] = 128+(d[i]-bd[i]);
d[i+1] = 128+(d[i+1]-bd[i+1]);
d[i+2] = 128+(d[i+2]-bd[i+2]);
}
return pixel;
The code is run with 2 parameters, amnt and pixel. Pixel is the ctx.getImageData object, and amnt is the blur radius. The blur function returns an imagedata object as well. The blur code is not the issue though. That works 100% as expected. What the problem is, is that it seems that the d and bd variables are the same. I'm not sure why. When bd[i] is subtracted from d[i] the result is 0, which when added to 128 makes for a completely gray image when the data is put back into the canvas. Oh, and the blurdata and pixel data have the same dimensions.
Any help really appreciated.
Blur function: http://www.files.croar.net/f/9/
Your data is the same because your data is the same. When you blur pixel.data in the blur function it is also changing the values in the array d. What you'll probably want to do is make two canvases with the same image. Have d be the pixel data for the unblurred image and then blur the data for the other image:
var composite = canvas1.getContext("2d").getImageData();
var d = composite.data;
var blurdata = blur(amnt, pixel);
var bd = blurdata.data;
for (var i=0; i<d.length; i+=4) {
d[i] = 128+(d[i]-bd[i]);
d[i+1] = 128+(d[i+1]-bd[i+1]);
d[i+2] = 128+(d[i+2]-bd[i+2]);
}
return composite;
I think that should get the job done.

How to Manipulate a line in Raphael to add/edit points on the line

I have quite a few years experience coding but very minimal javascript and raphael. I've been looking online and in a book I bought on raphael. Part of what I am trying to do is to have a line / path and provide the user the capability to add or edit points to this line.
Something similar to the route on google maps.
I know that a path can be constructed using arrays. I believe those elements can have a unique id that will help accessing them.
I'm thinking something similar to one of the approaches suggested in this question, in particular Adam MoszczyƄski's approach with regards to separating the drawing from the data; especially as down the road it's likely that this path data will need to be persisted/loaded.
The SVG path syntax is fairly simple if all you want to do is draw straight lines. You really only need to know two commands: M to set the location of the first point on the path and L to draw a line to the rest of the points.
For example, to draw the following polyline:
[5,2]___________[12,2]
/ \
/ \
/ [13,5]
/
[1,10]
you would use the following SVG path:
M 1 10 L 5 2 L 12 2 L 13 5
Note that SVG accepts , as number separators and spaces between commands and arguments are optional if they're unambiguous. So the above path can also be written as:
M 1,10 L 5,2 L 12,2 L 13,5
or even:
M1,10L5,2L12,2L13,5
But the first form is the most convenient for us to use programatically because it can simply be constructed by joining array elements with the space character:
var svgpath = [
'M', 1, 10,
'L', 5, 2,
'L', 12, 2,
'L', 13, 5
].join(' ');
Given this, it's easy to write a function that draws a path in Raphael. There are two ways this can be done:
Write your own independent line drawing function/library and use Raphael just as a tool to draw to screen. The simplest is just a function that draws the polyline.
function draw_polyline(paper,coords) {
var first = coords.shift();
var path = ['M', first[0], first[1]];
for (var i=0; i<coords.length; i++) {
path.push('L',coords[i][0],coords[i][1]);
}
return paper.path(path.join[' ']);
}
draw_polyline(paper,[[1,10],[5,2],[12,2],[13,5]]);
Write it as an extension to Raphael. The function is exactly the same but behaves as if it's part of Raphael:
Raphael.fn.polyline = function (coords) {
var first = coords.shift();
var path = ['M', first[0], first[1]];
for (var i=0; i<coords.length; i++) {
path.push('L',coords[i][0],coords[i][1]);
}
return this.path(path.join[' ']);
}
var paper = Raphael('div',640,480);
paper.polyline([[1,10],[5,2],[12,2],[13,5]]);
Note that the simple function above simply draws the line from the given coordinates. To add or remove points to the line you'll have to redraw the path by calling the polyline function with the updated coordinates. This works but is not exactly ideal since you'll have to keep track of the old line and delete it when you draw the new line.
A better solution is to have the line update itself. To do this we'll need to refactor the code above to separate the svg path generation from the drawing function:
function polyline_path(coords) {
var first = coords.shift();
var path = ['M', first[0], first[1]];
for (var i=0; i<coords.length; i++) {
path.push('L',coords[i][0],coords[i][1]);
}
return path.join(' ');
}
then the Raphael implementation is simply:
Raphael.fn.polyline = function (coords) {
return this.path(polyline_path(coords));
}
Now we can add some functionality to the Raphael path object that represents our polyline:
Raphael.fn.polyline = function (coords) {
var path = this.path(polyline_path(coords));
path.coords = function(xy) {
if (!xy) {return coords} // return coordinates if no arguments given
coords = xy;
path.attr('path',polyline_path(coords)); // otherwise update svg path
}
return path;
}
Now we can use it like this:
var route = paper.polyline([[1,10],[5,2],[12,2],[13,5]]);
// Add point to route:
route.coords(route.coords().push([15,10]));
That's still a bit ungainly but you get the idea. You can use this basic concept to implement more advanced API. For example, it would be nice if the polyline object implement most of the Array methods so you can simply do:
route.push([15,10]); // add point to route
route.shift(); // remove point from beginning of route
route.splice(1,1,[5,3]); // modify the second point
// etc..
It's really up to you how far you want to take this.

Javascript - Connect two lines

In the following picture:
alt text http://rookery9.aviary.com.s3.amazonaws.com/4478500/4478952_3e06_625x625.jpg
I want to connect the boxes in the above with below, Let us call the bottom edge of the top boxes as A and top edge of the below boxes as B
Now, I have two arrays containing the points in the line A and B say
A = [ {Ax1, Ay1},{Ax2, Ay2},.... ] and B = [ {Bx1, By1},{Bx2, By2},.... ]
In real world it can be like A = [ {100, 100},{120, 100},{140, 100},{160, 100}] and B=[ {120, 200},{140, 200},{160, 200},{180, 200},{200, 200},]
Please look at the black dots in the picture above
How can I get the connectiong points as shown in the pictures? Connecting point must be as close to the center of the line as possible.
Here is what I'm trying to get, but below functions draw line between the two matching points from the starting from the left of the both lines, Any suggessions
drawConnection : function(componentOut, componentIn, connectionKey) {
var outDim = $(componentOut).data('dim');
var inDim = $(componentIn).data('dim');
var outPorts = $(componentOut).data('ports');
var inPorts = $(componentIn).data('ports');
var abovePorts = {};
var belowPorts = {};
var i = 0;
if(outDim.bottomLeft.y < inDim.topLeft.y){
// Now proceed only if they can be connect with a single line
if(outDim.bottomLeft.x < inDim.topRight.x && outDim.bottomRight.x>inDim.topLeft.x) {
// Now get a proper connecting point
abovePorts = outPorts.bottom;
belowPorts = inPorts.top;
for(i=0; i<abovePorts.length; i++) {
for(j=0; j<belowPorts.length; j++) {
if(!abovePorts[i].inUse && !belowPorts[j].inUse && (abovePorts[i].x == belowPorts[j].x)){
console.debug("Drawing vertical lines between points ("+abovePorts[i].x+","+abovePorts[i].y+") and ("+abovePorts[i].x+","+belowPorts[j].y+")");
return true;
}
}
}
}
}
return false;
},
-- Update
I'm exactly trying to get something similar to this http://raphaeljs.com/graffle.html, but the connections should be made with straight lines as shown below
alt text http://rookery9.aviary.com.s3.amazonaws.com/4480500/4480527_1e77_625x625.jpg
Have you tried Raphael.js : http://raphaeljs.com/ ?
Another approach is to use the HTML+CSS engine of the browser, instead of using JS.
You can use a table.
One cell row for each box and a 2 cells row for the connector.
You color one of the border for the connector and use margin, float and width styles, to position the boxes.
I've already used this technique to draw org charts a long time ago... when IE6 was considered the best browser!
Another worth looking at is Processing.js if you want a bit more power. I've used Raphael.js before and that was pretty easy to pickup and use. Just be aware that both utilize the Canvas element which to my knowledge isn't supported in all browsers yet.

Categories