Aby osiągnąć lepsze wyniki, możesz zastosować obniżanie poziomu. Większość przeglądarek wydaje się używać interpolacji liniowej zamiast dwuczęściowej podczas zmiany rozmiaru obrazów.
( Aktualizacja Do specyfikacji dodano właściwość jakości, imageSmoothingQuality
która jest obecnie dostępna tylko w przeglądarce Chrome).
O ile nie wybierze się żadnego wygładzania lub najbliższego sąsiada, przeglądarka zawsze interpoluje obraz po zmniejszeniu go, ponieważ działa to jako filtr dolnoprzepustowy, aby uniknąć aliasingu.
Bi-linear wykorzystuje 2x2 piksele do wykonania interpolacji, podczas gdy bi-sześcienny wykorzystuje 4x4, więc wykonując to krokowo, można zbliżyć się do wyniku dwu-sześciennego, używając interpolacji bi-liniowej, jak widać na wynikowych obrazach.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function () {
canvas.height = canvas.width * (img.height / img.width);
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
oc.width = img.width * 0.5;
oc.height = img.height * 0.5;
octx.drawImage(img, 0, 0, oc.width, oc.height);
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
}
img.src = "//i.imgur.com/SHo6Fub.jpg";
<img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234">
<canvas id="canvas" width=300></canvas>
W zależności od tego, jak drastyczna jest zmiana rozmiaru, możesz pominąć krok 2, jeśli różnica jest mniejsza.
W wersji demonstracyjnej możesz zobaczyć, że nowy wynik jest teraz bardzo podobny do elementu obrazu.
Ponieważ skrzypce Trung Le Nguyen Nhat w ogóle nie są poprawne (w ostatnim kroku używa po prostu oryginalnego obrazu)
, napisałem własne ogólne skrzypce z porównaniem wydajności:
SKRZYPCE
Zasadniczo jest to:
img.onload = function() { var canvas = document.createElement('canvas'), ctx = canvas.getContext("2d"), oc = document.createElement('canvas'), octx = oc.getContext('2d'); canvas.width = width; // destination canvas size canvas.height = canvas.width * img.height / img.width; var cur = { width: Math.floor(img.width * 0.5), height: Math.floor(img.height * 0.5) } oc.width = cur.width; oc.height = cur.height; octx.drawImage(img, 0, 0, cur.width, cur.height); while (cur.width * 0.5 > width) { cur = { width: Math.floor(cur.width * 0.5), height: Math.floor(cur.height * 0.5) }; octx.drawImage(oc, 0, 0, cur.width * 2, cur.height * 2, 0, 0, cur.width, cur.height); } ctx.drawImage(oc, 0, 0, cur.width, cur.height, 0, 0, canvas.width, canvas.height); }
źródło
Stworzyłem usługę Angular wielokrotnego użytku do obsługi wysokiej jakości zmiany rozmiaru obrazów / płócien dla każdego, kto jest zainteresowany: https://gist.github.com/transitive-bullshit/37bac5e741eaec60e983
Usługa obejmuje dwa rozwiązania, ponieważ oba mają swoje wady / zalety. Podejście splotu lanczosa zapewnia wyższą jakość kosztem wolniejszego, podczas gdy podejście stopniowego zmniejszania skali daje rozsądnie antyaliasowane wyniki i jest znacznie szybsze.
Przykładowe użycie:
angular.module('demo').controller('ExampleCtrl', function (imageService) { // EXAMPLE USAGE // NOTE: it's bad practice to access the DOM inside a controller, // but this is just to show the example usage. // resize by lanczos-sinc filter imageService.resize($('#myimg')[0], 256, 256) .then(function (resizedImage) { // do something with resized image }) // resize by stepping down image size in increments of 2x imageService.resizeStep($('#myimg')[0], 256, 256) .then(function (resizedImage) { // do something with resized image }) })
źródło
Chociaż niektóre z tych fragmentów kodu są krótkie i działają, nie są one łatwe do naśladowania i zrozumienia.
Ponieważ nie jestem fanem „kopiuj-wklej” z przepełnienia stosu, chciałbym, aby programiści zrozumieli kod, który umieszczają w swoim oprogramowaniu. Mam nadzieję, że poniższe informacje okażą się przydatne.
DEMO : Zmiana rozmiaru obrazów za pomocą skrzynek JS i HTML Canvas Demo.
Możesz znaleźć 3 różne metody zmiany rozmiaru, które pomogą ci zrozumieć, jak działa kod i dlaczego.
https://jsfiddle.net/1b68eLdr/93089/
Pełny kod zarówno demonstracji, jak i metody TypeScript, której możesz chcieć użyć w swoim kodzie, można znaleźć w projekcie GitHub.
https://github.com/eyalc4/ts-image-resizer
To jest ostateczny kod:
export class ImageTools { base64ResizedImage: string = null; constructor() { } ResizeImage(base64image: string, width: number = 1080, height: number = 1080) { let img = new Image(); img.src = base64image; img.onload = () => { // Check if the image require resize at all if(img.height <= height && img.width <= width) { this.base64ResizedImage = base64image; // TODO: Call method to do something with the resize image } else { // Make sure the width and height preserve the original aspect ratio and adjust if needed if(img.height > img.width) { width = Math.floor(height * (img.width / img.height)); } else { height = Math.floor(width * (img.height / img.width)); } let resizingCanvas: HTMLCanvasElement = document.createElement('canvas'); let resizingCanvasContext = resizingCanvas.getContext("2d"); // Start with original image size resizingCanvas.width = img.width; resizingCanvas.height = img.height; // Draw the original image on the (temp) resizing canvas resizingCanvasContext.drawImage(img, 0, 0, resizingCanvas.width, resizingCanvas.height); let curImageDimensions = { width: Math.floor(img.width), height: Math.floor(img.height) }; let halfImageDimensions = { width: null, height: null }; // Quickly reduce the dize by 50% each time in few iterations until the size is less then // 2x time the target size - the motivation for it, is to reduce the aliasing that would have been // created with direct reduction of very big image to small image while (curImageDimensions.width * 0.5 > width) { // Reduce the resizing canvas by half and refresh the image halfImageDimensions.width = Math.floor(curImageDimensions.width * 0.5); halfImageDimensions.height = Math.floor(curImageDimensions.height * 0.5); resizingCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, halfImageDimensions.width, halfImageDimensions.height); curImageDimensions.width = halfImageDimensions.width; curImageDimensions.height = halfImageDimensions.height; } // Now do final resize for the resizingCanvas to meet the dimension requirments // directly to the output canvas, that will output the final image let outputCanvas: HTMLCanvasElement = document.createElement('canvas'); let outputCanvasContext = outputCanvas.getContext("2d"); outputCanvas.width = width; outputCanvas.height = height; outputCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, width, height); // output the canvas pixels as an image. params: format, quality this.base64ResizedImage = outputCanvas.toDataURL('image/jpeg', 0.85); // TODO: Call method to do something with the resize image } }; }}
źródło
Stworzyłem bibliotekę, która pozwala obniżyć dowolny procent, zachowując wszystkie dane dotyczące kolorów.
https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js
Ten plik, który możesz dołączyć do przeglądarki. Wyniki będą wyglądać jak Photoshop lub Magia Obrazów, zachowując wszystkie dane kolorów, uśredniając piksele, zamiast pobierać pobliskie i upuszczać inne. Nie używa wzoru do odgadywania średnich, bierze dokładną średnią.
źródło
Na podstawie odpowiedzi K3N przepisuję kod ogólnie dla każdego
var oc = document.createElement('canvas'), octx = oc.getContext('2d'); oc.width = img.width; oc.height = img.height; octx.drawImage(img, 0, 0); while (oc.width * 0.5 > width) { oc.width *= 0.5; oc.height *= 0.5; octx.drawImage(oc, 0, 0, oc.width, oc.height); } oc.width = width; oc.height = oc.width * img.height / img.width; octx.drawImage(img, 0, 0, oc.width, oc.height);
ZAKTUALIZUJ DEMO JSFIDDLE
Oto moja DEMO ONLINE
źródło
Nie rozumiem, dlaczego nikt nie sugeruje
createImageBitmap
.createImageBitmap( document.getElementById('image'), { resizeWidth: 300, resizeHeight: 234, resizeQuality: 'high' } ) .then(imageBitmap => document.getElementById('canvas').getContext('2d').drawImage(imageBitmap, 0, 0) );
działa pięknie (zakładając, że ustawisz identyfikatory dla obrazu i płótna).
źródło
Napisałem małe narzędzie js do przycinania i zmiany rozmiaru obrazu na interfejsie użytkownika. Oto link do projektu GitHub. Możesz także pobrać blob z końcowego obrazu, aby go wysłać.
import imageSqResizer from './image-square-resizer.js' let resizer = new imageSqResizer( 'image-input', 300, (dataUrl) => document.getElementById('image-output').src = dataUrl; ); //Get blob let formData = new FormData(); formData.append('files[0]', resizer.blob); //get dataUrl document.getElementById('image-output').src = resizer.dataUrl;
źródło