12/08/2018, 09:25

Open CV xử lý ảnh bài 1 xử lý màu, chuyển hệ màu

Không gian màu là một mô hình toán học dùng để mô tả các màu sắc trong thực tế được biểu diễn dưới dạng số học. Trên thực tế có rất nhiều không gian màu khác nhau được mô hình để sử dụng vào những mục đích khác nhau. Trong bài này ta sẽ tìm hiểu qua về ba không gian màu cơ bản hay được nhắc tới và ...

Không gian màu là một mô hình toán học dùng để mô tả các màu sắc trong thực tế được biểu diễn dưới dạng số học. Trên thực tế có rất nhiều không gian màu khác nhau được mô hình để sử dụng vào những mục đích khác nhau. Trong bài này ta sẽ tìm hiểu qua về ba không gian màu cơ bản hay được nhắc tới và ứng dụng nhiều, đó là hệ không gian màu RGB, HSV và CMYK.

Không gian màu RGB

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. Ý tưởng chính của không gian màu này là sự kết hợp của 3 màu sắc cơ bản : màu đỏ (R, Red), xanh lục (G, Green) và xanh lơ (B, Blue) để mô tả tất cả các màu sắc khác.
Nếu như một ảnh số được mã hóa bằng 24bit, nghĩa là 8bit cho kênh R, 8bit cho kênh G, 8bit cho kênh B, thì mỗ kênh này màu này sẽ nhận giá trị từ 0-255. Với mỗi giá trị khác nhau của các kênh màu kết hợp với nhau ta sẽ được một màu khác nhau, như vậy ta sẽ có tổng cộng 255x255x255 = 1.66 triệu màu sắc.

Ví dụ: màu đen là sự kết hợp của các kênh màu (R, G, B) với giá trị tương ứng (0, 0, 0) màu trắng có giá trị (255, 255, 255), màu vàng có giá trị (255, 255, 0), màu tím đậm có giá trị (64, 0, 128) ...Nếu ta dùng 16bit để mã hóa một kênh màu (48bit cho toàn bộ 3 kênh màu) thì dãi màu sẽ trãi rộng lên tới 3*2^16 = ... Một con số rất lớn.

Không gian màu CMYK.

CMYK là không gian màu được sử dụng phổ biến trong ngành công nghiệp in ấn.Ý tưởng cơ bản của hệ không gian này là dùng 4 màu sắc cơ bản để phục vụ cho việc pha trộn mực in. Trên thực tế, người ta dùng 3 màu là C=Cyan: xanh lơ, M=Magenta: hồng xẫm, và Y=Yellow: vàng để biểu diễn các màu sắc khác nhau. Nếu lấy màu hồng xẫm cộng với vàng sẽ ra màu đỏ, màu xẫm kết hợp với xanh lơ sẽ cho xanh lam ... Sự kết hợp của 3 màu trên sẽ cho ra màu đen, tuy nhiên màu đen ở đây khôn phải là đen tuyệt đối và thường có độ tương phản lớn, nên trong ngành in, để tiết kiệm mực in người ta thêm vào màu đen để in những chi tiết có màu đen thay vì phải kết hợp 3 màu sắc trên. Và như vậy ta có hệ màu CMYK. chữ K ở đây là để kí hiệu màu đen (Black), có nhẽ chữ B đã được dùng để biểu diễn màu Blue nên người ta lấy chữ cái cuối K để biểu diễn màu đen?

Nguyên lý làm việc của hệ màu này như sau : Trên một nền giấy trắng, khi mỗi màu này được in lên sẽ loại bỏ dần đi thành phần màu trắng. 3 màu C, M, Y khác nhau in theo những tỉ lệ khác nhau sẽ loại bỏ đi thành phần đó một cách khác nhau và cuối cùng cho ta màu sắc cần in. Khi cần in màu đen, thay vì phải in cả 3 màu người ta dùng màu đen để in lên. Nguyên lý này khác với nguyên lý làm việc của hệ RGB ở chỗ hệ RGB là sự kết hợp của các thành phần màu, còn hệ CMYK là sự loại bỏ lẫn nhau của các thành phần màu.

Không gian màu HSV.

HSV và cũng gần tương tự như HSL là không gian màu được dùng nhiều trong việc ch ỉnh sữa ảnh, phân tích ảnh và một phần của lĩnh vực thị giác máy tính. Hệ không gian này dựa vào 3 thông số sau để mô tả màu sắc H = Hue: màu sắc, S = Saturation: độ đậm đặc, sự bảo hòa, V = value: giá trị cường độ sáng.

Không gian màu này thường được biểu diễn dưới dạng hình trụ hoặc hình nón . Theo đó, Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV Ví dụ: màu đen là sự kết hợp của các kênh màu (R, G, B)với giá trị tương ứng (0, 0, 0) màu trắng có giá trị (255,255, 255), màu vàng có giá trị (255, 255, 0), màu tím đậm có giá trị (64, 0, 128) ...Nếu ta dùng 16bit để mã hóa một kênh màu (48bit cho toàn bộ 3 kênh màu) thì dãi màu sẽ trãi rộng lên tới 3*2^16 = ... Một con số rất lớn.

*Chuyển đổi giữa các không gian màu.

Chuyển đổi RGB sang CMYK và ngược lại.

Như đã nói ở trên, thành phần K là thành phần phụ dùng để in cho những điểm màu có màu đen trong hệ CYMK, do vậy để chuyển không gian màu từ RGB sang CMYK trước hết ta chuyển RGB sang CMY sau đó tìm thành phần K còn lại.

Công thức chuyển từ RGB sang CMY:

(C', M', Y') = ((255 - R), (255 - G), (255 - B)). Việc tính giá trị của K lại là một vấn đề khác vì nó liên quan tới nhà sản xuất công nghệ in, tuy nhiên về mặt lý thuyết có thể chấp nhận rằng K = min {C'/2,55, M'/2,55, Y'/2,55} , như vậy 0<= K <=100. Nếu K = 100, thì C = M = Y =0 (trương hợp in màu đen) Nếu 0< K < 100: C = (C'/2.55 - K) * 100 /(100 - K), M = (M'/2.55 - K) * 100 /(100 - K), Y = (Y'/2.55 - K) *100 /(100 - K) và K = K. Trong đó, C, M, Y, K được làm tròn tới để lấy chỉ số nguyên.

Chuyển đổi RGB sang HSV và ngược lại

Giả sử ta có một điểm màu có giá trị trong hệ RGB là (R, G, B). ta chuyển sang không gian HSV như sau: Đặt M = Max(R, G, B), m = Min(R, G, B) và C = M - m. Nếu M = R, H' = (G - B)/C mod 6. Nếu M = G, H' = (B - R)/C + 2. Nếu M = B, H' = (R - G)/C + 4. Và H = H'x60. Trong trường hợp C = 0, H = 00 V = M. S = C/V. Trong trường hợp V hoặc C bằng 0, S = 0. Để chuyển từ HSV sang RGB ta làm như sau: Giả sử ta có không gian màu HSV với H = [0, 360], S = [0, 1], V = [0, 1]. Khi đó, ta tính C = VxS. H' = H/60. X = C(1 - |H' mod2 -1|). Ta biểu diễn hệ (R1, G1, B1) như sau:

( 1, 1, 1) ={

(0, 0, 0) nếu H chưa được xác định

( C, X , 0) nếu 0<H'<1

( X, C, 0) nếu 1<H'<2

( 0, C ,X) nếu 2<H'<3

( 0, X , C) nếu 3<H'<4

( X, 0 , C) nếu 4<H'<5

( C, 0 , X) nếu 5<H'<6

}

Chương trình chuyển đổi các không gian màu

Trong OpenCV, các không gian màu được được chuyển đổi qua lại nhờ hàm cvtColor (convert color), nguyên mẫu hàm này như sau: cv::cvtColor(cv::InputArray src, cv::OutputArray dst, int code) Trong đó, src, dst là ảnh gốc và ảnh thu được sau khi chuyển đổi không gian màu. code mà mã chuyển đổi không gian màu. OpenCV định nghĩa khá nhiều chuyển đổi giữa các  không gian màu chẳng hạn như code = CV_BGR2GRAY sẽ chuyển ảnh ở không gian màu RGB sang ảnh xám, code = CV_HSV2BGR sẽ chuyển ảnh ở không gian màu HSV sang

không gian màu RGB …

 `#`include "stdafx.h"

`#`include <opencv2/core/core.hpp>

`#`include <opencv2/imgproc/imgproc.hpp>

`#`include <opencv2/highgui/highgui.hpp>

using namespace cv;

void main()

{

Mat src =imread("LucBinh.jpg",CV_LOAD_IMAGE_COLOR);

Mat gray, hsv, ycrcb;

cvtColor(src, gray, CV_BGR2GRAY);

cvtColor(src, hsv, CV_BGR2HSV);

cvtColor(src, ycrcb, CV_BGR2YCrCb);

imshow("src", src);

imshow("gray", gray);

imshow("hsv", hsv);

imshow("ycrcb", ycrcb);

waitKey(0);

Sử dụng Open CV trong android:

Trong ví dụ đầu tiên này, chúng tôi sẽ dựa vào các khung xem trước từ các máy ảnh từ khi chúng tôi có thể lấy lại những hình ảnh tương đối nhanh. Chúng tôi cần phải chọn một kích thước thích hợp cho các khung xem trước, như khung quá nhỏ sẽ dẫn đến một kết quả xấu khi chúng ta làm việc xử lý, và khung hình quá lớn sẽ làm chậm tất cả mọi thứ đến một mức độ không thể chấp nhận. Kể từ khi máy ảnh điện thoại thông minh thường có một bộ khác nhau của kích thước được hỗ trợ cho các khung xem trước, chúng ta cần phải chọn một kích thước tối thiểu chấp nhận và đi qua tất cả để tìm ra điều đúng. OpenCV có API Java của riêng mình, và không dựa trên dựa trên Java Máy ảnh API Android. Các mã sau mở máy ảnh và thiết lập kích thước xem trước mức chấp nhận được.

private void setupCamera() {

if (mCamera != null) {
   VideoCapture camera = mCamera;
   mCamera = null; // Make it null before releasing...
  camera.release();
}

mCamera = new VideoCapture(mCameraId);

List<Size> previewSizes = mCamera.getSupportedPreviewSizes();
double smallestPreviewSize = 1280 * 720; // We should be smaller than this...
double smallestWidth = 480; // Let's not get smaller than this...
for (Size previewSize : previewSizes) {
    if (previewSize.area() < smallestPreviewSize && previewSize.awidth >= smallestWidth) {
        mPreviewSize = previewSize;
    }
}
mCamera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, mPreviewSize.awidth);
mCamera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, mPreviewSize.height);
}

Một điều quan trọng cần nhớ khi làm việc với OpenCV là chúng ta cần rất nhiều RAM cho những hình ảnh chúng tôi đang xử lý. OpenCV sử dụng ma trận để đại diện cho tất cả các hình ảnh như vậy để được như hiệu quả nhất có thể, chúng tôi sẽ phân bổ các ma trận cần một lần và tái sử dụng chúng cho mỗi khung hình. Một khi chúng ta có các thiết lập máy ảnh và các ma trận phân bổ, chúng ta có thể bắt đầu để lấy khung preview để xử lý. Các mã sau đây cho thấy cách chúng ta làm điều này với OpenCV.

while (mDoProcess && mCamera != null) {
    boolean grabbed = mCamera.grab();
    if (grabbed) {
        mCamera.retrieve(mCurrentFrame,
Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGB);
        // Process mCurrentFrame…
    }
}

Bây giờ chúng ta có một khung xem trước trong mCurrentFrame rằng chúng ta có thể sử dụng để chế biến. Trong trường hợp này, chúng ta sẽ sử dụng chức năng inRange để tạo ra một mặt nạ mà chỉ bao gồm một khoảng màu cụ thể. Để có kết quả tốt nhất, đầu tiên chúng ta sẽ chuyển đổi các khung nắm lấy để HSV-format. Khi chúng tôi có mặt nạ, chúng tôi rút ra những khung ban đầu vào một ma trận mới bằng cách sử dụng mặt nạ, chúng tôi nhận được từ các inRange chức năng. Các mã sau đây cho thấy làm thế nào để chuyển đổi định dạng từ RGB sang HSV, tạo mặt nạ màu và vẽ kết quả.

// Convert mCurrentFrame to HSV format and store
the result in mCurrentFrameHsv
Imgproc.cvtColor(mCurrentFrame, mCurrentFrameHsv,
Imgproc.COLOR_RGB2HSV);
// Generate the color mask based on the lower and upper
color limits
Core.inRange(mCurrentFrameHsv, mLowerColorLimit,
mUpperColorLimit, mInRangeResult);
// Clear the final image before we copy the new filtered image
mFilteredFrame.setTo(new Scalar(0, 0, 0));
// Copy the original frame using the color mask
mCurrentFrame.copyTo(mFilteredFrame, mInRangeResult);

Làm thế nào để hiển thị các kết quả của hình ảnh được xử lý kết quả Bitmap bây giờ có thể được sử dụng để vẽ một SurfaceView hoặc tương tự để hiển thị kết quả. Cách dễ nhất để làm điều này là để chuyển đổi ma trận OpenCV đến một đối tượng Bitmap Android. Có một lớp học Util cung cấp một chức năng cho việc này.

// Convert the matrix to an Android bitmap
Utils.matToBitmap(result, resultBitmap, true);

0