Tài trợ bài viết này và giới thiệu dịch vụ, sản phẩm, thương hiệu, nhu cầu tuyển dụng của doanh nghiệp đến với cộng đồng.
La Kiến Vinh Óc quan sát và phân tích vấn đề sẽ giúp cho tư duy chúng ta tốt hơn. Bài viết này chủ yếu phân tích và hiện thực một hiệu ứng làm blur ảnh đơn giản, từ đó cung cấp ý tưởng cho các bạn xử lý ảnh với các mục tiêu khác nhau và các hiệu ứng khác nhau.
Nội dung bài viết

Giới thiệu

Nhằm giúp cho các bạn đang muốn hiểu sâu hơn và nắm được nguyên lý của việc xử lý ảnh, thay vì dùng các công cụ mạnh có sẵn, tôi chủ yếu phân tích 1 hiệu ứng và hướng dẫn các bạn hiểu được nguyên lý cơ bản nhất của xử lý ảnh.

Làm mở ảnh còn có thể có những tính ứng dụng như lọc nhiễu trong xử lý ảnh, xem thêm:

Đối tượng hướng đến

Dành cho bạn đọc tham khảo hoặc bình luận nếu chưa hiểu rõ, yêu cầu phải nắm được 1 ít kiến thức về pixel RGB888, RGBA8888, con trỏ và cấp phát động với C++.

Blur hay ảnh mờ là gì?

Quan sát 2 hình bên dưới và nhận xét thấy rằng, hình thứ 2 nhòe (mờ) hơn hình 1.

ss_1
HÌNH 1 - Rõ nét

ss_2
HÌNH 2 - Mờ hơn HÌNH 1

Nguyên lý của việc làm mờ ảnh

Tại một tọa độ pixel xác định trên ảnh kết quả, nó chính là việc giao thoa màu với các điểm ảnh lân cận.

Xét ảnh gốc với kích thước với width = 3px và height = 3px

ss_3

Điều mong đợi là ta sẽ làm mờ ảnh trên. Công việc của ta là phải tính toán lại màu cho 9 pixel (0:0, 0:1, 0:2, 1:0, 1:1, 1:2, 2:0, 2:1, 2:2) trên và cách đơn giản ta có thể nghĩ ra đó là dùng phương pháp trung bình cộng của các pixel màu gốc với các pixel xung quanh nó.

Tại vị trí 0:0 thì kết quả màu sẽ là trung bình cộng màu của các vị trí 0:0, 0:1, 1:0, 1:1 chia cho 4 (1 điểm gốc và 3 điểm lân cận).

ss_4

Các điểm còn lại sẽ có cách tính tương tự, giả sử điểm 1:1 (màu xanh lá như trong hình) sẽ có cách tính là trung bình cộng màu của pixel tại vị trí 1:1, 0:0, 0:1, 0:2, 1:0, 1:2, 2:0, 2:1, 2:2 và chia cho 9 (1 điểm gốc và 8 điểm xung quanh).

ss_5

Và cứ thế, dùng phương pháp này cho 7 pixel còn lại ta được kết quả như sau.

ss_6

So sánh lại hình đầu và kết quả cuối sau khi áp dụng giải thuật trên.

ss_7

Hiện thực giải thuật trên

Ý tưởng là ta sẽ vét cạn từng pixel trên ảnh, và lấy màu của từng điểm rồi tính trung bình cộng của số điểm xung quanh có thể tính được.

"Có thể tính được" ở đây giả sử ta đang tính pixel ở vị trí 0:0, vậy chỉ có thể tính được ở vị trí xoay quanh nó là 0:1, 1:1, 1:0 (ta phải loại trừ các trường hợp như -1:0, -1:1, 0:-1, 1:-1 và các trường hợp nằm ở biên lẫn các góc khác.

void blurMore(unsigned char* & img, int width, int height, int bpp)
{
	unsigned char* imgTemp = img;
	img = new unsigned char[width * height * bpp / 8];

	int step = bpp / 8;
	
	unsigned short colorTemp[3] = {0};

	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			int count = 1;

			colorTemp[0] = imgTemp[(i * width + j)*step + 0];
			colorTemp[1] = imgTemp[(i * width + j)*step + 1];
			colorTemp[2] = imgTemp[(i * width + j)*step + 2];

			// x o o
			// o + o
			// o o o
			if (i - 1 >= 0 && j - 1 >= 0)
			{
				count++;
				colorTemp[0] += imgTemp[((i - 1) * width + j - 1)*step + 0];
				colorTemp[1] += imgTemp[((i - 1) * width + j - 1)*step + 1];
				colorTemp[2] += imgTemp[((i - 1) * width + j - 1)*step + 2];
			}

			// o x o
			// o + o
			// o o o
			if (i - 1 >= 0)
			{
				count++;
				colorTemp[0] += imgTemp[((i - 1) * width + j)*step + 0];
				colorTemp[1] += imgTemp[((i - 1) * width + j)*step + 1];
				colorTemp[2] += imgTemp[((i - 1) * width + j)*step + 2];
			}

			// o o x
			// o + o
			// o o o			
			if (i - 1 >= 0 && j + 1 < width)
			{
				count++;
				colorTemp[0] += imgTemp[((i - 1) * width + j + 1)*step + 0];
				colorTemp[1] += imgTemp[((i - 1) * width + j + 1)*step + 1];
				colorTemp[2] += imgTemp[((i - 1) * width + j + 1)*step + 2];
			}

			// o o o
			// o + x
			// o o o
			if (j + 1 < width)
			{
				count++;
				colorTemp[0] += imgTemp[(i * width + j + 1)*step + 0];
				colorTemp[1] += imgTemp[(i * width + j + 1)*step + 1];
				colorTemp[2] += imgTemp[(i * width + j + 1)*step + 2];
			}

			// o o o
			// o + o
			// o o x
			if (i + 1 < height && j + 1 < width)
			{
				count++;
				colorTemp[0] += imgTemp[((i + 1) * width + j + 1)*step + 0];
				colorTemp[1] += imgTemp[((i + 1) * width + j + 1)*step + 1];
				colorTemp[2] += imgTemp[((i + 1) * width + j + 1)*step + 2];
			}

			// o o o
			// o + o
			// o x o
			if (i + 1 < height)
			{
				count++;
				colorTemp[0] += imgTemp[((i + 1) * width + j)*step + 0];
				colorTemp[1] += imgTemp[((i + 1) * width + j)*step + 1];
				colorTemp[2] += imgTemp[((i + 1) * width + j)*step + 2];
			}

			// o o o
			// o + o
			// x o o
			if (i + 1 < height && j - 1 >= 0)
			{
				count++;
				colorTemp[0] += imgTemp[((i + 1) * width + j - 1)*step + 0];
				colorTemp[1] += imgTemp[((i + 1) * width + j - 1)*step + 1];
				colorTemp[2] += imgTemp[((i + 1) * width + j - 1)*step + 2];
			}

			// o o o
			// x + o
			// o o o
			if (j - 1 >= 0)
			{
				count++;
				colorTemp[0] += imgTemp[(i * width + j - 1)*step + 0];
				colorTemp[1] += imgTemp[(i * width + j - 1)*step + 1];
				colorTemp[2] += imgTemp[(i * width + j - 1)*step + 2];
			}

			img[(i * width + j)*step + 0] = colorTemp[0] / count;
			img[(i * width + j)*step + 1] = colorTemp[1] / count;
			img[(i * width + j)*step + 2] = colorTemp[2] / count;
			(step == 4) && (img[(i * width + j)*step + 3] = imgTemp[(i * width + j)*step + 3]);
		}
	}

	delete []imgTemp;
}

Nâng cao

Hiện tại giải thuật này rất đơn giản, chỉ tính các điểm lân cận, mở rộng ra ta có thể thêm 1 thông số là weight (trọng số) để tính với 1 nhóm điểm với giới hạn lớn hơn, sẽ tạo ra ảnh có độ nhòe cao hơn nữa.

THẢO LUẬN
ĐÓNG