Nehmen wir an, das ist meine Leinwand, auf der ein böses Gesicht gezeichnet ist. Ich möchte benutzen toDataURL() um mein böses Gesicht als PNG zu exportieren; Die gesamte Leinwand ist jedoch gerastert, einschließlich des „Leerraums“ zwischen dem bösen Gesicht und den Rändern der Leinwand.
Was ist der beste Weg, um meine Leinwand auf ihren Inhalt zuzuschneiden / zu trimmen / zu verkleinern, damit mein PNG nicht größer als der Begrenzungsrahmen des Gesichts ist, wie unten? Der beste Weg scheint die Leinwand zu skalieren, aber angenommen, die Inhalte sind dynamisch …? Ich bin sicher, es sollte eine einfache Lösung dafür geben, aber es entgeht mir mit viel Googeln.
+------+
|(.Y. )|
| /_ |
|\____/|
+------+
Vielen Dank!
Klingt so, als ob Sie eine Funktion zum automatischen Zuschneiden wünschen. Hmm… Ich habe noch nicht so viel mit Leinwand gearbeitet. Iterieren Sie durch die horizontalen Linien, um die Grenzen Ihres Bildes zu identifizieren, wo Pixel nicht weiß sind. Scannen Sie dann die Vertikale nach demselben.
– CM Kanode
3. August 2012 um 13:24 Uhr
@AlexW: Ich bin mir nicht ganz sicher, ob Sie verstehen, wonach ich suche – ich möchte eher beschneiden als skalieren.
– c24w
3. August 2012 um 13:29 Uhr
@CMKanode: Danke für den Kommentar. Das waren so ziemlich meine Gedanken für eine Lösung als letztes Mittel: P. Es bohrt es jedoch auf ein ziemlich niedriges Niveau, also denke ich, dass es einen anderen Weg geben sollte!
– c24w
3. August 2012 um 13:30 Uhr
Dies sollte die Antwort sein: stackoverflow.com/a/12178531/1066234
– Benutzerbild
25. Oktober 2012 um 16:32 Uhr
potomek
Bearbeitet (Zeige Kommentare)
function cropImageFromCanvas(ctx) {
var canvas = ctx.canvas,
w = canvas.width, h = canvas.height,
pix = {x:[], y:[]},
imageData = ctx.getImageData(0,0,canvas.width,canvas.height),
x, y, index;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
index = (y * w + x) * 4;
if (imageData.data[index+3] > 0) {
pix.x.push(x);
pix.y.push(y);
}
}
}
pix.x.sort(function(a,b){return a-b});
pix.y.sort(function(a,b){return a-b});
var n = pix.x.length-1;
w = 1 + pix.x[n] - pix.x[0];
h = 1 + pix.y[n] - pix.y[0];
var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);
canvas.width = w;
canvas.height = h;
ctx.putImageData(cut, 0, 0);
var image = canvas.toDataURL();
}
Dies ist ein großartiger Code für das automatische Zuschneiden, danke! Ich muss morgen als Rache eine Geige kotzen 😉
– Campbeln
10. September 2014 um 4:57 Uhr
Die Sortierfunktion ist hier nicht erforderlich. Wenn Sie Ihr Minimum x,y und Maximum x,y verfolgen, wo Pixel nicht transparent sind, können Sie die Sortierung vollständig entfernen.
– Armin
9. April 2016 um 23:03 Uhr
Der Code funktioniert großartig, Sie müssen nur +1 in var cut = ctx.getImageData(pix.x[0]pix.y[0], w+1, h+1); canvas.width = w + 1; canvas.height = h + 1; Andernfalls werden die letzten Pixel abgeschnitten.
– osueboy
4. November 2016 um 19:03 Uhr
Diese Antwort ist ein Juwel
– Dranix
20. Januar 2017 um 5:30 Uhr
Überprüfen des Alphakanals (imageData.data[index+3] > 0) hat bei mir nicht funktioniert, da es immer größer als 0 war, musste zur Überprüfung wechseln, ob die Farbe nicht weiß ist (es entspricht meinen Bedürfnissen, da ich einen weißen Hintergrund habe): imageData.data[index] != 255 && imageData.data[index+1] != 255 && imageData.data[index+2] != 255. Dann hat das Zuschneiden perfekt funktioniert, danke.
– Truschkewitsch
23. Mai 2017 um 21:01 Uhr
Wenn ich gut verstanden habe, möchten Sie die gesamte Umgebung Ihres Bildes / Ihrer Zeichnung “zuschneiden” und die Leinwand auf diese Größe anpassen (wie wenn Sie in Photoshop einen “Zuschneiden” -Befehl ausführen).
Hier ist, wie ich es tun werde.
Gehen Sie durch alle Leinwandpixel und prüfen Sie, ob ihre Alpha-Komponente> 0 ist (das bedeutet, dass etwas in diesem Pixel gezeichnet ist). Alternativ können Sie die r,g,b-Werte überprüfen, wenn Ihr Leinwandhintergrund beispielsweise mit einer Volltonfarbe gefüllt ist.
Holen Sie sich die Koordinaten des obersten linken Pixels, das nicht leer ist, und dasselbe für das unterste rechte. Sie erhalten also die Koordinaten eines imaginären “Rechtecks”, das den nicht leeren Leinwandbereich enthält.
Speichern Sie diesen Bereich von Pixeldaten.
Ändern Sie die Größe Ihrer Leinwand auf ihre neuen Abmessungen (die der Region, die wir in Schritt 2 erhalten haben).
Fügen Sie den gespeicherten Bereich wieder auf der Leinwand ein.
Et, voilà 🙂
Der Zugriff auf Pixeldaten ist abhängig von der Größe Ihrer Leinwand ziemlich langsam (wenn sie riesig ist, kann es eine Weile dauern). Es gibt einige Optimierungen, um mit Raw-Canvas-Pixeldaten zu arbeiten (ich glaube, es gibt einen Artikel zu diesem Thema bei MDN), ich schlage vor, Sie googlen danach.
Ich habe eine kleine Skizze in jsFiddle vorbereitet, die Sie als Ausgangspunkt für Ihren Code verwenden können.
Alternativ zu 3 – 5 könntest du die Lösung von Alex W verwenden, die Leinwand übersetzen und die Größe ändern, wird wahrscheinlich schneller sein als die Pixel zu kopieren und einzufügen 🙂
– Carlos Cabo
9. August 2012 um 17:38 Uhr
Hier ist meine Meinung. Ich hatte das Gefühl, dass alle anderen Lösungen zu kompliziert waren. Nachdem ich es erstellt habe, sehe ich jetzt, dass es die gleiche Lösung wie die eines anderen ist, erwarte, dass sie nur eine Geige und keine Funktion geteilt haben.
Die am häufigsten gewählte Antwort hier sowie die Implementierungen, die ich online gefunden habe, trimmen ein zusätzliches Pixel, was beim Versuch, Text aus der Leinwand zu trimmen, sehr auffällig war. Ich habe meine eigene geschrieben, die für mich besser funktioniert hat:
var img = new Image;
img.onload = () => {
var canvas = document.getElementById('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
document.getElementById('button').addEventListener('click', ()=>{
autoCropCanvas(canvas, ctx);
document.getElementById('button').remove();
});
};
img.src="data:image/png;base64,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";
function autoCropCanvas(canvas, ctx) {
var bounds = {
left: 0,
right: canvas.width,
top: 0,
bottom: canvas.height
};
var rows = [];
var cols = [];
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (var x = 0; x < canvas.width; x++) {
cols[x] = cols[x] || false;
for (var y = 0; y < canvas.height; y++) {
rows[y] = rows[y] || false;
const p = y * (canvas.width * 4) + x * 4;
const [r, g, b, a] = [imageData.data[p], imageData.data[p + 1], imageData.data[p + 2], imageData.data[p + 3]];
var isEmptyPixel = Math.max(r, g, b, a) === 0;
if (!isEmptyPixel) {
cols[x] = true;
rows[y] = true;
}
}
}
for (var i = 0; i < rows.length; i++) {
if (rows[i]) {
bounds.top = i ? i - 1 : i;
break;
}
}
for (var i = rows.length; i--; ) {
if (rows[i]) {
bounds.bottom = i < canvas.height ? i + 1 : i;
break;
}
}
for (var i = 0; i < cols.length; i++) {
if (cols[i]) {
bounds.left = i ? i - 1 : i;
break;
}
}
for (var i = cols.length; i--; ) {
if (cols[i]) {
bounds.right = i < canvas.width ? i + 1 : i;
break;
}
}
var newWidth = bounds.right - bounds.left;
var newHeight = bounds.bottom - bounds.top;
var cut = ctx.getImageData(bounds.left, bounds.top, newWidth, newHeight);
canvas.width = newWidth;
canvas.height = newHeight;
ctx.putImageData(cut, 0, 0);
}
Klingt so, als ob Sie eine Funktion zum automatischen Zuschneiden wünschen. Hmm… Ich habe noch nicht so viel mit Leinwand gearbeitet. Iterieren Sie durch die horizontalen Linien, um die Grenzen Ihres Bildes zu identifizieren, wo Pixel nicht weiß sind. Scannen Sie dann die Vertikale nach demselben.
– CM Kanode
3. August 2012 um 13:24 Uhr
@AlexW: Ich bin mir nicht ganz sicher, ob Sie verstehen, wonach ich suche – ich möchte eher beschneiden als skalieren.
– c24w
3. August 2012 um 13:29 Uhr
@CMKanode: Danke für den Kommentar. Das waren so ziemlich meine Gedanken für eine Lösung als letztes Mittel: P. Es bohrt es jedoch auf ein ziemlich niedriges Niveau, also denke ich, dass es einen anderen Weg geben sollte!
– c24w
3. August 2012 um 13:30 Uhr
Dies sollte die Antwort sein: stackoverflow.com/a/12178531/1066234
– Benutzerbild
25. Oktober 2012 um 16:32 Uhr