javascript - NodeJS Canvas - 仅将剪切区域保存到新的画布/文件
问题描述
我面临以下挑战:
我们得到了很多只有黑白颜色的技术图纸。这样的绘图在单个图像中包含多个对象。我编写了一个边缘检测器并设法通过坐标(多边形)绘制一条线/剪辑每个单独对象的边界。
我的问题是我找不到仅将剪切区域保存到单个图像文件的合法方法。
我需要每个对象,因为它是自己的图像(如 jpg),对象周围没有任何额外的空白。
出于测试目的,我目前正在使用我在下面发布的图片
这就是我当前输出的样子,边框标记为红色。一开始我不得不反转颜色,不要注意
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/');
解决方案
首先,我想说的是,对于一个 StackOverflow 问题来说,这是一种复杂的方式,如果你想获得快速的答案,你必须将你的问题简化为小而精确的问题......
分解你的代码看起来,你的边框数组不像我预期的那样工作,看到这个:
https://raw.githack.com/heldersepu/hs-scripts/master/HTML/canvas_parse.html
我有四个画布,您可能会识别出代码的各个阶段:
- 首先是原样的图像
- 第二次应用过滤器并反转
- 第三是所有边界都在一个地方
- Forth 是您的边框数组的动画
我原以为边界中的每个项目都是一个多边形,但似乎有些东西还不太对劲,这是你必须集中时间的第一部分。
一旦你在边界数组中获得了多边形,在单独的图像中重新创建它们应该相对容易,我看到可能会使事情复杂化的是多边形内部的多边形,比如你的带有方孔的星星,你可以嵌套很多形状和创造一些奇怪的东西,围绕它的逻辑可能会变得非常棘手。
推荐阅读
- vbscript - 并发调用脚本后将错误值写入日志文件
- javascript - 在 OpenLayers 和 Thymeleaf 中使用 Javascript 变量
- android - Oreo 无限后台服务,例如 Messenger 或 Instagram
- ios - 添加到 UIScrollView 时调整 UITextView 父 UIView 的大小
- python - 如何使用python一次重命名多个文件
- c# - 我的方法中的重载错误
- r - 如何在 R 中的同一个 dygraph 中绘制多个变量图
- jquery - 使用 jQuery 响应在新选项卡中打开 url
- javascript - 如何将文本区域中的换行符替换为
- laravel - 如何在phpunit测试期间检查断点数据库中的值