Giải thuật xử lý ảnh mosaic

Nốt hôm nay là được nghỉ hẳn một tuần (honho)

Trong lúc đang đợi spec của KH mình xin giới thiệu về giải thuật sinh ảnh mosaic từ một ảnh gốc. Không hiểu sinh ảnh mosiac nhằm mục đích gì, chắc chỉ để sinh chơi. (haha)

  • Mosaic có nguồn gốc từ thời Hy Lạp cổ đại với ý nghĩa nguyên thủy là “loại nghệ thuật xứng đáng với trí tưởng tượng bay bổng và lòng kiên trì vô biên”, thuộc loại nghệ thuật có tuổi đời lâu nhất của loài người. (hucau)

Note: Hiệu ứng này có thể được thực hiện bằng Photoshop một cách dễ dàng hơn là ngồi code (yaoming). Ảnh sinh trong bài viết này gọi là ảnh chấm chấm theo phong cách mosaic thì chuẩn hơn (facepalm2)

1. Ý tưởng

Chúng ta sẽ tạo ra một layer gồm các hình tròn trắng trên background màu đen, sau đó thực hiện phép multiplying với layer mosaic tính toán được (really?)

Steps

  1. Load ảnh gốc đầu vào.
  2. Áp dụng hiệu ứng pixelate (lấy màu trung bình) của NxN block lân cận cho toàn ảnh.
  3. Tạo ảnh mặt nạ là các hình tròn trắng (bit 1) và background đen (bit 0).
  4. Thực hiện phép toán Multiply ảnh màu trung bình với ảnh mặt nạ.

Các bước tạo ảnh có thể hình dung đơn giản như sau:

Selection_017.png

_Chi tiết hơn ta thử áp dụng vào ảnh 150x150px. Trong trường hợp NxN block, N = 75; _

Selection_016.png

2. Thực hành

Mình sử dụng C++ và thư viện OpenCV để thực hiện ý tưởng trên.

Mat function colorDot(Mat image) {
	Mat buffer;

	// Copy từ ảnh gốc sang ảnh đệm
	image.copyTo(buffer);

	Mat dst = Mat::zeros(image.size(), CV_8UC3);
	Mat cir = Mat::zeros(image.size(), CV_8UC1);

	// Áp dụng phương thức pixelate cho NxN block lân cận có size = 10x10
	int N = 10;

	for (int i = 0; i < image.rows; i += N)
	{
		for (int j = 0; j < image.cols; j += N)
		{
			Rect rect = Rect(j, i, N, N) &
					Rect(0, 0, image.cols, image.rows);

            // Create block có màu trung bình
			Mat sub_dst(dst, rect);
			sub_dst.setTo(mean(image(rect)));

            // Tạo các hình tròn màu trắng (bit 1) trên layer đen (bit 0), bán kính N/2
			circle(
				cir,
				Point(j + N/2, i + N/2),
				N/2-1,
				CV_RGB(255,255,255),
				-1,
				CV_AA
			);
		}
	}

    // Convert ảnh circles sang floating-point
	Mat cir_32f;
	cir.convertTo(cir_32f, CV_32F);
	normalize(cir_32f, cir_32f, 0, 1, NORM_MINMAX);

    // Convert ảnh pixelate sang floating-point
	Mat dst_32f;
	dst.convertTo(dst_32f, CV_32F);

    // Tiến hành phép toán Multiply trên từng channel màu
	vector<Mat> channels;
	split(dst_32f, channels);
	for (int i = 0; i < channels.size(); ++i) {
		channels[i] = channels[i].mul(cir_32f);
	}

	// Merge 3 channels màu lại thành ảnh mosaic
	merge(channels, dst_32f);
	dst_32f.convertTo(dst, CV_8U);

	return dst;
}

3. Kết quả

Bên dưới đây là các ảnh đầu vào và đầu ra tương ứng (yeah)

test_01.jpg result_01.jpg

Xử lý realtime với video thu được webcam:

video_01.jpg