How can I use images in Plotly instead of just coloured dots? - plotly.js

I was trying the examples in the documentation and they are relatively easy. And it makes sense to use images or at least one image instead of those coloured dots. What I tried so far is this:
const x = Array.from({ length: 500, }, () => Math.random() * (6 - 3) + 3);
const y = Array.from({ length: 500, }, () => Math.random() * (6 - 3) + 3);
const data = [{
x,
y,
type: 'image',
mode: 'markers',
source: icon,
name: 'Some Image',
showlegend: true
}];
Plotly.newPlot('chart', data);
Here I am trying to render images instead of dots, but all I get is an image in the entire graph. How can I make every individual dot be the actual image. And here I mean icons, to be more precise, I want to use a wheel icon - 🎡.

All I had to add was this:
marker: {symbol: "hexagon2-dot"}

Related

How to interpret result of MLMultiArray in semantic segmentation through CoreML?

I am trying to implement a semantic segmentation model in my application. I have been able to convert the u2net model to a CoreML model. I am unable to get a workable result from the MLMultiArray output. The specification description is as follows:
input {
name: "input"
type {
imageType {
width: 512
height: 512
colorSpace: RGB
}
}
}
output {
name: "x_1"
type {
multiArrayType {
shape: 1
shape: 3
shape: 512
shape: 512
dataType: FLOAT32
}
}
}
The model works great when opening it and using the model preview functionality in Xcode. It shows the 2 different labels in 2 colours (there are only 2 classes + 1 background). I want to have the same output in my application, however when I manually process the MLMultiArray output to a CGImage I get different results. I am using the code provided here like this:
let image = output.cgImage(min: -1, max: 1, channel: 0, axes: (1,2,3))
This gives me something that looks somewhat usable but it has a lot of gradient within each channel. What I need is an image with simply 1 color value for each label.
I have tried converting the output of the model directly to an image through this sample code. This simply shows 'Inference Failed' in the Xcode model preview. When I try removing the unnecessary extra dimension in the MultiArray output I get this error:
"Error reading protobuf spec. validator error: Layer 'x_1' of type 'Convolution' has output rank 3 but expects rank at least 4."
What does the model preview in Xcode do what I am not doing? Is there a post processing step I need to take to get usable output?
Answering my own question:
Turns out the resulting pixels for each channel represent the possibility of it being the class represented by that channel.
In other words find the maximum pixel value at a certain position. The channel with the highest value is the class pixel.
func getLabelsForImage() {
....
setup model here
....
guard let output = try? model.prediction(input: input) else {
fatalError("Could not generate model output.")
}
let channelCount = 10
// Ugly, I know. But works:
let colors = [NSColor.red.usingColorSpace(.sRGB)!, NSColor.blue.usingColorSpace(.sRGB)!, NSColor.green.usingColorSpace(.sRGB)!, NSColor.gray.usingColorSpace(.sRGB)!, NSColor.yellow.usingColorSpace(.sRGB)!, NSColor.purple.usingColorSpace(.sRGB)!, NSColor.cyan.usingColorSpace(.sRGB)!, NSColor.orange.usingColorSpace(.sRGB)!, NSColor.brown.usingColorSpace(.sRGB)!, NSColor.magenta.usingColorSpace(.sRGB)!]
// I don't know my min and max output, -64 and 64 seems to work OK for my data.
var firstData = output.toRawBytes(min: Float32(-64), max: Float32(64), channel: 0, axes: (0,1,2))!.bytes
var outputImageData:[UInt8] = []
for _ in 0..<firstData.count {
let r:UInt8 = UInt8(colors[0].redComponent * 255)
let g:UInt8 = UInt8(colors[0].greenComponent * 255)
let b:UInt8 = UInt8(colors[0].blueComponent * 255)
let a:UInt8 = UInt8(colors[0].alphaComponent * 255)
outputImageData.append(r)
outputImageData.append(g)
outputImageData.append(b)
outputImageData.append(a)
}
for i in 1..<channelCount {
let data = output.toRawBytes(min: Float32(-64), max: Float32(64), channel: i, axes: (0,1,2))!.bytes
for j in 0..<data.count {
if data[j] > firstData[j] {
firstData[j] = data[j]
let r:UInt8 = UInt8(colors[i].redComponent * 255)
let g:UInt8 = UInt8(colors[i].greenComponent * 255)
let b:UInt8 = UInt8(colors[i].blueComponent * 255)
let a:UInt8 = UInt8(colors[i].alphaComponent * 255)
outputImageData[j*4] = r
outputImageData[j*4+1] = g
outputImageData[j*4+2] = b
outputImageData[j*4+3] = a
}
}
}
let image = imageFromPixels(pixels: outputImageData, width: 512, height: 512)
image.writeJPG(toURL: labelURL.deletingLastPathComponent().appendingPathComponent("labels.jpg"))
}
// I found this function here: https://stackoverflow.com/questions/38590323/obtain-nsimage-from-pixel-array-problems-swift
func imageFromPixels(pixels: UnsafePointer<UInt8>, width: Int, height: Int)-> NSImage { //No need to pass another CGImage
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
let bitsPerComponent = 8 //number of bits in UInt8
let bitsPerPixel = 4 * bitsPerComponent //ARGB uses 4 components
let bytesPerRow = bitsPerPixel * width / 8 // bitsPerRow / 8 (in some cases, you need some paddings)
let providerRef = CGDataProvider(
data: NSData(bytes: pixels, length: height * bytesPerRow) //Do not put `&` as pixels is already an `UnsafePointer`
)
let cgim = CGImage(
width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: bytesPerRow, //->not bits
space: rgbColorSpace,
bitmapInfo: bitmapInfo,
provider: providerRef!,
decode: nil,
shouldInterpolate: true,
intent: CGColorRenderingIntent.defaultIntent
)
return NSImage(cgImage: cgim!, size: NSSize(width: width, height: height))
}

How to find text bounds for multilined text in P5.js?

I need to create autosized text setting some box size:
text(textContent, textX, textY, textWidth, textHeight);
The problem is that the text is multilined because it can't place on one line. Ok, then I've tried to check textBounds - but it works only with one line of string. How to find bounds for text drawing in some box/rect (textWidth, textHeight)?
I am having a little difficulty understanding your question. But if you wanted to create a box that encompassed all the text you could implement something like the following:
Lets assume we have our text stored to objects like this to make the logic easier to read:
var text1 = {
"str" : "This Is Line One",
X1: 0,
Y1: 0,
X2: 300,
Y2: 100,
size:30
}
var text2 = {
"str" : "This Is Line 2",
X1: 0,
Y1: 30,
X2: 300,
Y2: 100,
size:20
}
X1,Y1 is the upper left coordinate, and X2,Y2 is the lower right.
Now we have a data model to work with. We simply have to combine the bounds into a single rectangle.
Remember:
Rectangles are Declared like this:
Rect(X,Y,Width,Height)
Which is exactly the information textBounds() gives us!
Here is a quick and dirty example of the math:
function draw() {
textSize(this.text1.size);
text(this.text1.str,this.text1.X1,this.text1.Y1,this.text1.X2,this.text1.Y2);
textSize(this.text2.size);
text(this.text2.str,this.text2.X1,this.text2.Y1,this.text2.X2,this.text2.Y2);
//Get Text Bounds object for each text
let text1box = this.font.textBounds(this.text1.str,this.text1.X1,this.text1.Y1,this.text1.size)
let text2box = this.font.textBounds(this.text2.str,this.text2.X1,this.text2.Y1,this.text2.size)
//Get the upper-left and lower-right coordinates of the bounding rectangle of all the text
let bounds = {
x1: min(text1box.X,text2box.X), //upper left x
y1: min(text1box.Y,text2box.Y), //uper left y
x2: max(text1box.x + text1box.w,text2box.x + text2box.w), //lower right x
y2: max(text1box.y + text1box.y,text2box.y + text2box.h) //lower right y
}
//if you want padding:
let padding = 2;
rect(
bounds.x1 - padding,
bounds.y1 - padding,
abs(bounds.x2, - bounds.x1) + padding, //Width of the bounding rectangle
abs(bounds.y2 - bounds.y1) + padding //Height of the bounding rectangle
)
}
To fully show the logic here is a quick diagram I made
The cool thing about this logic is that even if the text isn't lined up perfectly, it is still able to put a box around all the text.

Using Three.js to put a pin on a map of the USA

I'm trying to figure out a way to use latitude and longitude to put a pin on a map of the USA.
I'm using a perspective camera btw.
This is my mesh, which basically adds a color map, and a displacement map to give it some height:
const mapMaterial = new MeshStandardMaterial({
map: colorTexture,
displacementMap: this.app.textures[displacementMap],
metalness: 0,
roughness: 1,
displacementScale: 3,
color: 0xffffff,
//wireframe: true
})
const mapTextureWidth = 100
const mapTextureHeight = 100
const planeGeom = new PlaneGeometry(mapTextureWidth, mapTextureHeight, mapTextureWidth - 1, mapTextureHeight - 1)
this.mapLayer = new Mesh(planeGeom, mapMaterial)
this.mapLayer.rotation.x = -1
this.mapLayer.position.set(0, 0, 0); // set the original position
I've also added a camera to give it a slight tilt so we can see the height in the mountains and such.
In the end it looks like this:
What I need to do is add a map pin on the map by using latitude and longitude.
I've played around with converting lat and long to pixels, but that gives me an x and y relative to the screen, and not the map itself, (found this in a different SO post):
convertGeoToPixelPosition(
latitude, longitude,
mapWidth , // in pixels
mapHeight , // in pixels
mapLonLeft , // in degrees
mapLonDelta , // in degrees (mapLonRight - mapLonLeft);
mapLatBottom , // in degrees
mapLatBottomDegree
) {
const x = (longitude - mapLonLeft) * (mapWidth / mapLonDelta);
latitude = latitude * Math.PI / 180
const worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI)
const mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree))))
const y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitude)) / (1 - Math.sin(latitude)))) - mapOffsetY)
return { "x": x , "y": y}
}
Any thoughts on how I can transform the latitude and longitude to world coordinates?
I've already created the sprite for the map pin, and adding them works great, just have to figure out the proper place to put them....
Add your marker as a child of the mapLayer...
this.mapLayer.add( marker )
then set its position:
marker.position.set( (x/4096)-0.5)*100, (y/4096)-0.5)*100, 0)
where x and y are what you get from your convertGeo function.

How do I draw horizontal bars with a label using either ChartJS or D3?

What's the best way of drawing multiple horizontal lines and labels for a simple line graph in either ChartJS or D3? I know that I could draw these as individual lines and then do a text overlay but I'm wondering if there is a simpler solution. Ideally I'd be able to create each of the labels below as one unit and move it anywhere.
If this is simpler in another JS graph library then feel free suggest.
Example below
To do it with Chart.js you have to extend the line chart
Chart.types.Line.extend({
name: "LineAlt",
initialize: function (data) {
// it's easier to programmatically update if you store the raw data in the object (vs. storing the geometric data)
this.marks = data.marks;
this.marks.xStart = Number(data.labels[0]);
this.marks.xStep = data.labels[1] - data.labels[0];
// make sure all our x labels are uniformly apart
if (!data.labels.every(function (e, i, arr) { return !i || ((e - arr[i - 1]) === this.marks.xStep); }, this))
throw "labels must be uniformly spaced";
Chart.types.Line.prototype.initialize.apply(this, arguments);
},
draw: function () {
Chart.types.Line.prototype.draw.apply(this, arguments);
// save existing context properties
var self = this;
var ctx = self.chart.ctx;
var scale = self.scale;
ctx.save();
// line properties
ctx.lineWidth = 1;
ctx.fillStyle = "#666";
ctx.strokeStyle = "#666";
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
ctx.font = scale.font;
// draw marks
self.marks.forEach(function (mark) {
// assuming that the marks are always within the data range
var y = scale.calculateY(mark.y);
var x1 = scale.calculateX((mark.x1 - self.marks.xStart) / self.marks.xStep);
var x2 = scale.calculateX((mark.x2 - self.marks.xStart) / self.marks.xStep);
// draw line
ctx.beginPath();
ctx.moveTo(x1, y);
ctx.lineTo(x2, y);
// draw edges
ctx.moveTo(x1, y + 10);
ctx.lineTo(x1, y - 10);
ctx.moveTo(x2, y + 10);
ctx.lineTo(x2, y - 10);
ctx.stroke();
// draw text
ctx.fillText(mark.label, (x1 + x2) / 2, y + scale.fontSize * 1.5);
})
ctx.restore();
},
});
You pass in the data for drawing the lines like so
var data = {
...
marks: [
{
x1: 1.5,
x2: 3.5,
y: 50,
label: 'Label1'
},
{
x1: 5,
x2: 7,
y: 60,
label: 'Label2'
}
]
};
and you create the chart using this extended chart type
var myLineChart = new Chart(ctx).LineAlt(data);
You can update the lines like this
myLineChart.marks[0].y = 80;
myLineChart.marks[0].x1 = 9;
myLineChart.marks[0].x2 = 10;
and then call
myLineChart.update();
to reflect those changes on the canvas
Caveats
The (x axis) labels should be numeric and uniformly spaced.
The lines should be within the scale range of the y axis (alternatively you can do a scaleOverride to set the scale parameters so that the lines are within the y scale range)
Fiddle - http://jsfiddle.net/en92k763/2/

How to convert a cubic bezier value to a custom mina easing (snap.svg)

I'm trying to create custom easing for a snap.svg animation. I looked at the documentation (http://snapsvg.io/docs/#mina), but it's not clear to me how I could convert a CSS3 style cubic-bezier (for example: cubic-bezier(0.17, 0.67, 0.25, 0.99)) to custom easing with Snap.svg
I think you would firstly need a cubic-bezier function, then its relatively simple to include. (Note I don't pretend to understand any of the cubic code, just trying to highlight how to use it, I also don't know how good the example is).
I'm not sure if I'm allowed to post someone elses code on here really, and it makes sense to reference git in case its updated, so I have included the link, and displaying mainly how you would use it here.
Example jsfiddle
Github code I am using for the example here Read the doc there on how best to use it.
Define your cubic-bezier func...(code here from link above)
var cubicBezier = function(x1, y1, x2, y2, epsilon){
var curveX = function(t){
...
}
} // end of cubic-bezier function, see code on github link
// Create the specific bezier easing func...
var duration = 200;
var epsilon = (1000 / 60 / duration) / 4;
var timingFunction = cubicBezier(0.08, 1.05, 0.95, 0.12, epsilon);
var timingFunction2 = cubicBezier(0.5, 0.5, 0.5, 0.5, epsilon );
// Now the Snap stuff
var paper = Snap(600, 600);
var r = paper.rect(0,0,200, 200).attr({fill: "blue" });
var r2 = paper.rect(0,200,200,200).attr({ fill: "green" });
r.animate({ x: 200 }, 1000, timingFunction );
r2.animate({ x: 200 }, 1000, timingFunction2 );
As you can see in the last 2 lines, we include the custom easing function in the animate call.

Resources