15

Is there a algorithm to get the line strokes of a image (ignore curves, circles, etc., everything will be treated as lines, but still similiar to vectors), from their pixels? Then get a result of them, like a Array?

This is how it'd basically work to read

Basic read

In this way, each row of pixel would be read as 1 horizontal line and I'd like to handle vertical lines also; but if there's a round fat line that takes more than 1 row

round fat line

it'll be considered one line. Its line width is the same height of pixels it has.

For instance, let's suppose we've a array containing rows of pixels in the (red, green, blue, alpha) format (JavaScript):

/* formatted ImageData().data */
[
    new Uint8Array([
        /* first pixel */
        255, 0, 0, 255,
        /* second pixel */
        255, 0, 0, 255
    ]),

    new Uint8Array([
        /* first pixel */
        0, 0, 0, 0,
        /* second pixel */
        0, 0, 0, 0
    ])
]

This would be a 2x2px image data, with a straight horizontal red line. So, from this array, I want to get a array containing data of lines, like:

[
    // x, y: start point
    // tx, ty: end point
    // w: line width

    // the straight horizontal red line of 1 pixel
    { x: 0, y: 0, tx: 2, ty: 0, w: 1, rgba: [255, 0, 0, 255] }
]

Note: I'd like to handle anti-aliasing.

This is my function to read pixels in the above format:

var getImagePixels = function(img){
    var canvas = document.createElement('canvas'),
        ctx = canvas.getContext('2d');

    canvas.width = img.width;
    canvas.height = img.height;

    ctx.drawImage(img, 0, 0);

    var imgData = ctx.getImageData(0, 0, img.width, img.height).data;
    var nImgData = [];
    var offWidth = img.width * 4;

    var dataRow = (nImgData[0] = new Uint8Array(offWidth));

    for (var b = 0, i = 0; b++ < img.height;) {
        nImgData[b] = new Uint8Array(offWidth);

        for (var arrI = 0, len = i + offWidth; i < len; i += 4, arrI += 4) {
            nImgData[b][arrI] = imgData[i];
            nImgData[b][arrI + 1] = imgData[i + 1];
            nImgData[b][arrI + 2] = imgData[i + 2];
            nImgData[b][arrI + 3] = imgData[i + 3];
        }
    }

    return nImgData;
};
  • 2
    I think what you may want is an edge filter. – jedifans Aug 29 '16 at 21:57
  • @jedifans Can you tell me how would I filter edges? I'm pretty confuse about that –  Aug 29 '16 at 21:58
  • 1
    That one I'm not sure about, sorry. Hopefully someone else can help :) – jedifans Aug 29 '16 at 21:59
  • 2
    GIMP has a lot of filters open sourced, have a look at them – Déjà vu Sep 01 '16 at 00:12
  • 1
    Here's a start point: https://www.youtube.com/watch?v=C_zFhWdM4ic&list=PLzH6n4zXuckoRdljSlM2k35BufTYXNNeF&index=1 – Ismael Miguel Sep 01 '16 at 00:21
  • What you've quoted are related to image filtering, right? I was looking to observe the lines, but not to render them. Thanks for each idea of you all, though. –  Sep 01 '16 at 00:41
  • 1
    Can you restrict the problem at all? Must it handle transparency? Different colored lines? What kinds of curves - none? Semicircle? Quadratic? More complex? What about an arbitrary pen squiggle, should it handle that? Must it handle antialiasing? And, do you really need to do this to so many images that it isn't faster to just trace things with the pen tool in Photoshop? – twhb Sep 01 '16 at 00:42
  • @twhb Yes, it needs, yes, I don't want to recognize curves or circles. I just want to detect all the present lines in the image. And, yes, it should handle antialising. I want to get the image lines to convert to XML data, for a game called Transformice, which doesn't support curves in the game maps, only joints (lines), and it doesn't allow images in the map, also. –  Sep 01 '16 at 00:46
  • 1
    Thanks. The more we eliminate here, the easier this problem is. Why do we need transparency and different colors? Is it because you have an existing set of input images you want to work with, or does it have to do with what you want to output, or... ? You may get somewhere googling "jpg/png to svg converter". – twhb Sep 01 '16 at 00:56
  • 1
    @nicematt You are interested in edge detection. And, taking from that baseline, you should watch those 3 videos. It explains about filters AND edge-detection. I think it is really interesting. Not only it shows how to detect edges, but their orientation. Which you need for what you want. – Ismael Miguel Sep 01 '16 at 01:00
  • @twhb Nope, I just want to convert a single image to a object represeting *their pixels as lines*. I want the transparency to be ignored, only consider pixels with a visible alpha like >= 12 until 255. A line can't have multiple colors. When the program is reading a line, then it appears a new pixel with different color from it, create a new line in the result object. If I were using a SVG converter maybe it'd count the curves, but I don't want it to count the curves (nor polygons or cycles). –  Sep 01 '16 at 01:05
  • @IsmaelMiguel Yes, I've seen it has a video about edge detection, but I don't understand the english he says fine, since english isn't my primary language. I'll take my headphone and try yo watch it, though. Thanks for the suggestion! –  Sep 01 '16 at 01:05
  • 1
    Re converting to svg, yes probably, but then you can view source and extract what you want. If it's just one image I would definitely consider just tracing it in Photoshop. If neither works, please post the input image. – twhb Sep 01 '16 at 01:11
  • 1
    you probably need to post an example. i suspect your problem is much simpler than what most people are suggesting – softwarenewbie7331 Sep 01 '16 at 03:07
  • @softwarenewbie7331 The example result in the question is what I desired. Again, I want to read the pixels of a image *as lines*, and get their data, as shown in the op, but I'd like to ignore the invisible pixels which have alpha like 0, or <= 15. –  Sep 01 '16 at 10:56
  • 1
    What if you have a 2x2 block with the same color? I could say there are 4 1lines of length 1, 2 vertical lines of length 2, 2 horizontal lines of length 2, 2 diagonal lines of length 2√2 – Oriol Sep 01 '16 at 18:18
  • @Oriol It'd be parsed as one line if the edge of each row is round (width = 2), but it'd be parsed as two lines if each row has a square form (each width = 1). –  Sep 01 '16 at 18:30
  • your example is terrible. an actual example you will pass into the prog + what you expect from it will help a lot. I suspect you just want to parse a level map which could just be a few red lines drawn in MS.Paint. the answers here are overkill for that use case. – softwarenewbie7331 Sep 02 '16 at 02:35
  • @softwarenewbie7331 What does "prog" stands for? And yes, I want to parse every pixel in a image as "lines", "lines", I want to treat them as "lines". I'm a little sick though, that's why I'm not active with the question. –  Sep 02 '16 at 10:39
  • 1
    @nicematt prog means program. You asked a pretty advanced image processing question - one that has no perfect answer - but you have a restricted set of input. It would really help everyone if you provide asap the most complicated input you can, you can upload image to SO. Also, consider a bigger bounty if you want working code. – Sheepy Sep 05 '16 at 02:23
  • @Sheepy I updated the question, I'll increase the bounty when it end up. Is there a way to edit the bounty right now? –  Sep 05 '16 at 10:50
  • 1
    You should give us a real image example because as @sofwarenewbie7331 says the question your are asking is very complex and in fact as no exact solution for most of images. But with simple image ( and by simple i mean with a good SNR and few features ) your problem could be very easily solved. For instance, the example you give is a simple neighbor chain... – Antoine Bergamaschi Sep 05 '16 at 12:12

2 Answers2

2

You can find all lines using Hough transform. It will find only lines, no curves or circles. You may need to run edge detection before finding lines. Here is example: enter image description here enter image description here

Here you can find opencv example of implementation.

taarraas
  • 1,483
  • 9
  • 18
  • Are you sure this technique works fine? In this demo some lines aren't stroked. –  Sep 01 '16 at 18:43
  • 1
    It can detect stroked lines. But you may need to tune parameters for sensitivity. Hough transform treat each pixel as a potential line in all direction from this pixel. And then calculates score for each possible lines. If there are few pixels set across one line, this line will get more score. By setting threshold you choose minimum amount of pixels in one line to be detected – taarraas Sep 01 '16 at 18:49
2

I had a similar image processing question once, you can read it here. But you can basically take the same idea for anything you want to do with an image.

The basic data can be seen as follows:

var img = new Image,
    w = canvas.width,
    h = canvas.height,
    ctx = canvas.getContext('2d');

img.onload = imgprocess;
img.src = 'some.png';

function imgprocess() { 
    ctx.drawImage(this, 0, 0, w, h);

    var idata = ctx.getImageData(0, 0, w, h),
    buffer = idata.data,
    buffer32 = new Uint32Array(buffer.buffer),
    x, y,
    x1 = w, y1 = h, x2 = 0, y2 = 0;

    //You now have properties of the image from the canvas data. You will need to write your own loops to detect which pixels etc... See the example in the link for some ideas. 
}

UPDATE:

Working example of finding color data;

var canvas = document.getElementById('canvas');
var canvasWidth  = canvas.width;
var canvasHeight = canvas.height;
var ctx = canvas.getContext('2d');
var imageData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);

var buf = new ArrayBuffer(imageData.data.length);
var buf8 = new Uint8ClampedArray(buf);
var data = new Uint32Array(buf);

for (var y = 0; y < canvasHeight; ++y) {
    for (var x = 0; x < canvasWidth; ++x) {
        var value = x * y & 0xff;

        data[y * canvasWidth + x] =
            (255   << 24) |    // alpha
            (value << 16) |    // blue
            (value <<  8) |    // green
             value;            // red
    }
}

More examples can be seen here

The author above outlines pixel and line data:

The ImageData.data property referenced by the variable data is a one-dimensional array of integers, where each element is in the range 0..255. ImageData.data is arranged in a repeating sequence so that each element refers to an individual channel. That repeating sequence is as follows:

data[0]  = red channel of first pixel on first row
data[1]  = green channel of first pixel on first row
data[2]  = blue channel of first pixel on first row
data[3]  = alpha channel of first pixel on first row

data[4]  = red channel of second pixel on first row
data[5]  = green channel of second pixel on first row
data[6]  = blue channel of second pixel on first row
data[7]  = alpha channel of second pixel on first row

data[8]  = red channel of third pixel on first row
data[9]  = green channel of third pixel on first row
data[10] = blue channel of third pixel on first row
data[11] = alpha channel of third pixel on first row
Community
  • 1
  • 1
Mike
  • 1,436
  • 9
  • 16
  • 1
    I've updated my answer with more examples on how to deal with the data. – Mike Sep 01 '16 at 18:35
  • Thanks, I apparently know how to do that, I just don't have a logic set to read the pixels as \*lines. I'm actually doing this too. Just asking, are you using `buf8`? –  Sep 01 '16 at 18:41
  • I am not, but buff8 description added as it may help you – Mike Sep 01 '16 at 18:52