最近遇到一個(gè)需求,要把在C中通過opencv渲染的圖像划煮,通過jni傳到j(luò)ava送丰,再由java層創(chuàng)建BufferedImage并展示。
流程如下:
1. 通過java讀取圖片為BufferedImage
// 這里加入了使用exif信息修正圖片位置的代碼
private static BufferedImage readImg(String path) {
File img = new File(path);
if (!img.exists()) {
return null;
}
Metadata metadata = null;
BufferedImage read = null;
try {
read = ImageIO.read(img);
} catch (IOException e) {
e.printStackTrace();
}
try {
if (read == null) {
return null;
}
metadata = ImageMetadataReader.readMetadata(img);
ExifIFD0Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
int width = read.getWidth();
int height = read.getHeight();
if (directory != null) {
int orientation = 1;
try {
orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
AffineTransform affineTransform = new AffineTransform();
switch (orientation) {
case 1:
break;
case 2: // Flip X
affineTransform.scale(-1.0, 1.0);
affineTransform.translate(-width, 0);
break;
case 3: // PI rotation
affineTransform.translate(width, height);
affineTransform.rotate(Math.PI);
break;
case 4: // Flip Y
affineTransform.scale(1.0, -1.0);
affineTransform.translate(0, -height);
break;
case 5: // - PI/2 and Flip X
affineTransform.rotate(-Math.PI / 2);
affineTransform.scale(-1.0, 1.0);
break;
case 6: // -PI/2 and -width
affineTransform.translate(height, 0);
affineTransform.rotate(Math.PI / 2);
break;
case 7: // PI/2 and Flip
affineTransform.scale(-1.0, 1.0);
affineTransform.translate(-height, 0);
affineTransform.translate(0, width);
affineTransform.rotate(3 * Math.PI / 2);
break;
case 8: // PI / 2
affineTransform.translate(0, width);
affineTransform.rotate(3 * Math.PI / 2);
break;
default:
break;
}
AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage destinationImage = new BufferedImage(read.getHeight(), read.getWidth(), read.getType());
destinationImage = affineTransformOp.filter(read, destinationImage);
return destinationImage;
} catch (Exception ex) {
ex.printStackTrace();
}
}
return read;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
2. 將圖片轉(zhuǎn)換為int數(shù)組
java中int為32位弛秋,顏色劃分每八位分割蚪战,這個(gè)算法在C層會再次用到:
// 顏色組合有可能是ARGB, RGBA, ABGR, ARGB
// 如果需要將RGB轉(zhuǎn)換為ARGB或RGBA,那么只需要在對應(yīng)A通道上寫入255即可
channel1 = (char) ((val >> 24) & 0xFF);
channel2 = (char) ((val >> 16) & 0xFF);
channel3 = (char) ((val >> 8) & 0xFF);
channel4 = (char) (val & 0xFF);
3. 將該數(shù)組傳入jni層铐懊,并通過jni方法轉(zhuǎn)換為C層可用的形式邀桑,(這里選用參數(shù)方式傳遞數(shù)據(jù))
jint *const RGB_i = (env->GetIntArrayElements(java_rgb_array, &JNI_FALSE));
4. 創(chuàng)建cv::Mat
jint在jni_md.h中的定義就是int,所以這里可以直接強(qiáng)轉(zhuǎn)使用(是否有更安全的辦法)
CV_8UC4意思是:輸入數(shù)組是8位無符號整型科乎,4通道
cv::Mat src(input_img_height, input_img_width, CV_8UC4, (unsigned int*)RGB_i);
5. 將cv::Mat中的data成員變量(即儲存圖片顏色信息的數(shù)組)壁畸,轉(zhuǎn)換為bufferedImage接受的格式
// 這里轉(zhuǎn)換為RGB格式數(shù)組
void matToBitmapArray(const cv::Mat &image, jint*_data) {
// BGR format
for (int i = 0; i < image.total(); i ++) {
char r = image.data[3 * i + 2];
char g = image.data[3 * i + 1];
char b = image.data[3 * i + 0];
_data[i] = (((jint)r << 16) & 0x00FF0000) +
(((jint)g << 8) & 0x0000FF00) + ((jint)b & 0x000000FF);
}
}
6. 將該數(shù)組拷貝回jvm(依然通過參數(shù)方式傳遞)
env->SetIntArrayRegion(outputBuffer, 0, width*height, _data);
7. 在java中,創(chuàng)建BufferedImage
// 這里注意TYPE_INT_RGB對應(yīng)在jni層創(chuàng)建的RGB格式int數(shù)組
BufferedImage destImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
destImg.setRGB(0, 0, width, height, _data, 0, width);