Xử lý ảnh bitmap trong android
Bài đăng này đã không được cập nhật trong 9 năm
I. BITMAP TRONG ANDROID
Lớp Bitmap
là lớp thể hiện một bức ảnh trong hệ điều hành Android, chứa các thông tin và các phương thức cơ bản để có thể làm việc được với bức ảnh như đọc, ghi các điểm ảnh, lấy thông tin kích thước, ….
Đọc ảnh vào đối tượng Bitmap, cách đơn giản nhất là sử dụng lớp BitmapFactory decode file từ một đường dẫn đến ảnh trên thiết bị.
public Bitmap readBitmapFile(String path){
return BitmapFactory.decodeFile(path);
}
Một đối tượng Bitmap chiếm dụng khá nhiều bộ nhớ, đặc biệt với các bức ảnh nhiều màu như ảnh chụp từ camera. Ví dụ với camera 8 megapixels cho bức ảnh có kích thước 3264 x 2448 pixel
. Nếu Bitmap sử dụng cấu hình ARGB_8888
sau khi được đọc vào bộ nhớ có thể chiếm đến 30MB
(3264*2448*4 bytes)
bộ nhớ trong.
Điều đó sẽ gây nên exception java.lang.OutofMemoryError: bitmap size exceeds VM budget
.
Một điều nữa là việc xử lý bitmap có kích thước lớn sẽ cần nhiều thời gian tính toán hơn bitmap
có kích thước nhỏ. Chính vì vậy ta cần một số thủ thuật để có thể làm việc với bitmap hiệu quả.
public Bitmap readBitmapAndScale(String path){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //Chỉ đọc thông tin ảnh, không đọc dữ liwwuj
BitmapFactory.decodeFile(path,options); //Đọc thông tin ảnh
options.inSampleSize = 4; //Scale bitmap xuống 4 lần
options.inJustDecodeBounds=false; //Cho phép đọc dữ liệu ảnh ảnh
return BitmapFactory.decodeFile(path,options);
}
II. MÀU SẮC TRONG ANDROID
Trong hệ điều hành Android, lớp Color
là lớp thể hiện một màu sắc, lớp này có các thuộc tính và phương thức để làm việc với màu sắc.
Tuy vậy, màu sắc cũng được thể hiện là một số nguyên 4 bytes
, vậy có tổng cộng 2^32 ≈ 16.7 triệu màu
. Mỗi màu 4 bytes tương ứng với 4 kênh Alpha – Red – Green – Blue
, mỗi kênh sẽ chiếm 1 byte
.
Để đọc giá trị của một kênh có thể dùng các toán tử thao tác với bit để cho tốc độ thực thi nhanh.
int A, R, G, B;
int color = Color.parseColor("#4EFD54"); //Màu Light Green
A = (color >> 24) & 0xFF; //đọc byte thứ 4
R = (color >> 16) & 0xFF; //đọc byte thứ 3
G = (color >> 8) & 0xFF; //đọc byte thứ 2
B = color & 0xFF; //đọc byte thứ 1
III. THAO TÁC VỚI ĐIỂM ẢNH TRÊN BITMAP
Mỗi một điểm ảnh trên Bitmap
là một số nguyên đại diện cho một màu mang giá trị của 4 kênh màu cơ bản Alpha, Red, Green, Blue
. Chúng ta sẽ đọc các điểm ảnh vào một mảng kiểu nguyên có kích thước bằng với số pixels của ảnh. Các thuật toán xử lý ảnh sẽ xử lý trên mảng số nguyên đó sau đó lưu mảng kết quả trở lại Bitmap
.
Lớp Bitmap
cung cấp hai phương thức:
Bitmap.getPixels(int[] pixels,
int offset,
int stride,
int x,
int y,
int width,
int height);
Phương thức trên dùng để đọc toàn bộ điểm ảnh của một ảnh vào mảng số nguyên.
Bitmap.setPixels(int[] pixels,
int offset,
int stride,
int x,
int y,
int width,
int height);
Phương thức trên dùng để đặt lại giá trị màu các điểm ảnh từ một mảng số nguyên là mảng giá trị sau khi xử lý.
Như vậy ta có thể viết một hàm xủ lý ảnh như sau với đầu vào là một Bitmap
, đầu ra là một Bitmap
khác đã qua xủ lý:
public Bitmap perform(Bitmap inp) {
Bitmap bmOut = Bitmap.createBitmap(inp.getWidth(), inp.getHeight(),
inp.getConfig());
int A, R, G, B;
int w = inp.getWidth();
int h = inp.getHeight();
int[] colors = new int[w * h];
inp.getPixels(colors, 0, w, 0, 0, w, h);
int i = 0;
int j = 0;
int pos;
int val;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
pos = i * w + j;
A = (colors[pos] >> 24) & 0xFF;
R = (colors[pos] >> 16) & 0xFF;
G = (colors[pos] >> 8) & 0xFF;
B = colors[pos] & 0xFF;
//Thuật toán xử lý cho pixel tại vị trí (i,j)
colors[pos] = Color.argb(A, R, G, B);
}
}
bmOut.setPixels(colors, 0, w, 0, 0, w, h);
return bmOut;
}
IV. THỬ NGHIỆM
Chúng ta hãy thử viết một ứng dụng nho nhỏ với các thuật toán xử lý ảnh cơ bản với những gì chúng ta đã biết ở trên nhé. Ứng dụng có thể xư lý chuyển đổi ảnh màu về ảnh "đen trắng" (ảnh đa mức xám - grayscale) hoặc lọc màu đỏ của ảnh đi.
Bạn có thể tham khảo thêm source code tại Github
Lưu ý làm việc với Bitmap
có thể tốn nhiều thời gian xử lý vì vậy cần phải xử lý bất đồng bộ với main thread bằng AsyncTask
hoặc Thread
khác tránh việc bị block UI thread
.
(hihi)
All rights reserved