Ich verwende HTML5-Canvas-Elemente, um die Größe von Bildern in meinem Browser zu ändern. Es stellt sich heraus, dass die Qualität sehr gering ist. Ich habe Folgendes gefunden: Deaktivieren Sie die Interpolation beim Skalieren eines <canvas>, aber es hilft nicht, die Qualität zu verbessern.
Unten finden Sie meinen CSS- und JS-Code sowie das mit Photoshop überarbeitete und in der Canvas-API skalierte Bild.
Was muss ich tun, um beim Skalieren eines Bildes im Browser eine optimale Qualität zu erzielen?
Hinweis: Ich möchte ein großes Bild auf ein kleines verkleinern, die Farbe in einer Zeichenfläche ändern und das Ergebnis von der Zeichenfläche an den Server senden.
CSS:
canvas, img {
image-rendering: optimizeQuality;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
-ms-interpolation-mode: nearest-neighbor;
}
JS:
var $img = $('<img>');
var $originalCanvas = $('<canvas>');
$img.load(function() {
var originalContext = $originalCanvas[0].getContext('2d');
originalContext.imageSmoothingEnabled = false;
originalContext.webkitImageSmoothingEnabled = false;
originalContext.mozImageSmoothingEnabled = false;
originalContext.drawImage(this, 0, 0, 379, 500);
});
Die Bildgröße wurde mit Photoshop geändert:
Die Bildgröße wurde auf Leinwand geändert:
Bearbeiten:
Ich habe versucht, das Downscaling in mehr als einem Schritt durchzuführen, wie in:
Ändern der Größe eines Bildes in einer HTML5- Zeichenfläche und einer HTML5- Zeichenfläche drawImage: Anwenden von Antialiasing
Dies ist die Funktion, die ich verwendet habe:
function resizeCanvasImage(img, canvas, maxWidth, maxHeight) {
var imgWidth = img.width,
imgHeight = img.height;
var ratio = 1, ratio1 = 1, ratio2 = 1;
ratio1 = maxWidth / imgWidth;
ratio2 = maxHeight / imgHeight;
// Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
if (ratio1 < ratio2) {
ratio = ratio1;
}
else {
ratio = ratio2;
}
var canvasContext = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
var canvasCopy2 = document.createElement("canvas");
var copyContext2 = canvasCopy2.getContext("2d");
canvasCopy.width = imgWidth;
canvasCopy.height = imgHeight;
copyContext.drawImage(img, 0, 0);
// init
canvasCopy2.width = imgWidth;
canvasCopy2.height = imgHeight;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
var rounds = 2;
var roundRatio = ratio * rounds;
for (var i = 1; i <= rounds; i++) {
console.log("Step: "+i);
// tmp
canvasCopy.width = imgWidth * roundRatio / i;
canvasCopy.height = imgHeight * roundRatio / i;
copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);
// copy back
canvasCopy2.width = imgWidth * roundRatio / i;
canvasCopy2.height = imgHeight * roundRatio / i;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
} // end for
// copy back to canvas
canvas.width = imgWidth * roundRatio / rounds;
canvas.height = imgHeight * roundRatio / rounds;
canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);
}
Hier ist das Ergebnis, wenn ich eine 2-Stufen-Größenänderung verwende:
Hier ist das Ergebnis, wenn ich eine 3-Stufen-Größenänderung verwende:
Hier ist das Ergebnis, wenn ich eine 4-Stufen-Größenänderung verwende:
Hier ist das Ergebnis, wenn ich eine Größenänderung von 20 Schritten verwende:
Hinweis: Es stellt sich heraus, dass sich die Bildqualität von 1 Schritt auf 2 Schritte erheblich verbessert. Je mehr Schritte Sie dem Prozess hinzufügen, desto unschärfer wird das Bild.
Gibt es eine Möglichkeit, das Problem zu lösen, dass das Bild umso unschärfer wird, je mehr Schritte Sie hinzufügen?
Edit 2013-10-04: Ich habe den Algorithmus von GameAlchemist ausprobiert. Hier ist das Ergebnis im Vergleich zu Photoshop.
PhotoShop Bild:
GameAlchemist-Algorithmus:
quelle
Antworten:
Da Ihr Problem darin besteht, Ihr Bild zu verkleinern, macht es keinen Sinn, über Interpolation zu sprechen, bei der es um das Erstellen von Pixeln geht. Das Problem hier ist das Downsampling.
Um ein Bild herunterzusampeln, müssen wir jedes Quadrat von p * p-Pixeln im Originalbild in ein einzelnes Pixel im Zielbild verwandeln.
Aus Leistungsgründen führen Browser ein sehr einfaches Downsampling durch: Um das kleinere Bild zu erstellen, wählen sie nur EIN Pixel in der Quelle aus und verwenden dessen Wert für das Ziel. Das "vergisst" einige Details und fügt Rauschen hinzu.
Es gibt jedoch eine Ausnahme: Da das 2X-Bild-Downsampling sehr einfach zu berechnen ist (durchschnittlich 4 Pixel, um eines zu erstellen) und für Retina- / HiDPI-Pixel verwendet wird, wird dieser Fall ordnungsgemäß behandelt. Der Browser verwendet 4 Pixel für die Erstellung einer-.
ABER ... wenn Sie mehrmals ein 2X-Downsampling verwenden, werden Sie mit dem Problem konfrontiert, dass die aufeinanderfolgenden Rundungsfehler zu viel Rauschen verursachen.
Was noch schlimmer ist, Sie werden die Größe nicht immer um eine Zweierpotenz ändern, und die Größenänderung auf die nächste Potenz + eine letzte Größenänderung ist sehr laut.
Was Sie suchen, ist ein pixelgenaues Downsampling, dh ein erneutes Abtasten des Bildes, bei dem alle Eingabepixel berücksichtigt werden - unabhängig von der Skalierung.
Dazu müssen wir für jedes Eingabepixel seinen Beitrag zu einem, zwei oder vier Zielpixeln berechnen, je nachdem, ob sich die skalierte Projektion der Eingabepixel direkt innerhalb eines Zielpixels befindet, einen X-Rand, einen Y-Rand oder beides überlappt .
(Ein Schema wäre hier schön, aber ich habe keines.)
Hier ist ein Beispiel für die Leinwandskala im Vergleich zu meiner pixelgenauen Skala im Maßstab 1/3 eines Zombats.
Beachten Sie, dass das Bild möglicherweise in Ihrem Browser skaliert wird und von SO .jpegisiert wird.
Wir sehen jedoch, dass es besonders im Gras hinter dem Wombat und in den Zweigen rechts viel weniger Lärm gibt. Das Geräusch im Fell macht es kontrastreicher, aber es sieht so aus, als hätte er weiße Haare - im Gegensatz zum Quellbild -.
Das rechte Bild ist weniger eingängig, aber definitiv schöner.
Hier ist der Code für das pixelgenaue Downscaling:
Geigenergebnis: http://jsfiddle.net/gamealchemist/r6aVp/embedded/result/
Geige selbst: http://jsfiddle.net/gamealchemist/r6aVp/
Es ist ziemlich speichergierig, da ein Float-Puffer erforderlich ist, um die Zwischenwerte des Zielbilds zu speichern (-> wenn wir den Ergebnisbereich zählen, verwenden wir in diesem Algorithmus das 6-fache des Speichers des Quellbilds).
Es ist auch ziemlich teuer, da jedes Quellpixel unabhängig von der Zielgröße verwendet wird und wir für getImageData / putImageDate bezahlen müssen, auch ziemlich langsam.
In diesem Fall gibt es jedoch keine Möglichkeit, schneller zu sein, als jeden Quellwert zu verarbeiten, und die Situation ist nicht so schlimm: Für mein 740 * 556-Bild eines Wombats dauert die Verarbeitung zwischen 30 und 40 ms.
quelle
Schnelles Canvas-Resample mit guter Qualität: http://jsfiddle.net/9g9Nv/442/
Update: Version 2.0 (schneller, Web Worker + übertragbare Objekte) - https://github.com/viliusle/Hermite-resize
quelle
Vorschlag 1 - Verlängerung der Prozessleitung
Sie können Step-Down verwenden, wie ich in den Links beschreibe, auf die Sie verweisen, aber Sie scheinen sie falsch zu verwenden.
Ein Schritt nach unten ist nicht erforderlich, um Bilder auf Verhältnisse über 1: 2 zu skalieren (normalerweise, aber nicht beschränkt auf). Hier müssen Sie eine drastische Verkleinerung vornehmen, die Sie je nach Bildinhalt in zwei (und selten mehr) Schritte aufteilen müssen (insbesondere, wenn hohe Frequenzen wie dünne Linien auftreten).
Jedes Mal, wenn Sie ein Bild herunterproben, verlieren Sie Details und Informationen. Sie können nicht erwarten, dass das resultierende Bild so klar wie das Original ist.
Wenn Sie dann die Bilder in vielen Schritten verkleinern, verlieren Sie insgesamt viele Informationen und das Ergebnis ist schlecht, wie Sie bereits bemerkt haben.
Versuchen Sie es mit nur einem zusätzlichen Schritt oder oben mit zwei.
Faltungen
Im Falle von Photoshop beachten Sie, dass nach dem erneuten Abtasten des Bildes eine Faltung angewendet wird, z. B. Schärfen. Es findet nicht nur eine bi-kubische Interpolation statt. Um Photoshop vollständig zu emulieren, müssen wir auch die Schritte hinzufügen, die Photoshop ausführt (mit der Standardeinstellung).
In diesem Beispiel verwende ich meine ursprüngliche Antwort, auf die Sie in Ihrem Beitrag verweisen. Ich habe jedoch eine schärfere Faltung hinzugefügt, um die Qualität als Nachbearbeitungsprozess zu verbessern (siehe Demo unten).
Hier ist der Code zum Hinzufügen eines Schärfefilters (er basiert auf einem generischen Faltungsfilter - ich habe die Gewichtsmatrix zum Schärfen sowie einen Mischungsfaktor zum Anpassen der Aussprache des Effekts eingefügt):
Verwendung:
Der
mixFactor
Wert liegt zwischen [0.0, 1.0] und ermöglicht es Ihnen, den Schärfeeffekt herunterzuspielen - Faustregel: Je weniger Größe, desto weniger Effekt wird benötigt.Funktion (basierend auf diesem Snippet ):
Das Ergebnis der Verwendung dieser Kombination ist:
ONLINE DEMO HIER
Abhängig davon, wie viel des Schärfens Sie der Mischung hinzufügen möchten, können Sie das Ergebnis von Standard "verschwommen" bis sehr scharf erhalten:
Vorschlag 2 - Implementierung eines Algorithmus auf niedriger Ebene
Wenn Sie qualitativ das beste Ergebnis erzielen möchten, müssen Sie auf niedriger Ebene arbeiten und beispielsweise diesen brandneuen Algorithmus implementieren, um dies zu tun.
Siehe Interpolationsabhängiges Bild-Downsampling (2011) von IEEE.
Hier ist ein Link zum vollständigen Artikel (PDF) .
Derzeit gibt es keine Implementierungen dieses Algorithmus in JavaScript AFAIK. Sie müssen also eine Hand voll haben, wenn Sie sich dieser Aufgabe stellen möchten.
Das Wesentliche ist (Auszüge aus dem Papier):
Abstrakt
(Alle Details, Formeln usw. finden Sie unter dem angegebenen Link.)
quelle
Wenn Sie nur Leinwand verwenden möchten, erzielen Sie das beste Ergebnis mit mehreren Abwärtsschritten. Aber das ist noch nicht gut genug. Für eine bessere Qualität benötigen Sie eine reine js-Implementierung. Wir haben gerade pica - Hochgeschwindigkeits-Downscaler mit variabler Qualität / Geschwindigkeit veröffentlicht. Kurz gesagt, es ändert die Größe von 1280 * 1024px in ~ 0,1s und 5000 * 3000px in 1s mit höchster Qualität (Lanczos-Filter mit 3 Lappen). Pica hat eine Demo , in der Sie mit Ihren Bildern und Qualitätsstufen spielen und sie sogar auf Mobilgeräten ausprobieren können.
Pica hat noch keine unscharfe Maske, aber die wird sehr bald hinzugefügt. Das ist viel einfacher als die Implementierung eines Hochgeschwindigkeits-Faltungsfilters zur Größenänderung.
quelle
Warum die Leinwand verwenden, um die Größe von Bildern zu ändern? Moderne Browser verwenden alle die bikubische Interpolation - den gleichen Prozess wie Photoshop (wenn Sie es richtig machen) - und das schneller als der Canvas-Prozess. Geben Sie einfach die gewünschte Bildgröße an (verwenden Sie nur eine Dimension, Höhe oder Breite, um die Größe proportional zu ändern).
Dies wird von den meisten Browsern unterstützt, einschließlich späterer Versionen des IE. Frühere Versionen erfordern möglicherweise browserspezifisches CSS .
Eine einfache Funktion (mit jQuery) zum Ändern der Bildgröße lautet wie folgt:
Verwenden Sie dann einfach den zurückgegebenen Wert, um die Größe des Bilds in einer oder beiden Dimensionen zu ändern.
Natürlich gibt es verschiedene Verfeinerungen, die Sie vornehmen könnten, aber dies erledigt den Job.
Fügen Sie den folgenden Code in die Konsole dieser Seite ein und beobachten Sie, was mit den Gravataren passiert:
quelle
Nicht die richtige Antwort für Leute, die wirklich die Größe des Bildes selbst ändern müssen, sondern nur die Dateigröße verkleinern möchten .
Ich hatte ein Problem mit Bildern "direkt von der Kamera", die meine Kunden häufig in "unkomprimiertem" JPEG hochgeladen haben.
Nicht so bekannt ist, dass die Leinwand (in den meisten Browsern 2017) unterstützt, die Qualität von JPEG zu ändern
Mit diesem Trick könnte ich 4k x 3k Bilder mit> 10Mb auf 1 oder 2Mb reduzieren, sicher, dass es von Ihren Bedürfnissen abhängt.
Schau hier
quelle
Hier ist ein wiederverwendbarer Angular-Dienst für die Größenänderung von Bildern / Leinwand in hoher Qualität: https://gist.github.com/fisch0920/37bac5e741eaec60e983
Der Service unterstützt die Faltung von Lanczos und das schrittweise Downscaling. Der Faltungsansatz bietet eine höhere Qualität auf Kosten der Langsamkeit, während der schrittweise Downscaling-Ansatz einigermaßen antialiasierte Ergebnisse liefert und erheblich schneller ist.
Anwendungsbeispiel:
quelle
Dies ist der verbesserte Hermite-Größenänderungsfilter, der 1 Arbeiter verwendet, damit das Fenster nicht einfriert.
https://github.com/calvintwr/blitz-hermite-resize
quelle
Ich habe eine Lösung gefunden, die nicht direkt auf die Pixeldaten zugreifen und diese durchlaufen muss, um das Downsampling durchzuführen. Abhängig von der Größe des Bildes kann dies sehr ressourcenintensiv sein, und es ist besser, die internen Algorithmen des Browsers zu verwenden.
Die Funktion drawImage () verwendet eine Resampling-Methode mit linearer Interpolation und nächstem Nachbarn. Das funktioniert gut, wenn Sie nicht mehr als die Hälfte der Originalgröße verkleinern .
Wenn Sie eine Schleife ausführen, um jeweils nur die Hälfte der Größe zu ändern, sind die Ergebnisse recht gut und viel schneller als der Zugriff auf Pixeldaten.
Diese Funktion wird jeweils auf die Hälfte heruntergesampelt, bis die gewünschte Größe erreicht ist:
quelle
Vielleicht können Sie dies versuchen, was ich immer in meinem Projekt verwende. Auf diese Weise erhalten Sie nicht nur qualitativ hochwertige Bilder, sondern auch jedes andere Element auf Ihrer Leinwand.
quelle
anstelle von .85 , wenn wir 1.0 hinzufügen . Sie erhalten eine genaue Antwort.
Sie können ein klares und helles Bild erhalten. Bitte prüfen
quelle
Ich versuche wirklich zu vermeiden, Bilddaten zu durchlaufen, insbesondere bei größeren Bildern. Daher habe ich eine ziemlich einfache Möglichkeit gefunden, die Bildgröße mit ein paar zusätzlichen Schritten ohne Einschränkungen oder Einschränkungen angemessen zu reduzieren. Diese Routine geht auf den niedrigstmöglichen halben Schritt vor der gewünschten Zielgröße zurück. Dann skaliert es auf die doppelte Zielgröße und dann wieder auf die Hälfte. Klingt zunächst lustig, aber die Ergebnisse sind erstaunlich gut und gehen schnell dahin.
quelle
context.scale(xScale, yScale)
quelle
DEMO : Ändern der Größe von Bildern mit JS und HTML Canvas Demo Fiddler.
Möglicherweise finden Sie drei verschiedene Methoden, um die Größe zu ändern. Diese helfen Ihnen zu verstehen, wie der Code funktioniert und warum.
https://jsfiddle.net/1b68eLdr/93089/
Der vollständige Code der Demo und der TypeScript-Methode, die Sie möglicherweise in Ihrem Code verwenden möchten, finden Sie im GitHub-Projekt.
https://github.com/eyalc4/ts-image-resizer
Dies ist der endgültige Code:
quelle