BufferedImage与Opencv cv::Mat互相转换

最近遇到一个需求,要把在C中通过opencv渲染的图像,通过jni传到java,再由java层创建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. 将图片转换为int数组

java中int为32位,颜色划分每八位分割,这个算法在C层会再次用到:

// 颜色组合有可能是ARGB, RGBA, ABGR, ARGB
// 如果需要将RGB转换为ARGB或RGBA,那么只需要在对应A通道上写入255即可
channel1 = (char) ((val >> 24) & 0xFF);
channel2 = (char) ((val >> 16) & 0xFF);
channel3 = (char) ((val >> 8) & 0xFF);
channel4 = (char) (val & 0xFF);

3. 将该数组传入jni层,并通过jni方法转换为C层可用的形式,(这里选用参数方式传递数据)

jint *const RGB_i = (env->GetIntArrayElements(java_rgb_array, &JNI_FALSE));

4. 创建cv::Mat

jint在jni_md.h中的定义就是int,所以这里可以直接强转使用(是否有更安全的办法)
CV_8UC4意思是:输入数组是8位无符号整型,4通道

cv::Mat src(input_img_height, input_img_width, CV_8UC4, (unsigned int*)RGB_i);

5. 将cv::Mat中的data成员变量(即储存图片颜色信息的数组),转换为bufferedImage接受的格式

// 这里转换为RGB格式数组
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. 将该数组拷贝回jvm(依然通过参数方式传递)

env->SetIntArrayRegion(outputBuffer, 0, width*height, _data);

7. 在java中,创建BufferedImage

// 这里注意TYPE_INT_RGB对应在jni层创建的RGB格式int数组
BufferedImage destImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
destImg.setRGB(0, 0, width, height, _data, 0, width);

你可能感兴趣的:(BufferedImage与Opencv cv::Mat互相转换)