2012-08-29 17 views
20

Ho 100.000 immagini che non sono sotto il mio controllo. Alcune di queste immagini sono eccellenti in quanto l'immagine si estende ai limiti mentre alcune hanno una quantità eccessiva di spazio bianco.Ritaglia lo spazio bianco dell'immagine automaticamente usando jQuery

Quando lo spazio bianco è eccessivo, la pagina risulta terribile e le immagini sullo schermo sembrano tutte di dimensioni diverse.

Si può vedere quello che voglio dire qui:

http://www.fitness-saver.com/uk/shop/mountain-bikes/

Quello che mi è stato a caccia di jQuery è un metodo di ritaglio delle immagini e rimuovendo gli spazi automaticamente.

1) La quantità di spazio bianco è diversa in ogni immagine 2) I rapporti delle immagini sono diversi 3) Voglio usare javascript piuttosto che pre-elaborare le immagini.

Spero che tu possa aiutare!

Modifica: Ecco un'immagine di esempio - http://images.productserve.com/preview/3395/128554505.jpg. Nota che le immagini provengono da vari siti affiliati e provengono sicuramente da un dominio diverso.

+1

Posso suggerire di aggiungere effettivamente una delle immagini su cui si è verificato il problema. Eviterà la chiusura dei voti perché stai pubblicizzando in modo efficace il tuo sito. –

+1

È possibile disegnare l'immagine su una tela e rimuovere intere colonne e righe vuote e ridimensionare le immagini alla stessa dimensione (mantenendo le proporzioni). – Prusse

+2

"Voglio usare javascript piuttosto che pre-elaborare le immagini" - Perché? Perché dovresti volere che il browser degli utenti debba rimuovere lo spazio bianco ogni volta che viene caricata un'immagine anziché eseguirla una sola volta sul lato server, e quindi salvare l'immagine senza spaziatura bianca da utilizzare in futuro? – h2ooooooo

risposta

41

Per analizzare gli spazi vuoti in un'immagine, l'unico modo che conosco è quello di caricare l'immagine in un canvas:

var img = new Image(), 
    $canvas = $("<canvas>"), // create an offscreen canvas 
    canvas = $canvas[0], 
    context = canvas.getContext("2d"); 

img.onload = function() { 
    context.drawImage(this, 0, 0); // put the image in the canvas 
    $("body").append($canvas); 
    removeBlanks(this.width, this.height); 
}; 

// test image 
img.src = 'http://images.productserve.com/preview/1302/218680281.jpg'; 

Avanti, utilizzare il metodo getImageData(). Questo metodo restituisce un oggetto ImageData che è possibile utilizzare per ispezionare ogni dato di pixel (colore).

var removeBlanks = function (imgWidth, imgHeight) { 
    var imageData = context.getImageData(0, 0, canvas.width, canvas.height), 
      data = imageData.data, 
      getRBG = function(x, y) { 
         return { 
         red: data[(imgWidth*y + x) * 4], 
         green: data[(imgWidth*y + x) * 4 + 1], 
         blue: data[(imgWidth*y + x) * 4 + 2] 
         }; 
        }, 
      isWhite = function (rgb) { 
         return rgb.red == 255 && rgb.green == 255 && rgb.blue == 255; 
        }, 
      scanY = function (fromTop) { 
         var offset = fromTop ? 1 : -1; 

         // loop through each row 
         for(var y = fromTop ? 0 : imgHeight - 1; fromTop ? (y < imgHeight) : (y > -1); y += offset) { 

         // loop through each column 
         for(var x = 0; x < imgWidth; x++) { 
          if (!isWhite(getRBG(x, y))) { 
           return y;       
          }  
         } 
        } 
        return null; // all image is white 
       }, 
      scanX = function (fromLeft) { 
         var offset = fromLeft? 1 : -1; 

         // loop through each column 
         for(var x = fromLeft ? 0 : imgWidth - 1; fromLeft ? (x < imgWidth) : (x > -1); x += offset) { 

         // loop through each row 
         for(var y = 0; y < imgHeight; y++) { 
          if (!isWhite(getRBG(x, y))) { 
           return x;       
          }  
         } 
        } 
        return null; // all image is white 
       }; 


     var cropTop = scanY(true), 
      cropBottom = scanY(false), 
      cropLeft = scanX(true), 
      cropRight = scanX(false); 
    // cropTop is the last topmost white row. Above this row all is white 
    // cropBottom is the last bottommost white row. Below this row all is white 
    // cropLeft is the last leftmost white column. 
    // cropRight is the last rightmost white column. 
}; 

Francamente non ho potuto verificare questo codice per una buona ragione: mi sono imbattuto l'eccezione famigerato "Impossibile ottenere dati di immagine dalla tela, perché la tela è stata contaminata da dati cross-origine." Sicurezza .

Questo non è un bug, è una funzionalità prevista. Dal specs:

Il toDataURL(), toDataURLHD(), toBlob(), getImageData(), e getImageDataHD() metodi di controllare il flag e sarà un'eccezione SecurityError piuttosto che perdere i dati cross-origine .

Questo succede quando drawImage() carica i file da domini esterni, che provoca origine pulire bandiera della tela per essere impostato su false, prevenire ulteriori manipolazioni di dati.

temo si incorrere nello stesso problema, ma in ogni caso, here is the code.

Anche se questo funziona su lato client, posso immaginare quanto miserabile sarà prestazioni-saggio. Quindi, come ha detto Jan, se è possibile scaricare le immagini e pre-elaborarle sul lato server, sarebbe meglio.


Edit: ero curioso di vedere se il mio codice sarebbe davvero ritagliare un'immagine, e in effetti lo fa. enter image description here

È possibile controllarlo fuori here

Funziona solo per le immagini dal tuo dominio, come detto prima. È possibile scegliere la propria immagine con sfondo bianco e modificare l'ultima riga:

// define here an image from your domain 
img.src = 'http://localhost/strawberry2.jpg'; 

Ovviamente, è necessario eseguire il codice dal tuo dominio, non dalla jsFiddle.


Edit2: Se si desidera ritagliare e scalare fino a mantenere le stesse proporzioni, quindi modificare questo

var $croppedCanvas = $("<canvas>").attr({ width: cropWidth, height: cropHeight }); 

// finally crop the guy 
$croppedCanvas[0].getContext("2d").drawImage(canvas, 
    cropLeft, cropTop, cropWidth, cropHeight, 
    0, 0, cropWidth, cropHeight); 

a

var $croppedCanvas = $("<canvas>").attr({ width: imgWidth, height: imgHeight }); 

// finally crop the guy 
$croppedCanvas[0].getContext("2d").drawImage(canvas, 
    cropLeft, cropTop, cropWidth, cropHeight, 
    0, 0, imgWidth, imgHeight); 

Edit3: Un modo veloce per ritagliare le immagini sul browse r, è quello di parallelizzare il carico di lavoro attraverso l'uso di Web Workers, come spiega questo excellent article.

+0

Grazie per la risposta dettagliata. Non esiste un sistema di sicurezza equivalente che funzioni come Javascript e JSONP? –

+0

Se i siti affiliati restituiscono l'URL immagine come JSON, potresti provare a racchiudere la risposta JSON in una funzione javascript, vedi http://en.wikipedia.org/wiki/JSONP Inoltre, notifico il nuovo attributo 'crossorigin' HTML5 che permette di leggere le immagini da domini stranieri, MA il server (i tuoi affiliati) deve consentire che avendo un'intestazione 'Access-Control-Allow-Origin', vedi https://developer.mozilla.org/en-US/docs/CORS_Enabled_Image Ho introdotto questa nuova proprietà nel codice jsFiddle –

+0

@DavidHilditch Vedi la mia risposta aggiornata –

10

Sulla base dell'ottima risposta fornita da Jose Rui Santos, ho modificato il suo codice per lavorare con solo l'oggetto image senza la libreria jQuery da caricare.

Il ritorno di questa funzione è l'URL dei dati immagine ritagliati da utilizzare direttamente nell'elemento immagine.

/* 
    Source: http://jsfiddle.net/ruisoftware/ddZfV/7/ 
    Updated by: Mohammad M. AlBanna 
    Website: MBanna.info 
    Facebook: FB.com/MBanna.info 
*/ 

var myImage = new Image(); 
myImage.crossOrigin = "Anonymous"; 
myImage.onload = function(){ 
    var imageData = removeImageBlanks(myImage); //Will return cropped image data 
} 
myImage.src = "IMAGE SOURCE"; 



//-----------------------------------------// 
function removeImageBlanks(imageObject) { 
    imgWidth = imageObject.width; 
    imgHeight = imageObject.height; 
    var canvas = document.createElement('canvas'); 
    canvas.setAttribute("width", imgWidth); 
    canvas.setAttribute("height", imgHeight); 
    var context = canvas.getContext('2d'); 
    context.drawImage(imageObject, 0, 0); 

    var imageData = context.getImageData(0, 0, imgWidth, imgHeight), 
     data = imageData.data, 
     getRBG = function(x, y) { 
      var offset = imgWidth * y + x; 
      return { 
       red:  data[offset * 4], 
       green: data[offset * 4 + 1], 
       blue: data[offset * 4 + 2], 
       opacity: data[offset * 4 + 3] 
      }; 
     }, 
     isWhite = function (rgb) { 
      // many images contain noise, as the white is not a pure #fff white 
      return rgb.red > 200 && rgb.green > 200 && rgb.blue > 200; 
     }, 
       scanY = function (fromTop) { 
     var offset = fromTop ? 1 : -1; 

     // loop through each row 
     for(var y = fromTop ? 0 : imgHeight - 1; fromTop ? (y < imgHeight) : (y > -1); y += offset) { 

      // loop through each column 
      for(var x = 0; x < imgWidth; x++) { 
       var rgb = getRBG(x, y); 
       if (!isWhite(rgb)) { 
        if (fromTop) { 
         return y; 
        } else { 
         return Math.min(y + 1, imgHeight - 1); 
        } 
       } 
      } 
     } 
     return null; // all image is white 
    }, 
    scanX = function (fromLeft) { 
     var offset = fromLeft? 1 : -1; 

     // loop through each column 
     for(var x = fromLeft ? 0 : imgWidth - 1; fromLeft ? (x < imgWidth) : (x > -1); x += offset) { 

      // loop through each row 
      for(var y = 0; y < imgHeight; y++) { 
       var rgb = getRBG(x, y); 
       if (!isWhite(rgb)) { 
        if (fromLeft) { 
         return x; 
        } else { 
         return Math.min(x + 1, imgWidth - 1); 
        } 
       }  
      } 
     } 
     return null; // all image is white 
    }; 

    var cropTop = scanY(true), 
     cropBottom = scanY(false), 
     cropLeft = scanX(true), 
     cropRight = scanX(false), 
     cropWidth = cropRight - cropLeft, 
     cropHeight = cropBottom - cropTop; 

    canvas.setAttribute("width", cropWidth); 
    canvas.setAttribute("height", cropHeight); 
    // finally crop the guy 
    canvas.getContext("2d").drawImage(imageObject, 
     cropLeft, cropTop, cropWidth, cropHeight, 
     0, 0, cropWidth, cropHeight); 

    return canvas.toDataURL(); 
} 
+1

Perché hai aggiunto +10; non ha senso e mi hai fatto perdere un sacco di tempo cercando di trovare il bug –

+0

@IvanCastellanos Grazie per le modifiche! L'ho aggiunto nel codice perché l'immagine ritagliata aveva un testo e avevo bisogno di un po 'di spazio in basso e a destra. Perdonami per quello. – Mohammad

Problemi correlati