Mô hình màu HSI toàn tập và ứng dụng demo trên thiết bị di động android

1.Mô hình màu HSI là gì ?

Như chúng ta đã biết RGB là không gian màu rất phổ biến được dùng trong đồ họa máy tính và nhiều thiết bị kĩ thuật số khác. Tuy nhiên, RGB không phải là mô hình trực quan để mô tả màu sắc. Thay vào đó, con người có xu hướng sử dụng H(Hue), S(Saturation) và I (Intensity).
Mô hình HSI sử dụng 3 thông số để mô tả màu sắc :

  • Hue (mang thông tin màu sắc): H có giá trị 0 đến 360 độ
  • Saturation (độ bão hòa) : S có giá trị từ 0 tới 1
  • I (cường độ) : I có giá trị từ 0 tới 1

alt

2.Công thức chuyển từ RGB sang HSI

alt

3.Công thức chuyển từ HSI sang RGB

alt

4.Ứng dụng demo trên android

Nội dung triển khai : Ứng dụng cho phép chọn 1 điểm bất kì trên trục vô sắc (trục I), kết quả trả về là mảng màu mặt cắt vuông góc với trục tại vị trí lựa chọn

alt

Ý tưởng thực hiện :

Áp dụng kĩ thuật Tô đổ màu theo bán kính hình tròn – Radial Gradient

Chia đường tròn thành 12 phần :

       int colorCount = 12;


Khi đó mỗi phần sẽ ứng với 1 góc 30 độ :

    int colorAngleStep = 360 / 12;

Tạo 1 mảng hsi[] có kiểu float với tham số khởi tạo là :
  colors[colorCount] = colors[0];
  float hsi[] = new float[] { 0f, 1f, 1f };
  hsi[2] = (float) (vValue / 100.0f);

Quá trình đổ màu : Bắt đầu xuất phát từ tâm đường tròn và lan dân ra biên đường tròn. Tại bước thứ i : Color[i] sẽ được tính dựa vào hàm chuyển HSIToColor(hsi) là hàm chuyển từ hệ HSI sang RGB, tham số truyền vào là 1 mảng Float và đầu ra màu RGB.
    for (int i = 0; i < colors.length; i++) {
		hsi[0] = (float)(i * colorAngleStep +180.0f) % 360.0f;
		colors[i] = HSIToColor(hsi);
	}
    colors[colorCount] = colors[0];

Tại bước thứ i : Color[i] sẽ được tính dựa vào hàm chuyển HSIToColor(hsi) là hàm chuyển từ hệ HSI sang RGB, tham số truyền vào là 1 mảng Float và đầu ra màu RGB.
	float h = (float) (hsi[0]*Math.PI/180.0f);
	final float s = hsi[1];
	final float i = hsi[2];

	float x = i * (1.0f - s);
	float y;
	float z;

	int r = 0, g = 0, b = 0;
	if(s==0){
		r = g =b = Math.round(255*i);
	}else{
	if (0<= h && h < 2.0f*Math.PI/3.0f) {
		y = (float) (i * (1.0f + (s * Math.cos(h) / Math.cos(Math.PI/3.0f - h))));
		z = 3.0f * i - (x + y);
		r = Math.round(255 * y);
		g = Math.round(255 * z);
		b = Math.round(255 * x);
		}
	}
    else if (2.0f*Math.PI/3.0f<=h && h < 4.0f*Math.PI/3.0f) {
		h-=2.0f*Math.PI/3.0f;

		y = (float) (i * (1.0f + (s * Math.cos(h ) / Math.cos(Math.PI/3.0f - h))));
		z = 3.0f * i - (x + y);
		r = Math.round(255 * x);
		g = Math.round(255 * y);
		b = Math.round(255 * z);
	}
	else  {
		h-=4.0f*Math.PI/3.0f;

		y = (float) (i * (1.0f + (s * Math.cos(h) / Math.cos(Math.PI/3.0f - h))));
		z = 3.0f * i - (x + y);
		r = Math.round(255 * z);
		g = Math.round(255 * x);
		b = Math.round(255 * y);

	}
	}

	r = constrainn(r, 0, 255);
	g = constrainn(g, 0, 255);
	b = constrainn(b, 0, 255);

	return Color.rgb(r, g, b);
    }

Góc H khi đưa vào sẽ được xử lý như sau : đem H nhân với 2pi rồi chia cho 180 để chuyển H sang radian.
Màu sắc trung tâm dùng để vẽ sẽ được xác định như sau :
hsi[0] = 0f;
hsi[1] = 0f;
hsi[2] = (float) ((float) vValue / 100.0f);
int colorCenter = HSIToColor(hsi);

Sau đó tô màu đường tròn với mảng màu colors.
SweepGradient sweepGradient = new SweepGradient(width / 2, height / 2, colors, null);

Tiếp theo là phủ màu lên trên đường tròn :
RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, colorWheelRadius, colorCenter,
			colorCenter & 0x00FFFFFF, TileMode.CLAMP);

Kết hợp 2 shader với chế độ kết hợp là `PorterDuff.Mode.SRC_OVER `
ComposeShader composeShader = new ComposeShader(sweepGradient, radialGradient, PorterDuff.Mode.SRC_OVER);

	colorWheelPaint.setShader(composeShader);

Và cuối cùng là sử dụng canvas để vẽ đường tròn với tham số truyền vào là bitmap
Canvas canvas = new Canvas(bitmap);
	canvas.drawCircle(width / 2, height / 2, colorWheelRadius, colorWheelPaint);

* **Kết quả demo thu được :**

alt