Reputation: 788
I am getting a figure from an imageData that looks like this:
How could I get the edges of that figure? so that it looks more or less like this:
I have tried to validate for each pixel that the previous, next, top and bottom pixel is transparent in order to save that pixel and then draw on another canvas
var context = canvas.getContext("2d");
var pixelData = context.getImageData(0, 0, canvas.width, canvas.height);
var result = []
for (let index = 0; index < pixelData.data.length; index += 4) {
if (pixelData.data[index] != 0) {
if ((pixelData.data[index + canvas.width * 4]) == 0 || (pixelData.data[index - canvas.width * 4]) == 0 || pixelData.data[index - 4] == 0 || pixelData.data[index + 4] == 0) {
var x = index / 4 % canvas.width
var y = (index / 4 - x) / canvas.width;
result.push({ x: x, y: y })
}
}
}
context.clearRect(0, 0, canvas.width, canvas.height)
context.beginPath()
result.forEach(point => {
context.lineTo(point.x, point.y)
})
context.strokeStyle = "#000"
context.stroke()
But this does not seem to work very well.
How could I obtain the edge or, failing that, the polygon that is formed in that figure?
Upvotes: 2
Views: 418
Reputation: 17614
Expanding on my question in the comments ...
Your if statement is detecting sequentially, so drawing lines on the order it was detected will not have the desired effect.
Below is some sample code, Just drawing letter "W"
and apply your algorithm to detect the edge, I added some transparency so you can see the output of your lines in action, it should look like:
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
context.font = "99px Arial";
context.fillStyle = "red"
context.fillText("W", 20, 70)
var pixelData = context.getImageData(0, 0, canvas.width, canvas.height);
var result = []
for (let index = 0; index < pixelData.data.length; index += 4) {
if (pixelData.data[index] != 0) {
if ((pixelData.data[index + canvas.width * 4]) == 0 || (pixelData.data[index - canvas.width * 4]) == 0 || pixelData.data[index - 4] == 0 || pixelData.data[index + 4] == 0) {
var x = index / 4 % canvas.width
var y = (index / 4 - x) / canvas.width;
result.push({ x, y })
}
}
}
context.strokeStyle = "blue"
context.globalAlpha = 0.5
context.beginPath()
result.forEach(point => {
context.lineTo(point.x, point.y)
})
context.stroke()
<canvas id="canvas" width=100 height=100></canvas>
...But if instead of lines we draw small dots, we get a very different picture.
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
context.font = "99px Arial";
context.fillStyle = "red"
context.fillText("W", 20, 70)
var pixelData = context.getImageData(0, 0, canvas.width, canvas.height);
var result = []
for (let index = 0; index < pixelData.data.length; index += 4) {
if (pixelData.data[index] != 0) {
if ((pixelData.data[index + canvas.width * 4]) == 0 || (pixelData.data[index - canvas.width * 4]) == 0 || pixelData.data[index - 4] == 0 || pixelData.data[index + 4] == 0) {
var x = index / 4 % canvas.width
var y = (index / 4 - x) / canvas.width;
result.push({ x, y })
}
}
}
context.strokeStyle = "blue"
context.globalAlpha = 0.5
result.forEach(point => {
context.beginPath()
context.arc(point.x, point.y, 1, 0, 2 * Math.PI)
context.stroke()
})
<canvas id="canvas" width=100 height=100></canvas>
You do have the edge in the var result
but that is not a polygon, so you can not do lines between the points or you get something else like we see in the first code snippet. If you want to transform that into a polygon, you will have to sort that array by the points proximity to each other, but that is another question...
Upvotes: 1