我在NODEjs中有一个缓冲区形式的图像,我想调整它的大小。
从理论上讲,这应该可以在nodeJS中完成,因为我可以访问包含所有像素数据的缓冲区。
我到过很多地方,找到了一种使用NATIVE(仅!)nodejs来调整图像大小的简单方法,没有外部库,但是我只找到了使用库的解决方案:
https://www.npmjs.com/package/gm
Node gm - resize image and preserve aspect ratio?
Node.js: image resizing without ImageMagick
Easy way to resize image in Node.js?
How to resize an image in Node.js?
How to resize image in Node js
Node.js Resize Image
Resize image in node js
Download image and resize in nodejs
Resizing images with Nodejs and Imagemagick
Resize image to exact size by maintaining aspect ratio nodejs
How to resize images on node.js
Resize and crop image and keeping aspect ratio NodeJS & gm
How to resize image size in nodejs using multer
try to resize the stream image with sharp Node.js
Node.js: image resizing without ImageMagick
resize an image without uploading it to anywhere using gm in nodejs
Resize an image in Node.js using jimp and get the path the new image
但是所有这些解决方案都使用某种库,但是我只想使用普通的NodeJS。
我可以使用Buffer读取像素,因此我应该能够编写一个调整大小的Buffer,例如C++线程http://www.cplusplus.com/forum/general/2615/和许多其他线程,它们可以简单地遍历像素并调整其大小。
我发现了这个问题Resizing an image in an HTML5 canvas,它使用纯客户端JavaScript实现了图像大小调整,而无需依赖canvas drawImage来调整其大小(仅获取图像数据),这是他使用的代码:
function lanczosCreate(lobes) {
return function(x) {
if (x > lobes)
return 0;
x *= Math.PI;
if (Math.abs(x) < 1e-16)
return 1;
var xx = x / lobes;
return Math.sin(x) * Math.sin(xx) / x / xx;
};
}
// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes) {
this.canvas = elem;
elem.width = img.width;
elem.height = img.height;
elem.style.display = "none";
this.ctx = elem.getContext("2d");
this.ctx.drawImage(img, 0, 0);
this.img = img;
this.src = this.ctx.getImageData(0, 0, img.width, img.height);
this.dest = {
width : sx,
height : Math.round(img.height * sx / img.width),
};
this.dest.data = new Array(this.dest.width * this.dest.height * 3);
this.lanczos = lanczosCreate(lobes);
this.ratio = img.width / sx;
this.rcp_ratio = 2 / this.ratio;
this.range2 = Math.ceil(this.ratio * lobes / 2);
this.cacheLanc = {};
this.center = {};
this.icenter = {};
setTimeout(this.process1, 0, this, 0);
}
thumbnailer.prototype.process1 = function(self, u) {
self.center.x = (u + 0.5) * self.ratio;
self.icenter.x = Math.floor(self.center.x);
for (var v = 0; v < self.dest.height; v++) {
self.center.y = (v + 0.5) * self.ratio;
self.icenter.y = Math.floor(self.center.y);
var a, r, g, b;
a = r = g = b = 0;
for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
if (i < 0 || i >= self.src.width)
continue;
var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
if (!self.cacheLanc[f_x])
self.cacheLanc[f_x] = {};
for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
if (j < 0 || j >= self.src.height)
continue;
var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
if (self.cacheLanc[f_x][f_y] == undefined)
self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2)
+ Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
weight = self.cacheLanc[f_x][f_y];
if (weight > 0) {
var idx = (j * self.src.width + i) * 4;
a += weight;
r += weight * self.src.data[idx];
g += weight * self.src.data[idx + 1];
b += weight * self.src.data[idx + 2];
}
}
}
var idx = (v * self.dest.width + u) * 3;
self.dest.data[idx] = r / a;
self.dest.data[idx + 1] = g / a;
self.dest.data[idx + 2] = b / a;
}
if (++u < self.dest.width)
setTimeout(self.process1, 0, self, u);
else
setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
self.canvas.width = self.dest.width;
self.canvas.height = self.dest.height;
self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
var idx, idx2;
for (var i = 0; i < self.dest.width; i++) {
for (var j = 0; j < self.dest.height; j++) {
idx = (j * self.dest.width + i) * 3;
idx2 = (j * self.dest.width + i) * 4;
self.src.data[idx2] = self.dest.data[idx];
self.src.data[idx2 + 1] = self.dest.data[idx + 1];
self.src.data[idx2 + 2] = self.dest.data[idx + 2];
}
}
self.ctx.putImageData(self.src, 0, 0);
self.canvas.style.display = "block";
};
然后是图像(用
var img = new Image(); img.src = "something"
制作):img.onload = function() {
var canvas = document.createElement("canvas");
new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
// but feel free to raise it up to 8. Your client will appreciate
// that the program makes full use of his machine.
document.body.appendChild(canvas);
};
因此,首先,这在客户端非常慢,但是也许在服务器上可能会更快。节点中需要替换/不存在的内容是
ctx.getImageData
(可以使用缓冲区复制)有谁知道从Nodejs的起点开始,这在所有实际性能上都是明智的吗?如果不能,可以使用上述的C++教程代码,通过纯node-gyp来提高性能吗? (如下):
#include<iostream>
class RawBitMap
{
public:
RawBitMap():_data(NULL), _width(0),_height(0)
{};
bool Initialise()
{
// Set a basic 2 by 2 bitmap for testing.
//
if(_data != NULL)
delete[] _data;
_width = 2;
_height = 2;
_data = new unsigned char[ GetByteCount() ];
//
_data[0] = 0; // Pixels(0,0) red value
_data[1] = 1; // Pixels(0,0) green value
_data[2] = 2; // Pixels(0,0) blue value
_data[3] = 253; // Pixels(1,0)
_data[4] = 254;
_data[5] = 255;
_data[6] = 253; // Pixels(0,1)
_data[7] = 254;
_data[8] = 255;
_data[9] = 0; // Pixels(1,1)
_data[10] = 1;
_data[11] = 2;
return true;
}
// Perform a basic 'pixel' enlarging resample.
bool Resample(int newWidth, int newHeight)
{
if(_data == NULL) return false;
//
// Get a new buuffer to interpolate into
unsigned char* newData = new unsigned char [newWidth * newHeight * 3];
double scaleWidth = (double)newWidth / (double)_width;
double scaleHeight = (double)newHeight / (double)_height;
for(int cy = 0; cy < newHeight; cy++)
{
for(int cx = 0; cx < newWidth; cx++)
{
int pixel = (cy * (newWidth *3)) + (cx*3);
int nearestMatch = (((int)(cy / scaleHeight) * (_width *3)) + ((int)(cx / scaleWidth) *3) );
newData[pixel ] = _data[nearestMatch ];
newData[pixel + 1] = _data[nearestMatch + 1];
newData[pixel + 2] = _data[nearestMatch + 2];
}
}
//
delete[] _data;
_data = newData;
_width = newWidth;
_height = newHeight;
return true;
}
// Show the values of the Bitmap for demo.
void ShowData()
{
std::cout << "Bitmap data:" << std::endl;
std::cout << "============" << std::endl;
std::cout << "Width: " << _width << std::endl;
std::cout << "Height: " << _height << std::endl;
std::cout << "Data:" << std::endl;
for(int cy = 0; cy < _height; cy++)
{
for(int cx = 0; cx < _width; cx++)
{
int pixel = (cy * (_width *3)) + (cx*3);
std::cout << "rgb(" << (int)_data[pixel] << "," << (int)_data[pixel+1] << "," << (int)_data[pixel+2] << ") ";
}
std::cout << std::endl;
}
std::cout << "_________________________________________________________" << std::endl;
}
// Return the total number of bytes in the Bitmap.
inline int GetByteCount()
{
return (_width * _height * 3);
}
private:
int _width;
int _height;
unsigned char* _data;
};
int main(int argc, char* argv[])
{
RawBitMap bitMap;
bitMap.Initialise();
bitMap.ShowData();
if (!bitMap.Resample(4,4))
std::cout << "Failed to resample bitMap:" << std::endl ;
bitMap.ShowData();
bitMap.Initialise();
if (!bitMap.Resample(3,3))
std::cout << "Failed to resample bitMap:" << std::endl ;
bitMap.ShowData();
return 0;
}
我想这是在创建2x2位图并调整其大小,但是基本原理仍然应该可以与纯node-gyp一起应用。还有其他人这样做吗?这完全实用吗?
最佳答案
找到一种简单,快速的方法,仅对节点使用pngjs库(该库是在纯本机节点中编写的,因此甚至可以对其进行优化),并使用内置的流库。因此,在代码顶部只需执行var PNG = require("pngjs").PNG, stream = require("stream");
,然后使用以下代码:
function cobRes(iBuf, width, cb) {
b2s(iBuf)
.pipe(new PNG({
filterType: -1
}))
.on('parsed', function() {
var nw = width;
var nh = nw * this.height /this.width;
var f = resize(this, nw, nh);
sbuff(f.pack(), b=>{
cb(b);
})
})
function resize(srcPng, width, height) {
var rez = new PNG({
width:width,
height:height
});
for(var i = 0; i < width; i++) {
var tx = i / width,
ssx = Math.floor(tx * srcPng.width);
for(var j = 0; j < height; j++) {
var ty = j / height,
ssy = Math.floor(ty * srcPng.height);
var indexO = (ssx + srcPng.width * ssy) * 4,
indexC = (i + width * j) * 4,
rgbaO = [
srcPng.data[indexO ],
srcPng.data[indexO+1],
srcPng.data[indexO+2],
srcPng.data[indexO+3]
]
rez.data[indexC ] = rgbaO[0];
rez.data[indexC+1] = rgbaO[1];
rez.data[indexC+2] = rgbaO[2];
rez.data[indexC+3] = rgbaO[3];
}
}
return rez;
}
function b2s(b) {
var str = new stream.Readable();
str.push(b);
str.push(null);
return str;
}
function sbuff(stream, cb) {
var bufs = []
var pk = stream;
pk.on('data', (d)=> {
bufs.push(d);
})
pk.on('end', () => {
var buff = Buffer.concat(bufs);
cb(buff);
});
}
}
然后使用:
cobRes(fs.readFileSync("somePNGfile.png"), 200, buffer => fs.writeFileSync("new.png", buffer))
不知道为什么每个人都为此使用复杂的库:)