Related
I've prepared example for my case (just change something into js file for render map).
In short, I have source of map (coordinates, and sprites), and i need to draw this map.
I have following algorithm:
for(let x in data.region.draw_info){
const cellX = data.region.draw_info[x];
for(let y in cellX){
const cellY = cellX[y];
for(let cell of cellY){
ctx.drawImage(image, constans[cell[0]].x*1, constans[cell[0]].y*1, 32, 32, x*32, y*32, 32,32);
}
}
}
As you see, I use:
ctx.drawImage(image, constans[cell[0]].x, constans[cell[0]].y, 32, 32, x*32, y*32, 32,32);
Where: drawImage(image, space_source_x, space_source_y, size_cut_source_x, size_cut_source_y, space_canvas_x, space_canvas_y, size_put_canvas_x, size_put_canvas_y)
I guess, I filled right function drawImage, but I got cut of map (I have map 70x70, but I got render 9x5):
If I scale canvas, I will get full map, than my algorithm is right, but I think I have mistake in using drawImage
If you replace your line with the following, it draws all the images (but at a tiny scale)
ctx.drawImage(image, constans[cell[0]].x, constans[cell[0]].y, 2.2, 2.2, x*2.2, y*2.2, 2.2,2.2);
Im trying to create a convex hull with opencv.js based on an array with points, does anyone know a way to do this correctly and efficient? An array would look like this:
[
[5,5],
[10,10],
[15,15]
...
]
-> where the first value would be the x and the second the y value, but it wouldn't be a problem to change this format to something more suitable.
Thnx for the help :)
As far I could experiment OpenCV stores contour/hull data in Mat format with type CV_32SC2: essentially a flat list of 32bit short integers in [x1,y1,x2,y2,x3,y3,...] order.
Note the two channels/planes part of 32SC2: one channel for all the x values and another for all the y values
You can manually create such a Mat, access it's data32S property and fill in each value:
let testHull = cv.Mat.ones(4, 1, cv.CV_32SC2);
testHull.data32S[0] = 100;
testHull.data32S[1] = 100;
testHull.data32S[2] = 200;
testHull.data32S[3] = 100;
testHull.data32S[4] = 200;
testHull.data32S[5] = 200;
testHull.data32S[6] = 100;
testHull.data32S[7] = 200;
However OpenCV.js comes with a handy method to convert a flat array of values to such a Mat:
let testHull = cv.matFromArray(4, 1, cv.CV_32SC2, [100,100,200,100,200,200,100,200])
If your array is nested, you can simply use JS Array's flat() method to flatten it from a 2D array([[x1,y1]...]) to a 1D array ([x1,y1,...]).
So you don't have to worry about the Mat type and all that you can wrap it all into a nice function, for example:
function nestedPointsArrayToMat(points){
return cv.matFromArray(points.length, 1, cv.CV_32SC2, points.flat());
}
Here's a quick demo:
function onOpenCvReady(){
cv.then(test);
}
function nestedPointsArrayToMat(points){
return cv.matFromArray(points.length, 1, cv.CV_32SC2, points.flat());
}
function test(cv){
console.log("cv loaded");
// make a Mat to draw into
let mainMat = cv.Mat.zeros(30, 30, cv.CV_8UC3);
// make a fake hull
let points = [
[ 5, 5],
[25, 5],
[25,25],
[ 5,25]
]
let hull = nestedPointsArrayToMat(points);
console.log("hull data", hull.data32S);
// make a fake hulls vector
let hulls = new cv.MatVector();
// add the recently created hull
hulls.push_back(hull);
// test drawing it
cv.drawContours(mainMat, hulls, 0, [192,64,0,0], -1, 8);
// output to canvas
cv.imshow('canvasOutput', mainMat);
}
<script async src="https://docs.opencv.org/4.4.0/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
<canvas id="canvasOutput" width="30" height="30"></canvas>
Note that the above is a rough example, there's no data validation or any other fancier checks, but hopefully it illustrates the idea so it can be extended robustly as required.
Lets say that your points represent a contour:
var contours = new cv.MatVector();
for (var i = 0; i < points.size(); ++i) {
contours.push_back(new cv.Mat(points[i][0], points[i][1])
}
Now following this tutorial from opencv website:
// approximates each contour to convex hull
for (var i = 0; i < contours.size(); ++i) {
var tmp = new cv.Mat();
var cnt = contours.get(i);
// You can try more different parameters
cv.convexHull(cnt, tmp, false, true);
hull.push_back(tmp);
cnt.delete(); tmp.delete();
}
I have a question regarding the use of segmentation LUTs in AMI JS (not XTK but there is no ami js tag yet!). Particularly what I want to do is to load a segmentation / labelmap layer and display it with the right colors, one for each label.
My labelmap layer consists of N integer labels that define different structures (e.g from 0 to 14000), which are also the voxel values of the labelmap. Each one of the labels has a different color associated (they are generated by Freesurfer and can be seen on: https://surfer.nmr.mgh.harvard.edu/fswiki/FsTutorial/AnatomicalROI/FreeSurferColorLUT ).
What I would like is a LUT that, for each different label, paints it with the correspondant color. I have had trouble finding the right way to do it and have not had success so far. What I have done is to get all the colors and store them into an array (colors normalized between 0 and 1 and the first component being the position inside the texture from 0 to 1, with a step of 1/total labels, which results in a really small step as there are 1200 labels!). From what I've seen then, the HelpersLUT class takes all the colors and maps them discretely into the texture, but the colors appear messed up and I can't seem to get the opacities right either...
I have seen also that the StackModels also have some functionalities such as prepareSegmentation() and such but do not know how to specify the LUT in there and cannot get it to work either (it is not used on the example).
Which is the best way to create a discrete LUT with a different color for each integer label and the 0 value being transparent and the other labels opaque?
The procedure used to generate the LUTs is: First I read a JSON with the information of the Freesurfer and store it into a variable, the first component of each one is the index of the label between 0 and 1, and the other ones are the associated color to the label between 0 and 1 as well. I have also generated a LUT of opacities.
let customLUT = {
"fsLUT": [],
"default": [[0, 0, 0, 0], [0.25, 0.3, 0.4, 0.5], [0.5, 0.2, 0.5, 0.4],
[0.75, 0.1, 0.2, 0.3], [1, 0.5, 0.5, 0.8]],
"fsLUT0": [[0, 0], [0.01, 1], [0.6, 1], [1, 1]]
};
$.getJSON("https://cdn.rawgit.com/YorkeUtopy/ami-viewerData/e773d737/FreesurferInfo.json", function (data) {
FsInfo = data;
FsInfo.forEach(function (value, i) {
customLUT.fsLUT.push([i / FsInfo.length, (value.color[0] / 255), (value.color[1] / 255.000), (value.color[2] / 255.000)]);
});
});
Then I create a helpers LUT with the LUT0 defined and the LUT with the colors and apply it to the texture. Everythink else is just as the labelmap example createing the layer mix, etc...
lutLayerLblmap = new HelpersLut(
"my-lut-canvases-l1",
"default",
"linear", [[0, 0, 0, 0], [1, 1, 1, 1]],
customLUT.fsLUT0,
false
);
lutLayerLblmap.luts = customLUT;
lutLayerLblmap.lut = "fsLUT";
refObj.uniformsLayerLblmap.uLut.value = 1;
refObj.uniformsLayerLblmap.uTextureLUT.value = lutLayerLblmap.texture;
With that some colors appear but there are not correct and the opacities are messed up (I know the LUT0 is not correct and that it is not discrete!). However, when I make the helpersLUT discrete and put a LUT0 like [0,0],[1,1], the colors are messed up and the opacities do not apply correctly... maybe it is that the voxel values are not between 0 and 1 but have values such as 1100,1200... ? or that I am not correctly generating the LUTs (step size too small?).... Here are some examples of the LUT.
[0]: 0,0,0,0
[1]:0.0008319467554076539,0.27450980392156865,0.5098039215686274,0.7058823529411765
[2]:0.0016638935108153079,0.9607843137254902,0.9607843137254902,0.9607843137254902
[3]:0.0024958402662229617,0.803921568627451,0.24313725490196078,0.3058823529411765
[last -2]:0.997504159733777,0.08235294117647059,0.7058823529411765,0.7058823529411765
[last-1]:0.9983361064891847,0.8745098039215686,0.8627450980392157,0.23529411764705882
[last]:0.9991680532445923,0.8666666666666667,0.23529411764705882,0.23529411764705882
this is the sample data I use:
T1 Volume + Labelmap + Freesurfer JSON
You seem to be making everything fine.
It is a current limitation in AMI side.
It currently only supports 256 colors and on top of that, it requires values to be normalized.
In AMI, we need to support a new type of LUT (Segmentation LUT seems a good name).
Live fiddle based on you approach.
const fsLUT = [];
fetch("https://cdn.rawgit.com/YorkeUtopy/ami-viewerData/e773d737/FreesurferInfo.json")
.then(response => response.json())
.then(jsonLUT => {
jsonLUT.forEach(function (value, i) {
fsLUT.push([
i / json.length,
(value.color[0] / 255),
(value.color[1] / 255.000),
(value.color[2] / 255.000)]);
});
return fsLUT;
})
http://jsfiddle.net/agoyre4e/20/
I've built a name-[rgb] Javascript object. Your basic:
namedColors = {
AliceBlue: [240, 248, 255],
AntiqueWhite: [250, 235, 215],
...
object. But it occurred to me that I should be able to take a name string, "AliceBlue", say .. and have JavaScript find some sort of RGB representation of it (hex is fine). I know there are at least 140 named colors tucked away in the browser, but I can't seem to find them.
Is there a CSS or "style=..." stunt that lets me look up an RGB representation of a color name?
Minimal JavaScript function:
function nameToRgba(name) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.fillStyle = name;
context.fillRect(0,0,1,1);
return context.getImageData(0,0,1,1).data;
}
This is the solution I ended up with. I realized that colors came in two types: css strings and webgl typed arrays (usually 4 floats or ints, depending).
Hell with it, let the browser figure it: create a 1x1 canvas, fill it with any string color, grab the pixel, and destructure into an rgba array. There are two utilities below that create the 1x1 2d canvas ctx, attached.
# Return an RGB array given any legal CSS color, null otherwise.
# http://www.w3schools.com/cssref/css_colors_legal.asp
# The string can be CadetBlue, #0f0, rgb(255,0,0), hsl(120,100%,50%)
# The rgba/hsla forms ok too, but we don't return the a.
# Note: The browser speaks for itself: we simply set a 1x1 canvas fillStyle
# to the string and create a pixel, returning the r,g,b values.
# Warning: r=g=b=0 can indicate an illegal string. We test
# for a few obvious cases but beware of unexpected [0,0,0] results.
ctx1x1: u.createCtx 1, 1 # share across calls. closure wrapper better?
stringToRGB: (string) ->
#ctx1x1.fillStyle = string
#ctx1x1.fillRect 0, 0, 1, 1
[r, g, b, a] = #ctx1x1.getImageData(0, 0, 1, 1).data
return [r, g, b] if (r+g+b isnt 0) or
(string.match(/^black$/i)) or
(string in ["#000","#000000"]) or
(string.match(/rgba{0,1}\(0,0,0/i)) or
(string.match(/hsla{0,1}\(0,0%,0%/i))
null
What I love about it is that The Browser Speaks For Itself. Any legal string works just fine. Only downside is that if the string is illegal you get black, so need to do a few checks. The error checking is not great, but I don't need it in my usage.
The utility functions:
# Create a new canvas of given width/height
createCanvas: (width, height) ->
can = document.createElement 'canvas'
can.width = width; can.height = height
can
# As above, but returing the context object.
# Note ctx.canvas is the canvas for the ctx, and can be use as an image.
createCtx: (width, height) ->
can = #createCanvas width, height
can.getContext "2d"
Have a look into Colors.js with the functions "name2hex" and "name2rgb" this libary returns the hex or rgb values of your color name.
You can use a canvas to get the RGBA color from a name.
Please look at this fiddle: https://jsfiddle.net/AaronWatters/p1y298zk/19/
// We want to know the rgba values for this color name:
var testColor = "salmon"
// make a canvas
var canvas = $('<canvas width="100px" height="100px">');
// optional: display the canvas
var body = $(document.body);
canvas.appendTo(body);
// draw a rectangle on the canvas
var context = canvas[0].getContext("2d");
context.beginPath();
context.rect(0,0,100,100);
context.fillStyle = testColor;
context.fill();
// get the canvas image as an array
var imgData = context.getImageData(0, 0, 10, 10);
// rbga values for the element in the middle
var array = imgData.data.slice(50*4, 50*4+4);
// convert the opacity to 0..1
array[3] = array[3] / 255.0;
$("<div>The rgba for " + testColor + " is " + array + "</div>").appendTo(body);
This is how jp_doodle does color interpolation in transitions: https://aaronwatters.github.io/jp_doodle/080_transitions.html
Other approaches on this page use HTML5 Canvas.
But a straightforward alternative would be to derive the rgb() value from any color keyword using:
window.getComputedStyle()
Working Example:
const colorKeywordToRGB = (colorKeyword) => {
// CREATE TEMPORARY ELEMENT
let el = document.createElement('div');
// APPLY COLOR TO TEMPORARY ELEMENT
el.style.color = colorKeyword;
// APPEND TEMPORARY ELEMENT
document.body.appendChild(el);
// RESOLVE COLOR AS RGB() VALUE
let rgbValue = window.getComputedStyle(el).color;
// REMOVE TEMPORARY ELEMENT
document.body.removeChild(el);
return rgbValue;
}
// BASIC COLORS
console.log('red:', colorKeywordToRGB('red'));
console.log('green:', colorKeywordToRGB('green'));
console.log('yellow:', colorKeywordToRGB('yellow'));
console.log('blue:', colorKeywordToRGB('blue'));
// SIMPLE COLORS
console.log('fuchsia:', colorKeywordToRGB('fuchsia'));
console.log('lime:', colorKeywordToRGB('lime'));
console.log('maroon:', colorKeywordToRGB('maroon'));
console.log('navy:', colorKeywordToRGB('navy'));
console.log('olive:', colorKeywordToRGB('olive'));
console.log('purple:', colorKeywordToRGB('purple'));
console.log('teal:', colorKeywordToRGB('teal'));
console.log('transparent:', colorKeywordToRGB('transparent'));
// ADVANCED COLORS
console.log('blanchedalmond:', colorKeywordToRGB('blanchedalmond'));
console.log('coral:', colorKeywordToRGB('coral'));
console.log('darkorchid:', colorKeywordToRGB('darkorchid'));
console.log('firebrick:', colorKeywordToRGB('firebrick'));
console.log('gainsboro:', colorKeywordToRGB('gainsboro'));
console.log('honeydew:', colorKeywordToRGB('honeydew'));
console.log('papayawhip:', colorKeywordToRGB('papayawhip'));
console.log('seashell:', colorKeywordToRGB('seashell'));
console.log('thistle:', colorKeywordToRGB('thistle'));
console.log('wheat:', colorKeywordToRGB('wheat'));
Further Reading:
https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
Okay this is going to be hard to explain. So bear with me.
Im having less of a problem with the programming, and more a problem with the idea behind what Im trying to do.
I have a grid of triangles. Ref: http://i.imgur.com/08BPHiD.png [1]
Each triangle is it's own polygon on a canvas element that I have set as an object within the code. The only difference between the objects is the coordinates that I pass through as parameters of a function like so:
var triCoordX = [1, 2, 3, ...];
var triCoordY = [1, 2, 3, ...];
var triCoordFlipX = [1, 2, 3, ...];
var triCoordFlipY = [1, 2, 3, ...];
var createTri = function(x, y, z) {
return {
x: x,
y: y,
sides: 3,
radius: 15,
rotation: z,
fillRed: 17,
fillGreen: 17,
fillBlue: 17,
closed: true,
shadowColor: '#5febff',
shadowBlur: 5,
shadowOpacity: 0.18
}
};
for (i = 0; i < triCoordX.length; i++){
var tri = new Kinetic.RegularPolygon(createTri(triCoordX[i], triCoordY[i], 0));
}
for (i = 0; i < triCoordFlipX.length; i++){
var triFlip = new Kinetic.RegularPolygon(createTri(triCoordFlipX[i], triCoordFlipY[i], 180));
}
Now what Im trying to do exactly is have each object polygon be able to 'recognise' its neighbors for various graphical effects.
How I propose to do this is pass a 4th parameter into the function that I push from another array using the for loop that sets a kind of "index" for each polygon. Also in the for loop I will define a function that points to the index 'neighbors' of the object polygon.
So for instance, if I want to select a random triangle from the grid and make it glow, and on completion of a tween want to make one of it's neighbors glow I will have the original triangle use it's object function to identify a 'neighbor' index and pick at random one of its 3 'neighbors'.
The problem is with this model, Im not entirely sure how to do it without large amounts of bloat in my programming, or when I set the function for the loop, to set a way for the loop to intuitively pick the correct index numbers for what are actually the triangle's neighbors.
If all of that made sense, Im looking for any and all suggestions.
Think of your triangles as being laid out in a grid with the triangle in the top left corner being col==0, row==0.
Then you can find the row/col coordinates of the 3 neighbors of any triangle with the following function.
Ignore any neighbors with the following coordinates because the neighbors would be off the grid.
col<0
row<0
col>ColumnCount-1
row>RowCount-1
Example code (warning...untested code--you may have to tweak it):
function findNeighbors(t){
// determine if this triangle's row/col are even or odd
var evenRow=(t.col%2==0);
var evenCol=(t.row%2==0;
// left neighbor is always the same
n1={ col:t.col-1, row:t.row };
// right neighbor is always the same
n2={ col:t.col+1, row:t.row };
// third neighbor depends on row/col being even or odd
if(evenRow && evenCol){
n3={ col:t.col, row:t.row+1 };
}
if(evenRow && !evenCol){
n3={ col:t.col, row:t.row-1 };
}
if(!evenRow && evenCol){
n3={ col:t.col, row:t.row-1 };
}
if(!evenRow && !evenCol){
n3={ col:t.col, row:t.row+1 };
}
// return an array with the 3 neighbors
return([n1,n2,n3]);
}