在Android上玩转Opencv 系列: 4.基础知识 图像拷贝

149 阅读2分钟

在 Android 上使用 OpenCV 将一个图像拷贝到另一个图像的中间,可以通过以下步骤实现:

  1. 加载两个图像:首先需要加载源图像和目标图像。

  2. 计算位置:计算源图像应放置在目标图像中心的位置。

  3. 裁剪和拷贝:将源图像复制到目标图像的指定区域中。

实现步骤:

1. 加载图像

首先加载两个图像:一个是目标图像,另一个是源图像。

Mat src = Imgcodecs.imread("path/to/source_image.jpg");
Mat dst = Imgcodecs.imread("path/to/destination_image.jpg");

2. 计算位置

接下来,我们需要计算源图像应放置在目标图像中心的起始位置。假设目标图像较大,源图像较小,我们可以计算出源图像放置的区域。

例如,如果目标图像的大小是 (dst.cols(), dst.rows()),而源图像的大小是 (src.cols(), src.rows()),那么源图像的左上角应该放置在目标图像的中心。

int x = (dst.cols() - src.cols()) / 2;
int y = (dst.rows() - src.rows()) / 2;

这会计算出源图像的左上角应该放置在目标图像中的 (x, y) 坐标。

3. 拷贝图像

通过 Mat 类的 rect 和 copyTo 方法,将源图像的内容复制到目标图像的指定区域。

// 创建目标图像区域
Rect roi = new Rect(x, y, src.cols(), src.rows());

// 获取目标图像的ROI区域
Mat targetROI = dst.submat(roi);

// 将源图像复制到目标图像的ROI区域
src.copyTo(targetROI);

4. 保存或显示结果

拷贝操作完成后,可以选择保存或显示结果。

Imgcodecs.imwrite("path/to/output_image.jpg", dst);

或者在 Android 中使用 ImageView 显示结果,可以将 Mat 转换为 Bitmap 然后显示:

Bitmap bitmap = Bitmap.createBitmap(dst.cols(), dst.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(dst, bitmap);
imageView.setImageBitmap(bitmap);

完整代码示例:



/**
 * Bitmap 转Mat
 * @param bitmap
 * @return
 */
public Mat bitmapToMatWithAlpha(Bitmap bitmap) {
    Mat mat = new Mat();
    Utils.bitmapToMat(bitmap, mat);
    // 确保是 4 通道(BGRA),避免透明通道丢失
    if (mat.channels() == 3) {
        Imgproc.cvtColor(mat, mat, Imgproc.COLOR_BGR2BGRA);
    }
    return mat;
}


/**
 * 将srcBitmap拷贝到dstBitmap 左上角
 * @param srcBitmap
 * @param dstBitmap
 * @return
 */
public Bitmap overlayImage(Bitmap srcBitmap, Bitmap dstBitmap) {
    if (srcBitmap == null || dstBitmap == null) {
        Log.e(App.tag, "Source or destination bitmap is null");
        return null;
    }
    // 将 Bitmap 转换为 OpenCV Mat
    Mat src =bitmapToMatWithAlpha(srcBitmap);
    Mat dst = bitmapToMatWithAlpha(dstBitmap);

    // 确保 `src` 和 `dst` 存在
    if (src.empty() || dst.empty()) {
        Log.e(App.tag, "One of the images could not be converted to Mat");
        return null;
    }

    // 计算 ROI,确保 `src` 没有超出 `dst`
    int roiWidth = Math.min(src.cols(), dst.cols());
    int roiHeight = Math.min(src.rows(), dst.rows());

    // 确保 `roiWidth` 和 `roiHeight` 不为 0
    if (roiWidth <= 0 || roiHeight <= 0) {
        Log.e(App.tag, "ROI dimensions are invalid");
        return null;
    }

    // 取 `dst` 的左上角 ROI
    Rect roi = new Rect(0, 0, roiWidth, roiHeight);
    Mat targetROI = dst.submat(roi);
    targetROI.setTo(new Scalar(0, 255, 0, 255)); // 绿色填充,看看是否生效

    src.copyTo(targetROI);
    // 转换回 Bitmap
    Bitmap bitmap = Bitmap.createBitmap(dst.width(), dst.height(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(dst, bitmap);
    return  bitmap;
}


imgcopy.png

注意事项:

图像尺寸:确保源图像不会超出目标图像的范围。如果源图像较大,可能需要先调整源图像的大小。

性能:大图像处理时可能会耗费较多的内存和计算资源,特别是在 Android 设备上,可以考虑优化图像尺寸。