Số đo tập trung – Phần 3: Trung vị (Median)

Tiếp theo loạt bài về số đo xu hướng tập trung, hôm nay tôi sẽ nói với các bạn về trung vị (median). Nếu bạn nào biết nhiều từ hán việt có lẽ sẽ hiểu ý nghĩa của từ “trung vị” ngay. Nó có nghĩa là VỊ TRÍ Ở GIỮA. Đó chính là bản chất của trung vị. Tuy nhiên nó nằm ở giữa là giữa cái gì? Câu trả lời đó là nằm giữa các phần tử ĐÃ ĐƯỢC SẮP XẾP.

Giả sử chúng ta có 5 phần tử sau khi sắp xếp là:

3, 4, 7, 9, 17

Vậy trung vị ở đây sẽ là 7.

Điều gì nếu số phần tử là chẵn? Ví dụ như sau:

3, 5, 7, 9, 17, 21

Vậy trung vị lúc này sẽ là 2 phần tử: 7 và 9, và giá trị trung vị lúc này sẽ là:

Median = (7+9)/2 = 8

Không có gì phức tạp đúng không? Vậy chúng ta quay trở lại ví dụ cuối cùng ở Phần 1 mà tôi đã nêu lên nhược điểm của trung bình (mean). Đó là giả sử trong 1 bữa tiệc, có 15 người tham dự trong đó có 8 người 15 tuổi và 7 người 1 tuổi. Yêu cầu đặt ra là chúng ta cần chọn 1 độ tuổi để tham gia hoạt động cho phù hợp lứa tuổi. Nếu dùng trung bình để giải quyết thì tuổi trung bình (T) sẽ là:

T = (8 x 15 + 7 x 1)/15 = 8.47

Rõ ràng, con số này không hữu ích và nó chưa phản ánh đúng với tuổi thực tế để có thể lựa chọn chính xác tuổi cần, vì chỉ có 2 loại tuổi là 15 và 1 tuổi. Nếu chúng ta sử dụng trung vị thì sẽ ra sao? Số tuổi cần lấy sẽ là:

1, 1, 1, 1, 1, 1, 1, 15, 15, 15, 15, 15, 15, 15, 15

Vậy là các đối tượng trở lên và trở xuống danh sách có thể có các hoạt động phù hợp với lứa tuổi.

Đó là 1 ví dụ trong vô vàn các bài toán cần sử dụng. Và chúng ta thử áp dụng nó với xử lý ảnh. Vẫn với hình ảnh bông hoa ở phần 1:

flower

Hình ảnh bông hoa (ảnh đầu vào)

1 ô vuông 5×5 sẽ được sử dụng để duyệt qua toàn bộ các pixel của ảnh.

local_window_5x5

Tại mỗi pixel p, giá trị của 24 điểm ảnh lân cận và chính nó sẽ được đẩy vào 1 vector sau đó được sắp xếp và lấy ra kết quả trung vị. Giá trí này sẽ được thay thế cho pixel hiện tại. Giải thuật sẽ được cài đặt như sau:

#include <vector>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;

int main()
{
	Mat src = imread("C:/flower.jpg");
	int width = src.cols;
	int height = src.rows;
	int ratio = 12;
	Mat dst = Mat(height, width, CV_8UC3, Scalar(0));

	for(int y=0; y<height; y++)
		for(int x=0; x<width; x++)
		{
			vector<int> vecBlue, vecGreen, vecRed;
			int count = (ratio*2 + 1)*(ratio*2 + 1);

			for(int j=y-ratio; j<=y+ratio; j++)
				for(int i=x-ratio; i<=x+ratio; i++)
				{
					if (i<0 || j<0 || i>width-1 || j>height-1)
					{
						/* trường hợp là ngoài boundary của ảnh tức tọa độ âm thì trừ biến count và bỏ qua */
						count--;
						continue;
					}
					Vec3b pixel = src.at<Vec3b>(j,i);
					vecBlue.push_back(pixel[0]);
					vecGreen.push_back(pixel[1]);
					vecRed.push_back(pixel[2]);
				}
			/* thực hiện sắp xếp */
			sort(vecBlue.begin(), vecBlue.end(), less<int>());
			sort(vecGreen.begin(), vecGreen.end(), less<int>());
			sort(vecRed.begin(), vecRed.end(), less<int>());

			dst.at<Vec3b>(y,x)[0] = vecBlue[floor(count/2)];
			dst.at<Vec3b>(y,x)[1] = vecGreen[floor(count/2)];
			dst.at<Vec3b>(y,x)[2] = vecRed[floor(count/2)];

			vecBlue.clear(); vecGreen.clear(); vecRed.clear();
		}

	imshow("src", src);
	imshow("dst", dst);

	waitKey(0);
	return 0;
}

 Và đây là kết quả:

 flower_median

Nếu so sánh với kết quả của trung bình (mean) thì khá khác biệt phải không các bạn? Qua bài toán nhỏ này mong các bạn sẽ nắm được median và áp dụng nó nhiều hơn vào thực tế 🙂

Các bạn có thể download mã nguồn tại đây

Leave a Reply

Your email address will not be published. Required fields are marked *