Método Javascript para detectar a área de um PNG que não é transparente

Eu estou procurando um método javascript de detectar uma imagem dentro de um PNG transparente. Por exemplo, vou criar um PNG com uma canvas transparente de 940×680 e, em seguida, colocar um object de opacidade completo em algum lugar dentro dessa canvas.

Eu quero ser capaz de detectar o tamanho (h / w) e top + left local desse object que não é transparente dentro da canvas

Aqui está um exemplo da imagem original Tela PNG transparente com objeto de imagem Aqui está um exemplo do que eu gostaria de alcançar. (Sobreposição de checkbox delimitadora, com dados da margem superior + esquerda)

Resultados Imagem

Eu encontrei um recurso que faz alguma detecção de transparência, mas não tenho certeza de como dimensionar algo assim para o que estou procurando.

var imgData, width = 200, height = 200; $('#mask').bind('mousemove', function(ev){ if(!imgData){ initCanvas(); } var imgPos = $(this).offset(), mousePos = {x : ev.pageX - imgPos.left, y : ev.pageY - imgPos.top}, pixelPos = 4*(mousePos.x + height*mousePos.y), alpha = imgData.data[pixelPos+3]; $('#opacity').text('Opacity = ' + ((100*alpha/255) << 0) + '%'); }); function initCanvas(){ var canvas = $('')[0], ctx = canvas.getContext('2d'); ctx.drawImage($('#mask')[0], 0, 0); imgData = ctx.getImageData(0, 0, width, height); } 

Violino

O que você precisa fazer:

  • Obter o buffer
  • Obtenha uma referência de 32 bits desse buffer (se seus outros pixels forem transparentes, você poderá usar um buffer Uint32Array para iterar).
  • Digitalizar 0 – largura para encontrar a borda x1
  • Largura da varredura – 0 para encontrar a borda x2
  • Digitalizar 0 – altura para encontrar a borda y1
  • Altura da varredura – 0 para encontrar a borda y2

Essas varreduras podem ser combinadas, mas, para simplificar, mostrarei cada etapa separadamente.

Demonstração online disso pode ser encontrada aqui.

Resultado:

Instantâneo

Quando a imagem é carregada, desenhe-a (se a imagem for pequena, o restante deste exemplo seria perdido, pois você saberia as coordenadas ao desenhá-la – supondo que a imagem desenhada seja grande com uma pequena imagem dentro dela)

(nota: esta é uma versão não otimizada por uma questão de simplicidade)

 ctx.drawImage(this, 0, 0, w, h); var idata = ctx.getImageData(0, 0, w, h), // get image data for canvas buffer = idata.data, // get buffer (unnes. step) buffer32 = new Uint32Array(buffer.buffer), // get a 32-bit representation x, y, // iterators x1 = w, y1 = h, x2 = 0, y2 = 0; // min/max values 

Em seguida, digitalize cada borda. Para a borda esquerda, você digitaliza de 0 a largura para cada linha (não otimizada):

 // get left edge for(y = 0; y < h; y++) { // line by line for(x = 0; x < w; x++) { // 0 to width if (buffer32[x + y * w] > 0) { // non-transparent pixel? if (x < x1) x1 = x; // if less than current min update } } } 

Para a borda direita você simplesmente inverte o x iterator:

 // get right edge for(y = 0; y < h; y++) { // line by line for(x = w; x >= 0; x--) { // from width to 0 if (buffer32[x + y * w] > 0) { if (x > x2) x2 = x; } } } 

E o mesmo é para as bordas superior e inferior apenas que os iteradores são invertidos:

 // get top edge for(x = 0; x < w; x++) { for(y = 0; y < h; y++) { if (buffer32[x + y * w] > 0) { if (y < y1) y1 = y; } } } // get bottom edge for(x = 0; x < w; x++) { for(y = h; y >= 0; y--) { if (buffer32[x + y * w] > 0) { if (y > y2) y2 = y; } } } 

A região resultante é então:

 ctx.strokeRect(x1, y1, x2-x1, y2-y1); 

Existem várias otimizações que você pode implementar, mas elas dependem inteiramente do cenário, como se você conhecesse um posicionamento aproximado e não precisas de iterar todas as linhas / colunas.

Você poderia adivinhar a força bruta do posicionamento pulando x número de pixels e, quando encontrar um pixel não transparente, poderá criar uma área de pesquisa máxima com base nisso e assim por diante, mas isso está fora do escopo aqui.

Espero que isto ajude!