Saya menggunakan html5 canvas elemen untuk mengubah ukuran gambar im browser saya. Ternyata bahwa kualitas sangat rendah. Saya menemukan ini: https://stackoverflow.com/questions/7615009/disable-interpolation-when-scaling-a-canvas tapi itu tidak membantu untuk meningkatkan kualitas.
Di bawah ini adalah css dan js kode serta gambar scalled dengan Photoshop dan skala di atas kanvas API.
Apa yang harus saya lakukan untuk mendapatkan kualitas yang optimal ketika skala gambar dalam browser?
Catatan: saya ingin menurunkan gambar besar untuk satu kecil, mengubah warna di kanvas dan mengirim hasil dari kanvas ke server.
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);
});
Gambar diubah ukurannya dengan photoshop:
Gambar diubah ukurannya di atas kanvas:
Edit:
Saya mencoba untuk membuat downscaling dalam lebih dari satu langkah-langkah seperti yang diusulkan dalam:
https://stackoverflow.com/questions/2303690/resizing-an-image-in-an-html5-canvas dan https://stackoverflow.com/questions/17861447/html5-canvas-drawimage-how-to-apply-antialiasing
Ini adalah fungsi yang saya telah digunakan:
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);
}
Berikut ini adalah hasilnya jika saya menggunakan 2 step down sizing:
Berikut ini adalah hasilnya jika saya menggunakan 3 step down sizing:
Berikut ini adalah hasilnya jika saya menggunakan 4 step down sizing:
Berikut ini adalah hasilnya jika saya menggunakan 20 step down sizing:
Catatan: ternyata dari 1 langkah 2 langkah ada peningkatan besar dalam kualitas gambar, tetapi lebih banyak langkah-langkah yang anda tambahkan ke proses yang lebih fuzzy gambar menjadi.
Apakah ada cara untuk memecahkan masalah bahwa gambar akan lebih fuzzy semakin banyak langkah yang anda tambahkan?
Edit 2013-10-04: saya mencoba algoritma GameAlchemist. Berikut ini adalah hasil dibandingkan dengan Photoshop.
Gambar PhotoShop:
GameAlchemist's Algoritma:
Sejak masalah anda adalah untuk downscale gambar anda, tidak ada gunanya berbicara tentang interpolasi -yang adalah tentang menciptakan pixel. Masalah di sini adalah downsampling.
Untuk downsample gambar, kita perlu mengubah setiap persegi dari p * p piksel dalam citra asli ke dalam satu pixel dalam gambar tujuan.
Untuk pertunjukan alasan Browser tidak sangat sederhana downsampling : untuk membangun gambar yang lebih kecil, mereka hanya akan memilih SATU pixel dalam sumber dan penggunaan nilai untuk tujuan. yang 'lupa' beberapa rincian dan menambah kebisingan.
Belum ada's pengecualian bahwa : sejak 2X gambar downsampling sangat sederhana untuk menghitung (rata-rata 4 piksel untuk membuat satu) dan digunakan untuk retina/HiDPI piksel, kasus ini ditangani dengan baik -Browser tidak membuat penggunaan 4 piksel untuk membuat satu.
TAPI... jika anda menggunakan beberapa waktu 2X downsampling, anda'll menghadapi masalah yang berturut-turut kesalahan pembulatan akan menambah terlalu banyak noise. Apa's lebih buruk, anda tidak't selalu resize oleh dua kekuatan, dan ukuran ke listrik terdekat + a lalu resize sangat bising.
Apa yang anda cari adalah pixel-sempurna downsampling, yaitu : re-sampling dari gambar yang akan mengambil semua input piksel ke akun -fasilitas skala-. Untuk melakukan itu kita harus menghitung, untuk setiap input pixel, kontribusinya untuk satu, dua, atau empat tujuan piksel tergantung cuaca skala proyeksi input piksel yang tepat dalam tujuan piksel, tumpang tindih X perbatasan, yang Y perbatasan, atau keduanya. ( Skema akan bagus di sini, tapi aku don't memiliki satu. )
Berikut ini's contoh kanvas skala vs pixel saya sempurna skala pada 1/3 skala zombat.
Perhatikan bahwa gambar mungkin bisa di turunkan di Browser anda, dan .jpegized oleh S. O.. Namun kita melihat bahwa ada's jauh lebih sedikit kebisingan terutama di rumput di belakang wombat, dan cabang-cabang di sebelah kanannya. Kebisingan di bulu membuatnya lebih kontras, tapi sepertinya dia's punya rambut putih -tidak seperti sumber gambar-. Gambar kanan adalah kurang catchy tapi pasti lebih bagus.
Berikut ini's kode untuk melakukan pixel yang sempurna downscaling :
biola hasil :
biola itu sendiri : http://jsfiddle.net/gamealchemist/r6aVp/// scales the image by (float) scale < 1
// returns a canvas containing the scaled image.
function downScaleImage(img, scale) {
var imgCV = document.createElement('canvas');
imgCV.width = img.width;
imgCV.height = img.height;
var imgCtx = imgCV.getContext('2d');
imgCtx.drawImage(img, 0, 0);
return downScaleCanvas(imgCV, scale);
}
// scales the canvas by (float) scale < 1
// returns a new canvas containing the scaled image.
function downScaleCanvas(cv, scale) {
if (!(scale < 1) || !(scale > 0)) throw ('scale must be a positive number <1 ');
var sqScale = scale * scale; // square scale = area of source pixel within target
var sw = cv.width; // source image width
var sh = cv.height; // source image height
var tw = Math.floor(sw * scale); // target image width
var th = Math.floor(sh * scale); // target image height
var sx = 0, sy = 0, sIndex = 0; // source x,y, index within source array
var tx = 0, ty = 0, yIndex = 0, tIndex = 0; // target x,y, x,y index within target array
var tX = 0, tY = 0; // rounded tx, ty
var w = 0, nw = 0, wx = 0, nwx = 0, wy = 0, nwy = 0; // weight / next weight x / y
// weight is weight of current source point within target.
// next weight is weight of current source point within next target's point.
var crossX = false; // does scaled px cross its current px right border ?
var crossY = false; // does scaled px cross its current px bottom border ?
var sBuffer = cv.getContext('2d').
getImageData(0, 0, sw, sh).data; // source buffer 8 bit rgba
var tBuffer = new Float32Array(3 * tw * th); // target buffer Float32 rgb
var sR = 0, sG = 0, sB = 0; // source's current point r,g,b
/* untested !
var sA = 0; //source alpha */
for (sy = 0; sy < sh; sy++) {
ty = sy * scale; // y src position within target
tY = 0 | ty; // rounded : target pixel's y
yIndex = 3 * tY * tw; // line index within target array
crossY = (tY != (0 | ty + scale));
if (crossY) { // if pixel is crossing botton target pixel
wy = (tY + 1 - ty); // weight of point within target pixel
nwy = (ty + scale - tY - 1); // ... within y+1 target pixel
}
for (sx = 0; sx < sw; sx++, sIndex += 4) {
tx = sx * scale; // x src position within target
tX = 0 | tx; // rounded : target pixel's x
tIndex = yIndex + tX * 3; // target pixel index within target array
crossX = (tX != (0 | tx + scale));
if (crossX) { // if pixel is crossing target pixel's right
wx = (tX + 1 - tx); // weight of point within target pixel
nwx = (tx + scale - tX - 1); // ... within x+1 target pixel
}
sR = sBuffer[sIndex ]; // retrieving r,g,b for curr src px.
sG = sBuffer[sIndex + 1];
sB = sBuffer[sIndex + 2];
/* !! untested : handling alpha !!
sA = sBuffer[sIndex + 3];
if (!sA) continue;
if (sA != 0xFF) {
sR = (sR * sA) >> 8; // or use /256 instead ??
sG = (sG * sA) >> 8;
sB = (sB * sA) >> 8;
}
*/
if (!crossX && !crossY) { // pixel does not cross
// just add components weighted by squared scale.
tBuffer[tIndex ] += sR * sqScale;
tBuffer[tIndex + 1] += sG * sqScale;
tBuffer[tIndex + 2] += sB * sqScale;
} else if (crossX && !crossY) { // cross on X only
w = wx * scale;
// add weighted component for current px
tBuffer[tIndex ] += sR * w;
tBuffer[tIndex + 1] += sG * w;
tBuffer[tIndex + 2] += sB * w;
// add weighted component for next (tX+1) px
nw = nwx * scale
tBuffer[tIndex + 3] += sR * nw;
tBuffer[tIndex + 4] += sG * nw;
tBuffer[tIndex + 5] += sB * nw;
} else if (crossY && !crossX) { // cross on Y only
w = wy * scale;
// add weighted component for current px
tBuffer[tIndex ] += sR * w;
tBuffer[tIndex + 1] += sG * w;
tBuffer[tIndex + 2] += sB * w;
// add weighted component for next (tY+1) px
nw = nwy * scale
tBuffer[tIndex + 3 * tw ] += sR * nw;
tBuffer[tIndex + 3 * tw + 1] += sG * nw;
tBuffer[tIndex + 3 * tw + 2] += sB * nw;
} else { // crosses both x and y : four target points involved
// add weighted component for current px
w = wx * wy;
tBuffer[tIndex ] += sR * w;
tBuffer[tIndex + 1] += sG * w;
tBuffer[tIndex + 2] += sB * w;
// for tX + 1; tY px
nw = nwx * wy;
tBuffer[tIndex + 3] += sR * nw;
tBuffer[tIndex + 4] += sG * nw;
tBuffer[tIndex + 5] += sB * nw;
// for tX ; tY + 1 px
nw = wx * nwy;
tBuffer[tIndex + 3 * tw ] += sR * nw;
tBuffer[tIndex + 3 * tw + 1] += sG * nw;
tBuffer[tIndex + 3 * tw + 2] += sB * nw;
// for tX + 1 ; tY +1 px
nw = nwx * nwy;
tBuffer[tIndex + 3 * tw + 3] += sR * nw;
tBuffer[tIndex + 3 * tw + 4] += sG * nw;
tBuffer[tIndex + 3 * tw + 5] += sB * nw;
}
} // end for sx
} // end for sy
// create result canvas
var resCV = document.createElement('canvas');
resCV.width = tw;
resCV.height = th;
var resCtx = resCV.getContext('2d');
var imgRes = resCtx.getImageData(0, 0, tw, th);
var tByteBuffer = imgRes.data;
// convert float32 array into a UInt8Clamped Array
var pxIndex = 0; //
for (sIndex = 0, tIndex = 0; pxIndex < tw * th; sIndex += 3, tIndex += 4, pxIndex++) {
tByteBuffer[tIndex] = Math.ceil(tBuffer[sIndex]);
tByteBuffer[tIndex + 1] = Math.ceil(tBuffer[sIndex + 1]);
tByteBuffer[tIndex + 2] = Math.ceil(tBuffer[sIndex + 2]);
tByteBuffer[tIndex + 3] = 255;
}
// writing result to canvas.
resCtx.putImageData(imgRes, 0, 0);
return resCV;
}
Hal ini cukup memori serakah, karena pelampung penyangga diperlukan untuk menyimpan nilai-nilai menengah dari tujuan gambar (-> jika kita menghitung hasil kanvas, kami menggunakan 6 kali sumber gambar's memori dalam algoritma ini). Hal ini juga cukup mahal, karena masing-masing sumber pixel ini digunakan dengan tujuan, ukuran, dan kami harus membayar untuk getImageData / putImageDate, cukup lambat juga. Tapi ada's tidak ada cara untuk menjadi lebih cepat dari proses masing-masing sumber nilai dalam hal ini, dan situasi tidak seburuk itu : saya 740 * 556 gambar wombat, pengolahan membutuhkan waktu antara 30 dan 40 ms.
Cepat kanvas resample dengan kualitas yang baik:
Update: versi 2.0 (lebih cepat, pekerja web + dipindahtangankan benda) - https://github.com/viliusle/Hermite-resize
/**
* Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
*
* @param {HtmlElement} canvas
* @param {int} width
* @param {int} height
* @param {boolean} resize_canvas if true, canvas will be resized. Optional.
*/
function resample_single(canvas, width, height, resize_canvas) {
var width_source = canvas.width;
var height_source = canvas.height;
width = Math.round(width);
height = Math.round(height);
var ratio_w = width_source / width;
var ratio_h = height_source / height;
var ratio_w_half = Math.ceil(ratio_w / 2);
var ratio_h_half = Math.ceil(ratio_h / 2);
var ctx = canvas.getContext("2d");
var img = ctx.getImageData(0, 0, width_source, height_source);
var img2 = ctx.createImageData(width, height);
var data = img.data;
var data2 = img2.data;
for (var j = 0; j < height; j++) {
for (var i = 0; i < width; i++) {
var x2 = (i + j * width) * 4;
var weight = 0;
var weights = 0;
var weights_alpha = 0;
var gx_r = 0;
var gx_g = 0;
var gx_b = 0;
var gx_a = 0;
var center_y = (j + 0.5) * ratio_h;
var yy_start = Math.floor(j * ratio_h);
var yy_stop = Math.ceil((j + 1) * ratio_h);
for (var yy = yy_start; yy < yy_stop; yy++) {
var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
var center_x = (i + 0.5) * ratio_w;
var w0 = dy * dy; //pre-calc part of w
var xx_start = Math.floor(i * ratio_w);
var xx_stop = Math.ceil((i + 1) * ratio_w);
for (var xx = xx_start; xx < xx_stop; xx++) {
var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
var w = Math.sqrt(w0 + dx * dx);
if (w >= 1) {
//pixel too far
continue;
}
//hermite filter
weight = 2 * w * w * w - 3 * w * w + 1;
var pos_x = 4 * (xx + yy * width_source);
//alpha
gx_a += weight * data[pos_x + 3];
weights_alpha += weight;
//colors
if (data[pos_x + 3] < 255)
weight = weight * data[pos_x + 3] / 250;
gx_r += weight * data[pos_x];
gx_g += weight * data[pos_x + 1];
gx_b += weight * data[pos_x + 2];
weights += weight;
}
}
data2[x2] = gx_r / weights;
data2[x2 + 1] = gx_g / weights;
data2[x2 + 2] = gx_b / weights;
data2[x2 + 3] = gx_a / weights_alpha;
}
}
//clear and resize canvas
if (resize_canvas === true) {
canvas.width = width;
canvas.height = height;
} else {
ctx.clearRect(0, 0, width_source, height_source);
}
//draw
ctx.putImageData(img2, 0, 0);
}
Anda dapat menggunakan langkah-down seperti yang saya jelaskan di link yang anda lihat tapi anda muncul untuk menggunakan mereka dengan cara yang salah.
Mundur tidak diperlukan untuk mengatur skala gambar untuk rasio-rasio di atas 1:2 (biasanya, tapi tidak terbatas pada). Itu adalah di mana anda perlu untuk melakukan drastis down-scaling anda perlu membaginya dalam dua (dan jarang, lebih lanjut) langkah-langkah tergantung pada isi gambar (khususnya di mana tinggi-frekuensi seperti garis tipis terjadi).
Setiap kali anda ke bawah-contoh gambar anda akan kehilangan detail dan informasi. Anda tidak bisa mengharapkan gambar yang dihasilkan akan jernih seperti aslinya.
Jika anda maka skala bawah gambar dalam banyak langkah yang anda akan kehilangan banyak informasi total dan hasilnya akan menjadi miskin karena anda sudah melihat.
Mencoba hanya dengan satu langkah ekstra, atau di puncak dua.
Dalam kasus Photoshop melihat bahwa ini berlaku lilit setelah gambar telah di-re-sampel, seperti mempertajam. It's tidak hanya bi-kubik interpolasi yang terjadi sehingga dalam rangka untuk sepenuhnya meniru Photoshop kita perlu juga menambahkan langkah-langkah Photoshop lakukan (dengan pengaturan default).
Untuk contoh ini saya akan menggunakan saya asli jawaban yang anda lihat di posting anda, tetapi saya telah menambahkan mempertajam konvolusi untuk itu untuk meningkatkan kualitas sebagai posting proses (lihat demo di bawah).
Berikut ini adalah kode untuk menambahkan mempertajam filter (it's berdasarkan generik lilit filter - aku meletakkan berat badan matriks untuk mempertajam dalamnya serta campuran faktor untuk menyesuaikan pengucapan efek):
Penggunaan:
sharpen(context, width, height, mixFactor);
The mixFactor
adalah nilai antara [0.0, 1.0] dan memungkinkan anda melakukan mengecilkan mempertajam efek - rule-of-thumb: kurang ukuran kurang dari efek yang dibutuhkan.
Fungsi (berdasarkan cuplikan):
function sharpen(ctx, w, h, mix) {
var weights = [0, -1, 0, -1, 5, -1, 0, -1, 0],
katet = Math.round(Math.sqrt(weights.length)),
half = (katet * 0.5) |0,
dstData = ctx.createImageData(w, h),
dstBuff = dstData.data,
srcBuff = ctx.getImageData(0, 0, w, h).data,
y = h;
while(y--) {
x = w;
while(x--) {
var sy = y,
sx = x,
dstOff = (y * w + x) * 4,
r = 0, g = 0, b = 0, a = 0;
for (var cy = 0; cy < katet; cy++) {
for (var cx = 0; cx < katet; cx++) {
var scy = sy + cy - half;
var scx = sx + cx - half;
if (scy >= 0 && scy < h && scx >= 0 && scx < w) {
var srcOff = (scy * w + scx) * 4;
var wt = weights[cy * katet + cx];
r += srcBuff[srcOff] * wt;
g += srcBuff[srcOff + 1] * wt;
b += srcBuff[srcOff + 2] * wt;
a += srcBuff[srcOff + 3] * wt;
}
}
}
dstBuff[dstOff] = r * mix + srcBuff[dstOff] * (1 - mix);
dstBuff[dstOff + 1] = g * mix + srcBuff[dstOff + 1] * (1 - mix);
dstBuff[dstOff + 2] = b * mix + srcBuff[dstOff + 2] * (1 - mix)
dstBuff[dstOff + 3] = srcBuff[dstOff + 3];
}
}
ctx.putImageData(dstData, 0, 0);
}
Hasil dari menggunakan kombinasi ini akan sama:
[DEMO ONLINE DI SINI][2]
Tergantung pada berapa banyak mengasah ingin anda tambahkan ke campuran anda bisa mendapatkan hasil dari default "buram" sangat tajam:
Jika anda ingin mendapatkan hasil yang terbaik kualitas-bijaksana anda'll perlu untuk pergi ke tingkat rendah dan mempertimbangkan untuk menerapkan untuk contoh ini merek baru algoritma untuk melakukan hal ini.
Lihat Interpolasi Tergantung Gambar Downsampling (2011) dari IEEE. Berikut ini adalah link untuk kertas secara penuh (PDF).
Tidak ada implementasi dari algoritma ini dalam JavaScript AFAIK dari saat ini sehingga anda're di tangan-penuh jika anda ingin melemparkan diri pada tugas ini.
Intinya adalah (kutipan dari kertas):
Abstrak
interpolasi berorientasi adaptif down-sampling algoritma yang diusulkan untuk bit-rate rendah gambar coding dalam makalah ini. Diberikan gambar, algoritma yang telah diusulkan bisa mendapatkan gambar resolusi rendah, dari yang tinggi kualitas gambar dengan resolusi yang sama sebagai input gambar dapat diinterpolasi. Berbeda dari tradisional down-sampling algoritma, yang independen dari proses interpolasi, yang diusulkan down-sampling algoritma engsel down-sampling untuk proses interpolasi. Akibatnya, diusulkan down-sampling algoritma ini mampu mempertahankan asli informasi dari input gambar ke tingkat terbesar. Down-sampel gambar kemudian dimasukkan ke JPEG. Total variasi (TV) berdasarkan post pemrosesan ini kemudian diterapkan untuk didekompresi gambar resolusi rendah. pada Akhirnya, citra yang diproses adalah interpolasi untuk menjaga asli resolusi dari citra masukan. Hasil eksperimen verifikasi yang memanfaatkan downsampled gambar dengan algoritma yang diusulkan, yang interpolasi citra dengan kualitas yang lebih tinggi dapat dicapai. Selain itu, algoritma yang diusulkan mampu mencapai kinerja yang unggul dari JPEG untuk bit rate rendah gambar coding.
(lihat link yang disediakan bagi semua rincian, rumus dll.)
Jika anda ingin menggunakan kanvas hanya, hasil terbaik akan sama dengan beberapa downsteps. Tapi yang's tidak baik enougth belum. Untuk kualitas yang lebih baik anda perlu js murni implementasi. Kami baru saja merilis pica - kecepatan tinggi downscaler dengan variabel kualitas/kecepatan. Singkatnya, mengubah ukuran 12801024px di ~0.1 s, dan 50003000px gambar di 1s, dengan kualitas tertinggi (lanczos filter dengan 3 lobus). Pica telah demo, dimana anda dapat bermain dengan gambar anda, tingkat kualitas, dan bahkan mencobanya pada perangkat mobile.
Pica tidak memiliki unsharp mask, namun yang akan ditambahkan segera. Yang's jauh lebih mudah daripada menerapkan kecepatan tinggi lilit filter untuk mengubah ukuran.
Mengapa menggunakan kanvas untuk mengubah ukuran gambar? Browser Modern semua menggunakan bicubic interpolasi — proses yang sama yang digunakan oleh Photoshop (jika anda're melakukannya dengan benar) — dan mereka melakukannya lebih cepat dari kanvas proses. Hanya menentukan ukuran gambar yang anda inginkan (hanya menggunakan satu dimensi, tinggi atau lebar, untuk mengubah ukuran proporsional).
Hal ini didukung oleh sebagian besar browser, termasuk versi IE. Versi sebelumnya mungkin memerlukan browser tertentu CSS.
Fungsi sederhana (menggunakan jQuery) untuk mengubah ukuran sebuah gambar akan menjadi seperti ini:
function resizeImage(img, percentage) {
var coeff = percentage/100,
width = $(img).width(),
height = $(img).height();
return {"width": width*coeff, "height": height*coeff}
}
Kemudian hanya menggunakan nilai yang dikembalikan untuk mengubah ukuran gambar dalam satu atau dua dimensi.
Jelas ada berbagai penyempurnaan anda bisa membuat, tapi ini mendapatkan pekerjaan yang dilakukan.
Paste kode berikut ke konsol dari halaman ini dan melihat apa yang terjadi ke gravatars:
function resizeImage(img, percentage) {
var coeff = percentage/100,
width = $(img).width(),
height = $(img).height();
return {"width": width*coeff, "height": height*coeff}
}
$('.user-gravatar32 img').each(function(){
var newDimensions = resizeImage( this, 150);
this.style.width = newDimensions.width + "px";
this.style.height = newDimensions.height + "px";
});
Bukan jawaban yang tepat bagi orang-orang yang benar-benar perlu untuk mengubah ukuran gambar itu sendiri, tapi hanya untuk mengecilkan ukuran file.
Saya punya masalah dengan "langsung dari kamera" gambar, bahwa pelanggan saya sering di-upload di "terkompresi" JPEG.
Tidak begitu dikenal, bahwa kanvas mendukung (di sebagian besar browser 2017) untuk mengubah kualitas JPEG
data=canvas.toDataURL('image/jpeg', .85) # [1..0] default 0.92
Dengan trik ini saya bisa mengurangi 4k x 3k foto dengan >10 mb untuk 1 atau 2 mb, pasti itu tergantung pada kebutuhan anda.
Berikut ini adalah reusable Sudut layanan untuk kualitas gambar yang tinggi / canvas ukuran: https://gist.github.com/fisch0920/37bac5e741eaec60e983
Layanan yang mendukung lanczos konvolusi dan langkah-bijak downscaling. Konvolusi pendekatan kualitas yang lebih tinggi dengan biaya yang lebih lambat, sedangkan langkah-bijak downscaling pendekatan menghasilkan cukup antialiased hasil dan secara signifikan lebih cepat.
Contoh penggunaan:
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
})
})
Saya menemukan solusi yang doesn't perlu untuk mengakses langsung data pixel dan loop melalui itu untuk melakukan downsampling. Tergantung pada ukuran dari gambar ini dapat menjadi sumber daya yang sangat intensif, dan akan lebih baik untuk menggunakan browser's algoritma internal.
The drawImage() menggunakan fungsi linear-interpolasi nearest-neighbor metode resampling. Yang bekerja dengan baik ketika anda tidak resize turun lebih dari setengah ukuran aslinya.
Jika anda loop untuk hanya mengubah ukuran max satu setengah pada suatu waktu, hasilnya akan sangat baik, dan jauh lebih cepat daripada mengakses data pixel.
Fungsi ini downsample ke setengah pada suatu waktu sampai mencapai ukuran yang diinginkan:
function resize_image( src, dst, type, quality ) {
var tmp = new Image(),
canvas, context, cW, cH;
type = type || 'image/jpeg';
quality = quality || 0.92;
cW = src.naturalWidth;
cH = src.naturalHeight;
tmp.src = src.src;
tmp.onload = function() {
canvas = document.createElement( 'canvas' );
cW /= 2;
cH /= 2;
if ( cW < src.width ) cW = src.width;
if ( cH < src.height ) cH = src.height;
canvas.width = cW;
canvas.height = cH;
context = canvas.getContext( '2d' );
context.drawImage( tmp, 0, 0, cW, cH );
dst.src = canvas.toDataURL( type, quality );
if ( cW <= src.width || cH <= src.height )
return;
tmp.src = dst.src;
}
}
// The images sent as parameters can be in the DOM or be image objects
resize_image( $( '#original' )[0], $( '#smaller' )[0] );
Mungkin pria anda dapat mencoba ini, yang selalu saya gunakan di proyek saya.Dengan cara ini anda tidak hanya mendapatkan kualitas gambar yang tinggi ,namun unsur lainnya pada kanvas anda.
/*
* @parame canvas => canvas object
* @parame rate => the pixel quality
*/
function setCanvasSize(canvas, rate) {
const scaleRate = rate;
canvas.width = window.innerWidth * scaleRate;
canvas.height = window.innerHeight * scaleRate;
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
canvas.getContext('2d').scale(scaleRate, scaleRate);
}
DEMO: mengubah Ukuran gambar dengan JS dan HTML Canvas Demo fiddler.
Anda mungkin menemukan 3 metode yang berbeda untuk melakukan hal ini resize, yang akan membantu anda memahami bagaimana kode bekerja dan mengapa.
Kode penuh dari kedua demo, dan Ketangkasan metode yang anda mungkin ingin menggunakan kode anda, yang dapat ditemukan di GitHub proyek.
https://github.com/eyalc4/ts-image-resizer
Ini adalah kode akhir:
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 size 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
}
};
}}
konteks.skala(xScale, yScale)
<canvas id="c"></canvas>
<hr/>
<img id="i" />
<script>
var i = document.getElementById('i');
i.onload = function(){
var width = this.naturalWidth,
height = this.naturalHeight,
canvas = document.getElementById('c'),
ctx = canvas.getContext('2d');
canvas.width = Math.floor(width / 2);
canvas.height = Math.floor(height / 2);
ctx.scale(0.5, 0.5);
ctx.drawImage(this, 0, 0);
ctx.rect(0,0,500,500);
ctx.stroke();
// restore original 1x1 scale
ctx.scale(2, 2);
ctx.rect(0,0,500,500);
ctx.stroke();
};
i.src = 'https://static.md/b70a511140758c63f07b618da5137b5d.png';
</script>