如何将二进制缓冲区保存到nodejs中的png文件?
How to save binary buffer to png file in nodejs?
我有包含位图信息的二进制 nodejs Buffer 对象。如何从缓冲区制作图像并将其保存到文件?
编辑:
我尝试使用@herchu 所说的文件系统包,但如果我这样做:
let robot = require("robotjs")
let fs = require('fs')
let size = 200
let img = robot.screen.capture(0, 0, size, size)
let path = 'myfile.png'
let buffer = img.image
fs.open(path, 'w', function (err, fd) {
if (err) {
// Something wrong creating the file
}
fs.write(fd, buffer, 0, buffer.length, null, function (err) {
// Something wrong writing contents!
})
})
我明白了
注意:我正在根据您最后的编辑来编辑我的答案
如果您使用的是 Robotjs,请检查其 Bitmap object 是否包含 raw 像素数据的缓冲区 -- 不是 PNG 或任何其他文件格式内容,只是像素彼此相邻(在您的情况下正好是 200 x 200 个元素)。
我没有在 Robotjs 库中找到任何以其他格式写入内容的功能(我也不知道),所以在这个答案中我使用了不同的库,Jimp,用于图像操纵。
let robot = require("robotjs")
let fs = require('fs')
let Jimp = require('jimp')
let size = 200
let rimg = robot.screen.capture(0, 0, size, size)
let path = 'myfile.png'
// Create a new blank image, same size as Robotjs' one
let jimg = new Jimp(size, size);
for (var x=0; x<size; x++) {
for (var y=0; y<size; y++) {
// hex is a string, rrggbb format
var hex = rimg.colorAt(x, y);
// Jimp expects an Int, with RGBA data,
// so add FF as 'full opaque' to RGB color
var num = parseInt(hex+"ff", 16)
// Set pixel manually
jimg.setPixelColor(num, x, y);
}
}
jimg.write(path)
请注意,转换是通过手动遍历所有像素完成的;这在 JS 中很慢。还有一些关于每个库如何处理其像素格式的细节,因此在循环中需要进行一些操作——从嵌入的评论中应该清楚。
将此添加为@herchu 接受的答案的附录,此代码示例 processes/converts 原始字节的速度要快得多(全屏对我来说 < 1s)。希望这对某人有帮助。
var jimg = new Jimp(width, height);
for (var x=0; x<width; x++) {
for (var y=0; y<height; y++) {
var index = (y * rimg.byteWidth) + (x * rimg.bytesPerPixel);
var r = rimg.image[index];
var g = rimg.image[index+1];
var b = rimg.image[index+2];
var num = (r*256) + (g*256*256) + (b*256*256*256) + 255;
jimg.setPixelColor(num, x, y);
}
}
虽然@herchu 和@Jake 的解决方案有效,但它们非常慢(根据我的经验,需要 10-15 秒)。
Jimp supports converting Raw Pixel Buffer into PNG 开箱即用,工作速度更快(亚秒)。
const img = robot.screen.capture(0, 0, width, height).image;
new Jimp({data: img, width, height}, (err, image) => {
image.write(fileName);
});
快四倍!
如果使用此脚本,全屏 1920x1080 大约需要 280ms 和 550Kb。
我在比较每字节 2 字节线程和额头时发现了这个模式。
const robotjs = require('robotjs');
const Jimp = require('jimp');
const app = require('express').Router();
app.get('/screenCapture', (req, res)=>{
let image = robotjs.screen.capture();
for(let i=0; i < image.image.length; i++){
if(i%4 == 0){
[image.image[i], image.image[i+2]] = [image.image[i+2], image.image[i]];
}
}
var jimg = new Jimp(image.width, image.height);
jimg.bitmap.data = image.image;
jimg.getBuffer(Jimp.MIME_PNG, (err, result)=>{
res.set('Content-Type', Jimp.MIME_PNG);
res.send(result);
});
});
如果您在 jimp.getBuffer
之前添加此代码,您将获得大约 210 毫秒和 320Kb 的全屏
jimg.rgba(true);
jimg.filterType(1);
jimg.deflateLevel(5);
jimg.deflateStrategy(1);
我建议您查看 sharp,因为它的性能指标优于 jimp。
实际上非常有效的 robotjs 屏幕捕获的问题是 BGRA 颜色模型而不是 RGBA。所以你需要做额外的颜色旋转。
此外,当我们从桌面截屏时,我无法想象我们需要透明度的情况。所以,我建议忽略它。
const [left, top, width, height] = [0, 0, 100, 100]
const channels = 3
const {image, width: cWidth, height: cHeight, bytesPerPixel, byteWidth} = robot.screen.capture(left, right, width, height)
const uint8array = new Uint8Array(cWidth*cHeight*channels);
for(let h=0; h<cHeight; h+=1) {
for(let w=0; w<cWidth; w+=1) {
let offset = (h*cWidth + w)*channels
let offset2 = byteWidth*h + w*bytesPerPixel
uint8array[offset] = image.readUInt8(offset2 + 2)
uint8array[offset + 1] = image.readUInt8(offset2 + 1)
uint8array[offset + 2] = image.readUInt8(offset2 + 0)
}
}
await sharp(Buffer.from(uint8array), {
raw: {
width: cWidth,
height: cHeight,
channels,
}
}).toFile('capture.png')
我这里使用的是中间数组,其实你可以直接换入截屏的结果。
我有包含位图信息的二进制 nodejs Buffer 对象。如何从缓冲区制作图像并将其保存到文件?
编辑:
我尝试使用@herchu 所说的文件系统包,但如果我这样做:
let robot = require("robotjs")
let fs = require('fs')
let size = 200
let img = robot.screen.capture(0, 0, size, size)
let path = 'myfile.png'
let buffer = img.image
fs.open(path, 'w', function (err, fd) {
if (err) {
// Something wrong creating the file
}
fs.write(fd, buffer, 0, buffer.length, null, function (err) {
// Something wrong writing contents!
})
})
我明白了
注意:我正在根据您最后的编辑来编辑我的答案
如果您使用的是 Robotjs,请检查其 Bitmap object 是否包含 raw 像素数据的缓冲区 -- 不是 PNG 或任何其他文件格式内容,只是像素彼此相邻(在您的情况下正好是 200 x 200 个元素)。
我没有在 Robotjs 库中找到任何以其他格式写入内容的功能(我也不知道),所以在这个答案中我使用了不同的库,Jimp,用于图像操纵。
let robot = require("robotjs")
let fs = require('fs')
let Jimp = require('jimp')
let size = 200
let rimg = robot.screen.capture(0, 0, size, size)
let path = 'myfile.png'
// Create a new blank image, same size as Robotjs' one
let jimg = new Jimp(size, size);
for (var x=0; x<size; x++) {
for (var y=0; y<size; y++) {
// hex is a string, rrggbb format
var hex = rimg.colorAt(x, y);
// Jimp expects an Int, with RGBA data,
// so add FF as 'full opaque' to RGB color
var num = parseInt(hex+"ff", 16)
// Set pixel manually
jimg.setPixelColor(num, x, y);
}
}
jimg.write(path)
请注意,转换是通过手动遍历所有像素完成的;这在 JS 中很慢。还有一些关于每个库如何处理其像素格式的细节,因此在循环中需要进行一些操作——从嵌入的评论中应该清楚。
将此添加为@herchu 接受的答案的附录,此代码示例 processes/converts 原始字节的速度要快得多(全屏对我来说 < 1s)。希望这对某人有帮助。
var jimg = new Jimp(width, height);
for (var x=0; x<width; x++) {
for (var y=0; y<height; y++) {
var index = (y * rimg.byteWidth) + (x * rimg.bytesPerPixel);
var r = rimg.image[index];
var g = rimg.image[index+1];
var b = rimg.image[index+2];
var num = (r*256) + (g*256*256) + (b*256*256*256) + 255;
jimg.setPixelColor(num, x, y);
}
}
虽然@herchu 和@Jake 的解决方案有效,但它们非常慢(根据我的经验,需要 10-15 秒)。
Jimp supports converting Raw Pixel Buffer into PNG 开箱即用,工作速度更快(亚秒)。
const img = robot.screen.capture(0, 0, width, height).image;
new Jimp({data: img, width, height}, (err, image) => {
image.write(fileName);
});
快四倍! 如果使用此脚本,全屏 1920x1080 大约需要 280ms 和 550Kb。 我在比较每字节 2 字节线程和额头时发现了这个模式。
const robotjs = require('robotjs');
const Jimp = require('jimp');
const app = require('express').Router();
app.get('/screenCapture', (req, res)=>{
let image = robotjs.screen.capture();
for(let i=0; i < image.image.length; i++){
if(i%4 == 0){
[image.image[i], image.image[i+2]] = [image.image[i+2], image.image[i]];
}
}
var jimg = new Jimp(image.width, image.height);
jimg.bitmap.data = image.image;
jimg.getBuffer(Jimp.MIME_PNG, (err, result)=>{
res.set('Content-Type', Jimp.MIME_PNG);
res.send(result);
});
});
如果您在 jimp.getBuffer
之前添加此代码,您将获得大约 210 毫秒和 320Kb 的全屏
jimg.rgba(true);
jimg.filterType(1);
jimg.deflateLevel(5);
jimg.deflateStrategy(1);
我建议您查看 sharp,因为它的性能指标优于 jimp。
实际上非常有效的 robotjs 屏幕捕获的问题是 BGRA 颜色模型而不是 RGBA。所以你需要做额外的颜色旋转。
此外,当我们从桌面截屏时,我无法想象我们需要透明度的情况。所以,我建议忽略它。
const [left, top, width, height] = [0, 0, 100, 100]
const channels = 3
const {image, width: cWidth, height: cHeight, bytesPerPixel, byteWidth} = robot.screen.capture(left, right, width, height)
const uint8array = new Uint8Array(cWidth*cHeight*channels);
for(let h=0; h<cHeight; h+=1) {
for(let w=0; w<cWidth; w+=1) {
let offset = (h*cWidth + w)*channels
let offset2 = byteWidth*h + w*bytesPerPixel
uint8array[offset] = image.readUInt8(offset2 + 2)
uint8array[offset + 1] = image.readUInt8(offset2 + 1)
uint8array[offset + 2] = image.readUInt8(offset2 + 0)
}
}
await sharp(Buffer.from(uint8array), {
raw: {
width: cWidth,
height: cHeight,
channels,
}
}).toFile('capture.png')
我这里使用的是中间数组,其实你可以直接换入截屏的结果。