首页 > 解决方案 > NodeJS Canvas - 仅将剪切区域保存到新的画布/文件

问题描述

我面临以下挑战:

我们得到了很多只有黑白颜色的技术图纸。这样的绘图在单个图像中包含多个对象。我编写了一个边缘检测器并设法通过坐标(多边形)绘制一条线/剪辑每个单独对象的边界。

我的问题是我找不到仅将剪切区域保存到单个图像文件的合法方法。

我需要每个对象,因为它是自己的图像(如 jpg),对象周围没有任何额外的空白。

出于测试目的,我目前正在使用我在下面发布的图片

多星.jpg

这就是我当前输出的样子,边框标记为红色。一开始我不得不反转颜色,不要注意

multistar_currentOutput

var http = require('http')
var fs = require('fs') 
var Canvas = require('canvas');

function pixelIsTouching (coord1, coord2){

    if(coord1[0] == coord2[0] && 
      (coord1[1] == coord2[1]+1 ||
      coord1[1] == coord2[1]-1)){
        return true;
    }else if(coord1[1] == coord2[1] && 
            (coord1[0] == coord2[0]+1 ||
            coord1[0] == coord2[0]-1)){
        return true;
    }else{
        return false;
    }
}

http.createServer(function (req, res) {
    fs.readFile(__dirname + '/images/multistar.jpg', function(err, data) {
        if (err) throw err;

        var img = new Canvas.Image; // Create a new Image
        img.src = data;

        var canvas = Canvas.createCanvas(img.width, img.height);
        var ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, img.width, img.height);

        var pixeldata = ctx.getImageData(0, 0, img.width, img.height);

        // Filter out any black white noise + Invert 
        for(var x = 0; x < pixeldata.data.length; x+=4){

            if( pixeldata.data[x] > 127) {
                pixeldata.data[x] = 0;
                pixeldata.data[x+1] = 0;
                pixeldata.data[x+2] = 0;
            }else if(pixeldata.data[x] <= 127) {
                pixeldata.data[x] = 255;
                pixeldata.data[x+1] = 255;
                pixeldata.data[x+2] = 255;
            }
        }

        ctx.putImageData(pixeldata, 0, 0);

        var border = [];

        // Bilddaten pixelweise abarbeiten
        for (x = 0; x < pixeldata.width; x++) {

            for (y = 0; y < pixeldata.height; y++) {

                offset = (pixeldata.width * y + x) * 4;
                r = pixeldata.data[offset];   // rot
                g = pixeldata.data[offset + 1]; // grün
                b = pixeldata.data[offset + 2]; // blau
                a = pixeldata.data[offset + 3]; // Transparenz

                topOffset           = (pixeldata.width * (y-1) + x) * 4;
                rTop                = pixeldata.data[topOffset];

                topRightOffset      = (pixeldata.width * (y-1) + (x+1)) * 4;
                rTopRight           = pixeldata.data[topRightOffset];

                rightOffset         = (pixeldata.width * y + (x+1)) * 4;
                rRight              = pixeldata.data[rightOffset];

                bottomRightOffset   = (pixeldata.width * (y+1) + (x+1)) * 4;
                rBottomRight        = pixeldata.data[bottomRightOffset];

                bottomOffset        = (pixeldata.width * (y+1) + x) * 4;
                rBottom             = pixeldata.data[bottomOffset];

                bottomLeftOffset    = (pixeldata.width * (y+1) + (x-1)) * 4;
                rBottomLeft         = pixeldata.data[bottomLeftOffset];

                leftOffset          = (pixeldata.width * y + (x-1)) * 4;
                rLeft               = pixeldata.data[leftOffset];

                topLeftOffset       = (pixeldata.width * (y-1) + (x-1)) * 4;
                rTopLeft            = pixeldata.data[topLeftOffset];

                // Check around current white pixel if black one is near
                if( r == 255 && rTop == 0 ||
                    r == 255 && rTopRight == 0 ||
                    r == 255 && rRight == 0 ||
                    r == 255 && rBottomRight == 0 ||
                    r == 255 && rBottom == 0 || 
                    r == 255 && rBottomLeft == 0 ||
                    r == 255 && rLeft == 0 ||
                    r == 255 && rTopLeft == 0){

                        border.push([x, y]);
                    }
            }
        }

        //Order border array
        var borderscounter = 0;
        var borders = [[]];

        var coordFound = undefined;

        borders[borderscounter].push(border[0]);
        border.shift();


        while(border.length > 0){

            coordFound = false;

            for(a = 0; a < border.length; a++){

                lastBlobElement = borders[borderscounter].length-1;

                if(pixelIsTouching(border[a], borders[borderscounter][lastBlobElement])){

                    coordFound = true;
                    borders[borderscounter].push(border[a]);
                    border.splice(a, 1);

                }
            }

            if(coordFound == false && border.length > 0){
                borderscounter++;
                borders[borderscounter] = [];
                borders[borderscounter].push(border[0]);
                border.shift();
            }
        }

        // Draw Line via Border Array

        ctx.lineWidth = 1;
        ctx.strokeStyle = 'red';
        ctx.beginPath();

        for(b = 0; b < borders.length; b++){
            for(var a = 0; a < borders[b].length; a++){

                // + 0.5 to get rid of blurry lines
                if(a == 0){
                    ctx.moveTo(borders[b][a][0]+0.5, borders[b][a][1]);
                }else{
                    ctx.lineTo(borders[b][a][0]+0.5, borders[b][a][1]);
                }

                if(a == borders[b].length-1){
                    ctx.closePath();
                }
            }
        }

        ctx.stroke();

        //ctx.globalCompositeOperation = 'destination-out';
        // Crop images via coordinates
        ctx.beginPath();
        ctx.moveTo(borders[0][0][0], borders[0][0][1]);
        for(var i = 1; i < borders[0].length; i++){
            var p = borders[0][i];
            ctx.lineTo(borders[0][i][0], borders[0][i][1]);
        }
        ctx.closePath();

        ctx.clip();









        // save images as files
        var file = canvas.toDataURL("image/png");

        var data = file.replace(/^data:image\/\w+;base64,/, "");
        var buf = new Buffer(data, 'base64');
        fs.writeFile('cropped/image.png', buf);


        res.write('<html><body>');
        res.write('<img src="' + canvas.toDataURL() + '" />');
        res.write('</body></html>');
        res.end();
    });

}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');


标签: javascriptnode.jscanvas

解决方案


首先,我想说的是,对于一个 StackOverflow 问题来说,这是一种复杂的方式,如果你想获得快速的答案,你必须将你的问题简化为小而精确的问题......


分解你的代码看起来,你的边框数组不像我预期的那样工作,看到这个:

https://raw.githack.com/heldersepu/hs-scripts/master/HTML/canvas_parse.html

我有四个画布,您可能会识别出代码的各个阶段:

  • 首先是原样的图像
  • 第二次应用过滤器并反转
  • 第三是所有边界都在一个地方
  • Forth 是您的边框数组的动画

我原以为边界中的每个项目都是一个多边形,但似乎有些东西还不太对劲,这是你必须集中时间的第一部分。

一旦你在边界数组中获得了多边形,在单独的图像中重新创建它们应该相对容易,我看到可能会使事情复杂化的是多边形内部的多边形,比如你的带有方孔的星星,你可以嵌套很多形状和创造一些奇怪的东西,围绕它的逻辑可能会变得非常棘手。


推荐阅读