Node.js: redimensionamento de imagem sem ImageMagick

86

Estou desenvolvendo um aplicativo da web em Node.js (+ express 4) onde os usuários podem definir sua imagem de perfil enviando-a para o servidor. Já limitamos o tipo MIME do arquivo e o tamanho máximo do arquivo, de modo que o usuário não pode enviar mais de 200 KB de imagens png ou JPEG.

O problema é que gostaríamos de redimensionar (lado do servidor) a resolução da imagem enviada para 200x200 para melhorar o carregamento da página e economizar espaço no disco. Depois de alguma pesquisa, todas as respostas apontaram para o uso de qualquer módulo baseado em ImageMagick ou GraphicsMagick.

No entanto, ter que instalar o ImageMagick / GraphicsMagick para fazer um redimensionamento simples da imagem parece um exagero para mim, então, existe alguma outra solução além desta para Node.js?

Edit: Eu mudei a solução aceita para nítida porque a solução anterior (lwip) não é mais mantida. Obrigado por todos os seus comentários!

zacr0
fonte
Oi. Eu tenho uma pergunta Como diminuir o tamanho da imagem para menos de 200 KB? Por favor, explique sobre o caminho. Obrigado.
C.Petrescu
Olá, vale a pena postar essa pergunta como uma nova se você não encontrar nenhuma relacionada que tenha sido postada anteriormente. Para esclarecer, tente procurar métodos de compactação e redimensionamento na API fornecida para as ferramentas que você pode encontrar nesta pergunta.
zacr0 de

Respostas:

92

Eu votaria em afiado :

sharp('input.jpg')
  .resize(200, 200)
  .toFile('ouput.jpg', function(err) {
    // output.jpg is a 200 pixels wide and 200 pixels high image
    // containing a scaled and cropped version of input.jpg
  });

É rápido, normalmente 6x mais rápido do que as ligações de nó mais rápidas baseadas em imagemagick e é executado em muito pouca memória, talvez 10x menos . links nítidos para a biblioteca de imagens libvips diretamente, não há transferência para um programa externo, e a própria biblioteca é mais rápida e eficiente do que * magick nesta tarefa. Ele suporta coisas úteis como fluxo, buffer e entrada e saída de sistema de arquivos, gerenciamento de cores, transparência, promessas, sobreposições, WebP, SVG e muito mais.

A partir de 0,20 afiado, o npm irá baixar automaticamente binários completos pré-compilados na maioria das plataformas, então não há necessidade de node-gyp. Basta inserir:

npm install sharp

ou:

yarn add sharp

E pronto.

jcupitt
fonte
7
A partir da v0.12.0, sharpnão há mais dependências de tempo de execução externas para usuários do Linux e Windows, pois inclui uma versão pré-compilada de libvips. Você descobrirá que as operações de redimensionamento são aproximadamente 10x mais rápidas do que o LWIP e com uma fração do uso da memória.
Lovell Fuller
4
Sharp adicionou suporte nativo a gif e svg com versão 0.15 sharp.dimens.io/en/stable/changelog
jcupitt
1
São centenas de arquivos, mas todos são gerenciados automaticamente pelo npm, você não precisa se preocupar com isso.
jcupitt
4
@CoDEmanX Talvez tente executar npm install --global --production windows-build-toolsprimeiro. Consulte também github.com/Microsoft/nodejs-guidelines/blob/master/…
Lovell Fuller
1
Eu construí um pequeno script que torna mais fácil ver que as coisas estão fazendo e como ficariam no conteúdo, como cartões boostrap e fundo: capa para otimizar melhor os parâmetros, talvez seja de interesse github.com/lcherone/sharp-test
Lawrence Cherone
71

Recentemente comecei a desenvolver um módulo de processamento de imagem para NodeJS sem quaisquer dependências de tempo de execução ( leia porque ). Ainda está nos estágios iniciais, mas já pode ser usado.

O que você está pedindo seria feito da seguinte maneira:

image.resize(200, 200, function(err, image){
    // encode resized image to jpeg and get a Buffer object
    image.toBuffer('jpg', function(err, buffer){
        // save buffer to disk / send over network / etc.
    });
});

Mais informações no repositório Github do módulo .

EyalAr
fonte
7
Seu módulo é incrível. No entanto, é preciso muita memória. Tentei writeFile uma 1.8Mbimagem e requer 130 MB de memória. Depois disso, faço um teste redimensionando uma 4MB, 11000x6000imagem para várias miniaturas (640.560.480, ..., 160) e ela ocupa aproximadamente 1,7 GB de memória. Isso é um bug?
Lewis
2
Olá @Orion, obrigado pelo feedback. Vá para o repositório Github e abra um problema com mais alguns detalhes (sistema operacional, versões, código para reproduzir isso). Vamos tentar resolver isso juntos :)
EyalAr
@EyalAr Ei Eyal, como redimensionar e manter o aspecto? (redimensionar para o tamanho máximo possível de largura, altura) sem esticar
Daniel Krom
10
Eu sugeriria não usar o lwip em 2017. O pacote parece não ser mais compatível e tem grandes problemas de instalação no Windows e agora até mesmo nas plataformas Unix.
zerefel de
9
lwip infelizmente é um projeto morto. agudo, no entanto, ainda parece ser mantido ativamente.
Laurent,
16

Dê uma olhada em lwip: https://github.com/EyalAr/lwip

Muito simples e fácil de usar

npm install lwip

e então em seu código de nó,

// obtain an image object:
require('lwip').open('image.jpg', function(err, image){

  // check err...
  // define a batch of manipulations and save to disk as JPEG:
  image.batch()
    .scale(0.75)          // scale to 75%
    .rotate(45, 'white')  // rotate 45degs clockwise (white fill)
    .crop(200)            // crop a 200X200 square from center
    .blur(5)              // Gaussian blur with SD=5
    .writeFile('output.jpg', function(err){
      // check err...
      // done.
    });

});

Eu implementei isso com sucesso no meu uploader de arquivos e funciona perfeitamente.

Arvind
fonte
1
Isso é o que eu estava procurando, não ter que instalar dependências externas pesadas e exageradas para algumas funções relacionadas ao redimensionamento de imagens.
zacr0,
3
Btw @EyalAr é o autor deste módulo de nó. Seu comentário também está listado abaixo.
Arvind
Muito intuitivo de instalar e trabalhar. Eu realmente gosto que você não seja obrigado a implementar nenhuma biblioteca binária como o ImageMagick.
ChrisRich
Essa é exatamente a mesma resposta (proprietário do lwip), mas posterior: stackoverflow.com/a/24543924/1525495
Jorge Fuentes González
12

Existe uma boa biblioteca de manipulação de imagens escrita inteiramente em JavaScript, sem dependências de nenhuma outra biblioteca, Jimp. https://github.com/oliver-moran/jimp

Exemplo de uso:

var Jimp = require("jimp");

// open a file called "lenna.png"
Jimp.read("lenna.png", function (err, lenna) {
    if (err) throw err;
    lenna.resize(256, 256)            // resize
         .quality(60)                 // set JPEG quality
         .write("lena-small.jpg"); // save
});
edtech
fonte
Quão rápido é isso em comparação ao ImageMagik?
Christopher Grigg de
2
sharp tem um conjunto de benchmarks: sharp.dimens.io/en/stable/performance --- Nesse teste, jimp é 5x mais lento que IM e 30x mais lento que sharp. Claro, rápido o suficiente é rápido o suficiente, e a velocidade não é o único fator a ser considerado.
jcupitt de
Tempo real talvez não, mas Jimp é incrível apenas para escrever arquivos de miniaturas de vários tamanhos (e recuperá-los como arquivos em cache depois).
coderofsalvation
jimp não oferece suporte a webp e não oferecerá suporte no futuro próximo. Veja: github.com/oliver-moran/jimp/issues/144
Viacheslav Dobromyslov
8

sharp tem desfrutado de alguma popularidade recentemente, mas é a mesma ideia que * ligações mágicas.

No entanto, ter que instalar ImageMagick / GraphicsMagick para fazer um redimensionamento simples da imagem parece um exagero para mim

O redimensionamento da imagem é tudo menos simples. O formato JPEG é particularmente complexo e existem várias maneiras de dimensionar gráficos com resultados de qualidade variada, algumas delas facilmente implementadas. Existem bibliotecas de processamento de imagens para fazer esse trabalho, então se não houver outro motivo para você não poder instalá-las, vá em frente.

Ry-
fonte
13
Talvez eu seja um desenvolvedor preguiçoso, mas assim que vi o processo de instalação do ImageMagick e me perguntando quanto eu gastaria para instalá-lo na minha instância do Amazon AWS EC2, imediatamente comecei a procurar outras opções - especialmente considerando que tudo que eu precisava era a capacidade de redimensionar imagens para miniaturas.
ChrisRich
7

O Canvas é 2,3 vezes mais rápido que o ImageMagic.

Você pode tentar comparar módulos Node.js para manipulação de imagens - https://github.com/ivanoff/images-manipulation-performance

author's results:
 sharp.js : 9.501 img/sec; minFreeMem: 929Mb
 canvas.js : 8.246 img/sec; minFreeMem: 578Mb
 gm.js : 4.433 img/sec; minFreeMem: 791Mb
 gm-imagemagic.js : 3.654 img/sec; minFreeMem: 804Mb
 lwip.js : 1.203 img/sec; minFreeMem: 54Mb
 jimp.js : 0.445 img/sec; minFreeMem: 82Mb
Dimitry Ivanov
fonte
3

Se você não precisa de uma imagem grande, pode redimensioná-la no lado do cliente antes de carregá-la:

Lendo arquivos em JavaScript usando APIs de arquivo

Redimensionar a imagem do lado do cliente com javascript antes de fazer upload para o servidor

Muitos usuários podem ter uma boa imagem de si mesmos em um smartphone, e muitos deles têm mais de 200 KB. Observe que os dados fornecidos pelo cliente não são confiáveis, portanto, as verificações do lado do servidor ainda se aplicam.

Andrei Volgin
fonte
2
Você nunca pode confiar no cliente, um usuário só precisa saber o endpoint de upload para enviar o que quiser. Portanto, validações como o tamanho do arquivo ainda se aplicam. Porém, redimensionar no lado do cliente é uma boa ideia.
Kev
1

Eu estava usando o lwip (como sugerido anteriormente por arvind), mas mudei para o png-crop . Parece funcionar um pouco mais rápido para mim (Win 8.1 x64, Node v0.12.7). O código no repo parece incrivelmente leve e operacionalmente simples de usar.

var pngcrop = require('png-crop');
var config = {left: 10, top: 100, height: 150, width: 150};
pngcrop.crop('cats.png','cats-cropped.png',config);

Claro, só fará arquivos PNG ...

Dan Caseley
fonte
0

Sharp funciona muito bem e é fácil de usar com streams, funciona perfeitamente, mas você precisa compilá-lo com a versão do node, isso é uma desvantagem. Estava usando o Sharp para processamento de imagem, com uma imagem de um bucket AWS S3 e funcionou perfeitamente, mas tive que usar outro módulo. GM não funcionou para mim, mas Jimp funcionou muito bem!

Você tem que prestar atenção ao caminho da imagem escrita, pode haver alguns erros se você iniciar o caminho com um "/".

Foi assim que usei o Jimp no nodeJS:

const imageUrl = `SOME_URL`;
let imgExported = 'EXPORTED_PIC.png';

Jimp.read(imageUrl)
    .then(image => {
        image   
            .resize(X, Y) 
            .write(`tmp/`+ imgExported, err => { 
                if(err) 
                    console.error('Write error: ', err);
                else { ... // don't forget to put a callback() } }

            });

Também atente para a ordem de execução, coloque um callback para que outras coisas não aconteçam quando você não quiser. Tentei usar "await" para Jimp.read (), mas não funcionou bem.

Alex Seceleanu
fonte
A partir de 0,20, ele fará download automaticamente de um binário pré-compilado para sua versão exata do nó na maioria das plataformas, então não há necessidade de construir nada.
jcupitt de
Infelizmente não funcionou para mim. Eu precisava usar o sharp em um sistema de arquivos somente leitura, com várias versões de node.js e tive que baixar o módulo sharp para cada versão de nó que estava usando e estava demorando muito.
Alex Seceleanu
0

Você pode fazer isso usando jimp (node_module)

Gravação local:

Jimp.read(path) // this can be url or local location
      .then(image=> {
          image
            .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
            .write('path-to-save');
      })
      .catch(err => {
        console.log(err);
      });

Para fazer upload para s3 ou onde quiser.

Jimp.read(urls) // this can be url or local location
          .then(image=> {
              image
                .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
                .getBase64(Jimp.AUTO, (err, res) => {
                  const buf = new Buffer(
                    res.replace(/^data:image\/\w+;base64,/, ""),
                    "base64"
                  );
                  var data = {
                    Key: key,
                    Bucket: bucket,
                    Body: body,
                    ContentEncoding: "base64",
                    ContentType: "image/jpeg"
                  };
                  s3.putObject(data, function(err, data) {
                    if (err) {
                      throw err;
                    } else {
                      console.log("succesfully uploaded the image!");
                    }
                  });
                });
          })
          .catch(err => {
            console.log(err);
          });
Nouman Dilshad
fonte
0

Eu gosto da biblioteca resize-img por sua simplicidade.

const fs = require('fs');
const resizeImg = require('resize-img');

(async () => {
    const image = fs.readFileSync('unicorn.png');

    const newImage = await resizeImg(image, { width: 128, height: 128 });

    fs.writeFileSync('unicorn-128x128.png', newImage);
})();
ns16
fonte
0

Redimensionar imagem implementado usando Google Drive API v3 . Este método é recomendado para o Google Apps Script para inserir imagens no Google Sheets.

Algoritmo:

  1. Envio imagem para a pasta do Google Drive.
  2. Pegue o URL público da miniatura da imagem.
  3. Substitua o parâmetro 'redimensionar' no URL pela largura e / ou altura necessárias. (O tamanho padrão da miniatura é 220px).
  4. Baixe a miniatura redimensionada do Google Drive.

Veja o exemplo aqui: https://github.com/dobromyslov/google-drive-utils/blob/511c44c2c48862b47c60038423b7f71bf1d28f49/src/index.ts#L150

E cuidado com as cotas do GDrive:

  • consultas por dia: 1000000000
  • consultas por 100 segundos por usuário: 1000
  • consultas por 100 s: 10.000
Viacheslav Dobromyslov
fonte