This programming guide is only available in Vietnamese. Switch to Vietnamese to read the full article.
Pixel manipulation cho phép bạn truy cập và sửa đổi từng pixel trên canvas — mở ra khả năng tạo image filters, hiệu ứng đặc biệt và công cụ xử lý ảnh ngay trên trình duyệt.
1. ImageData Object
getImageData() trả về object chứa mảng pixel data dạng Uint8ClampedArray. Mỗi pixel gồm 4 giá trị RGBA (0-255).
// Lấy pixel data từ canvas
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray
// Data layout: [R, G, B, A, R, G, B, A, ...]
// Mỗi pixel = 4 bytes (Red, Green, Blue, Alpha)
console.log(data.length); // width * height * 4
// Truy cập pixel tại (x, y)
function getPixel(data, width, x, y) {
const index = (y * width + x) * 4;
return {
r: data[index],
g: data[index + 1],
b: data[index + 2],
a: data[index + 3]
};
}
// Set pixel tại (x, y)
function setPixel(data, width, x, y, r, g, b, a = 255) {
const index = (y * width + x) * 4;
data[index] = r;
data[index + 1] = g;
data[index + 2] = b;
data[index + 3] = a;
}
// Tạo ImageData mới (tất cả pixel transparent đen)
const newImageData = ctx.createImageData(200, 200);
// Hoặc clone size từ imageData có sẵn
const cloned = ctx.createImageData(imageData);
2. Filter: Grayscale & Sepia
Grayscale chuyển ảnh sang đen trắng. Có 2 cách: trung bình đơn giản hoặc dùng hệ số luminance (chính xác hơn). Hãy thử trực tiếp các bộ lọc khác nhau với demo bên dưới — ảnh nguồn được vẽ bằng code (không cần tải file), sau đó áp dụng filter qua việc duyệt từng pixel RGBA:
function applyGrayscale(ctx, canvas) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// Luminance formula (ITU-R BT.709)
const gray = 0.299 * data[i] + 0.587 * data[i+1] + 0.114 * data[i+2];
data[i] = gray; // R
data[i + 1] = gray; // G
data[i + 2] = gray; // B
// Alpha giữ nguyên
}
ctx.putImageData(imageData, 0, 0);
}
function applySepia(ctx, canvas) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i+1], b = data[i+2];
data[i] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189); // R
data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168); // G
data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131); // B
}
ctx.putImageData(imageData, 0, 0);
}
3. Filter: Brightness, Contrast & Invert
Các filter đơn giản thao tác trực tiếp trên giá trị pixel.
function applyBrightness(ctx, canvas, amount) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.max(0, Math.min(255, data[i] + amount));
data[i + 1] = Math.max(0, Math.min(255, data[i+1] + amount));
data[i + 2] = Math.max(0, Math.min(255, data[i+2] + amount));
}
ctx.putImageData(imageData, 0, 0);
}
function applyContrast(ctx, canvas, factor) {
// factor > 1 = tăng contrast, < 1 = giảm
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.max(0, Math.min(255, ((data[i] - 128) * factor) + 128));
data[i + 1] = Math.max(0, Math.min(255, ((data[i+1] - 128) * factor) + 128));
data[i + 2] = Math.max(0, Math.min(255, ((data[i+2] - 128) * factor) + 128));
}
ctx.putImageData(imageData, 0, 0);
}
function applyInvert(ctx, canvas) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i];
data[i + 1] = 255 - data[i + 1];
data[i + 2] = 255 - data[i + 2];
}
ctx.putImageData(imageData, 0, 0);
}
4. Convolution Filters: Blur & Edge Detection
Convolution filter áp dụng kernel matrix lên mỗi pixel, tính toán dựa trên pixel lân cận. Đây là nền tảng của blur, sharpen và edge detection.
function applyConvolution(ctx, canvas, kernel, divisor = 1) {
const src = ctx.getImageData(0, 0, canvas.width, canvas.height);
const dst = ctx.createImageData(canvas.width, canvas.height);
const w = canvas.width, h = canvas.height;
const kSize = Math.sqrt(kernel.length);
const half = Math.floor(kSize / 2);
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
let r = 0, g = 0, b = 0;
for (let ky = 0; ky < kSize; ky++) {
for (let kx = 0; kx < kSize; kx++) {
const px = Math.min(w-1, Math.max(0, x + kx - half));
const py = Math.min(h-1, Math.max(0, y + ky - half));
const idx = (py * w + px) * 4;
const weight = kernel[ky * kSize + kx];
r += src.data[idx] * weight;
g += src.data[idx + 1] * weight;
b += src.data[idx + 2] * weight;
}
}
const idx = (y * w + x) * 4;
dst.data[idx] = Math.max(0, Math.min(255, r / divisor));
dst.data[idx + 1] = Math.max(0, Math.min(255, g / divisor));
dst.data[idx + 2] = Math.max(0, Math.min(255, b / divisor));
dst.data[idx + 3] = src.data[idx + 3]; // giữ alpha
}
}
ctx.putImageData(dst, 0, 0);
}
// Box Blur 3x3
const boxBlur = [1,1,1, 1,1,1, 1,1,1];
applyConvolution(ctx, canvas, boxBlur, 9);
// Gaussian Blur 3x3
const gaussianBlur = [1,2,1, 2,4,2, 1,2,1];
applyConvolution(ctx, canvas, gaussianBlur, 16);
// Sobel Edge Detection (horizontal)
const sobelX = [-1,0,1, -2,0,2, -1,0,1];
applyConvolution(ctx, canvas, sobelX);
// Sharpen
const sharpen = [0,-1,0, -1,5,-1, 0,-1,0];
applyConvolution(ctx, canvas, sharpen);
5. Thực hành: Color Picker Tool
Xây dựng công cụ chọn màu: click vào canvas để lấy màu pixel tại vị trí đó. Di chuyển chuột trên demo
bên dưới — giá trị RGBA tại con trỏ được đọc trực tiếp qua getImageData(x, y, 1, 1) và
hiển thị real-time:
function setupColorPicker(canvas) {
const ctx = canvas.getContext('2d');
// Load ảnh lên canvas
const img = new Image();
img.crossOrigin = 'anonymous'; // cần cho getImageData
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
};
img.src = 'photo.jpg';
// Click handler
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = Math.floor((e.clientX - rect.left) * scaleX);
const y = Math.floor((e.clientY - rect.top) * scaleY);
// Lấy pixel tại vị trí click
const pixel = ctx.getImageData(x, y, 1, 1).data;
const [r, g, b, a] = pixel;
// Chuyển sang HEX
const hex = '#' + [r, g, b].map(v =>
v.toString(16).padStart(2, '0')
).join('');
console.log(`RGB: (${r}, ${g}, ${b})`);
console.log(`HEX: ${hex}`);
console.log(`Alpha: ${a}`);
// Hiển thị preview swatch
const swatch = document.getElementById('colorSwatch');
swatch.style.backgroundColor = hex;
document.getElementById('colorValue').textContent = hex;
});
}
6. Câu hỏi trắc nghiệm ôn tập
Trắc nghiệm 1: Pixel index
Pixel tại (x=10, y=5) trên canvas width=100, index trong data array là bao nhiêu?
Trắc nghiệm 2: Uint8ClampedArray
Nếu gán data[i] = 300, giá trị thực tế sẽ là?
Trắc nghiệm 3: Convolution kernel
Box blur 3x3 dùng divisor bằng bao nhiêu?
Tải file code thực hành minh họa bài học
File script tổng hợp các ví dụ về pixel manipulation, filters và color picker:
Tải về canvas_pixels.js
Comments
Bình luận