java - 拉普拉斯金字塔产生奇怪的结果?

标签 java android matlab opencv image-processing

我正在尝试基于此 MATLAB 代码制作拉普拉斯金字塔:

% I is the image array, where its size is r x c x 3(<-RGB channels).
function pyr = laplacian_pyramid(I,nlev)

r = size(I,1);
c = size(I,2);

% recursively build pyramid
pyr = cell(nlev,1);
filter = [.0625, .25, .375, .25, .0625];
J = I;
for l = 1:nlev - 1
    % apply low pass filter, and downsample
    I = downsample(J,filter);
    odd = 2*size(I) - size(J);  % for each dimension, check if the upsampled version has to be odd
    % in each level, store difference between image and upsampled low pass version
    pyr{l} = J - upsample(I,odd,filter);
    J = I; % continue with low pass image
end
pyr{nlev} = J; % the coarest level contains the residual low pass image

其中,downsample() 看起来像这样:

function R = downsample(I, filter)

border_mode = 'symmetric';

% low pass, convolve with separable filter
R = imfilter(I,filter,border_mode);     %horizontal
R = imfilter(R,filter',border_mode);    %vertical

% decimate
r = size(I,1);
c = size(I,2);
R = R(1:2:r, 1:2:c, :);  

这里是upsample():

function R = upsample(I,odd,filter)

% increase resolution
I = padarray(I,[1 1 0],'replicate'); % pad the image with a 1-pixel border
r = 2*size(I,1);
c = 2*size(I,2);
k = size(I,3);
R = zeros(r,c,k);
R(1:2:r, 1:2:c, :) = 4*I; % increase size 2 times; the padding is now 2 pixels wide

% interpolate, convolve with separable filter
R = imfilter(R,filter);     %horizontal
R = imfilter(R,filter');    %vertical

% remove the border
R = R(3:r - 2 - odd(1), 3:c - 2 - odd(2), :);

此 MATLAB 代码运行正常。只关注 downsample() 函数,因为我的 OpenCV 版本的那个函数就是我的问题发生的原因。


现在这是我对这个 MATLAB 代码的 OpenCV 版本的尝试:

private List<Mat> laplacianPyramid(Mat mat,int depth)
    {
        //mat.type() is CV_8UC3 (16).
        List<Mat> pyramid = new ArrayList<Mat>();
        //I make a clone so I don't ruin the original matrix.
        Mat clone = mat.clone();
        Mat J = clone;
        for(int i=0;i<=depth-2;i++)
        {
            clone = image_reduce(J);
            Mat temp = new Mat();
            Point odd = new Point(clone.size().height*2 - J.height(), clone.size().width*2 - J.width());
            Core.subtract(J, image_expand(clone, odd), temp);
            pyramid.add(temp);
            J = clone;
        }
        pyramid.add(J);
        return pyramid;
    }

这是我的 upsample() 的 OpenCV 版本:

private Mat image_expand(Mat image, Point odd){
        //I make a clone so I don't ruin the original image.
        Mat imageClone = image.clone();
        copyMakeBorder(imageClone, imageClone, 1, 1, 1, 1, BORDER_REPLICATE);
        Mat kernelX = getGaussianKernel();
        Mat kernelY = new Mat();
        Core.transpose(kernelX, kernelY);
        Mat UIntVer = new Mat(imageClone.size(), CV_8UC3);
        imageClone.convertTo(UIntVer, CV_8UC3);
        Imgproc.resize(UIntVer, UIntVer, new Size(imageClone.width()*2, imageClone.height()*2), 0, 0, Imgproc.INTER_NEAREST);

        //Now implement the zero padding between each columns and rows, just like the MATLAB version.
        Mat mask = new Mat(2,2, CV_8UC1);
        int[][] array = new int[2][2];
        array[0][0] = 255;
        array[1][0] = 0;
        array[0][1] = 0;
        array[1][1] = 0;
        for (int i=0; i<2; i++) {
            for (int j = 0; j < 2; j++) {
                mask.put(i, j, array[i][j]);
            }
        }
        //mask becomes twice the size of image.
        Mat biggerMask = new Mat();
        Core.repeat(mask, imageClone.height(), imageClone.width(), biggerMask);
        List<Mat> rgbUIntVer = new ArrayList<Mat>();
        Core.split(UIntVer,rgbUIntVer);
        Core.bitwise_and(rgbUIntVer.get(0), biggerMask, rgbUIntVer.get(0));
        Core.bitwise_and(rgbUIntVer.get(1), biggerMask, rgbUIntVer.get(1));
        Core.bitwise_and(rgbUIntVer.get(2), biggerMask, rgbUIntVer.get(2));
        Core.merge(rgbUIntVer, UIntVer);

        int r = imageClone.height()*2;
        int c = imageClone.width()*2;
        Mat result = new Mat(r, c, CV_32FC3);
        UIntVer.convertTo(UIntVer, CV_32FC3);
        Scalar four = new Scalar(4);
        Core.multiply(UIntVer, four, UIntVer);
        Imgproc.sepFilter2D(UIntVer,result,-1,kernelX,kernelY,new Point(-1,-1) ,0,BORDER_DEFAULT);
        Rect roi = new Rect(2, 2, c-4-(int)odd.y, r-4-(int)odd.x);
        result = new Mat(result, roi);
        return result;
    }

这个 OpenCV 版本代码的问题是结果只是纯蓝色。 Laplacian Pyramids 应该保存边缘检测结果,但示例结果是这样的。上图是原始输入,下图是生成的金字塔的底层。

enter image description here

我检查了输入图像是否被正确读取。我的代码有问题,但我找不到哪里。任何帮助将不胜感激!

我怀疑某些处理函数只在 R channel 上工作,而不在 GB channel 上工作。


我发现问题出在 image_expand() 上。它只是简单地输出一个红色版本的图像,这解释了为什么结果图像是蓝色的,因为结果图像是原始图像和 image_expand() 的输出之间的差异。所以问题出在 image_expand() 中。


以防万一,这是我的 getGaussianKernel() 代码:

private Mat getGaussianKernel(){
        float[] kernel = new float[5];
        kernel[0]= 0.0625f;
        kernel[1]=0.25f;
        kernel[2]=0.375f;
        kernel[3]=0.25f;
        kernel[4]=0.0625f;
        Mat mat = new Mat(5,1,CV_32FC1);
        mat.put(0,0,kernel);
        return mat;
    }

最佳答案

我发现了问题。正如@Ander Biguri 先生和@Cris Luengo 先生(对不起,如果我假设你的性别)告诉我,问题出在

UIntVer.convertTo(UIntVer, CV_32FC3);
Scalar four = new Scalar(4);
Core.multiply(UIntVer, four, UIntVer);

image_expand() 中。显然,要乘以标量值,您必须对图像矩阵执行 Core.split(),并对每个 channel 应用乘法,然后执行 Core.merge()它。这是因为 Core.multiply() 一次只处理 1 个 channel 。我希望这可以帮助其他人发现 Core.multiply() 的问题。

关于java - 拉普拉斯金字塔产生奇怪的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53229340/

相关文章:

android - 后台用户移动轨迹图,显示用户点击按钮后的所有轨迹

machine-learning - 如何使用 libsvm matlab 固定参数 cost 和 gamma 来提高精度?

java - 如何在 Netbeans 中与用户维护一个空的 Java EE 企业应用程序项目?

java - 在 HttpSessionListener 中如何获取 HttpServletRequest?

java - 乔达 - 打印日期时间忽略时区

matlab - 如何在 Matlab R2012b 的绘图中获取斯堪的纳维亚字母 ä 和 ö

matlab - 向录制的音频添加回声

java - Excel-VBA - VBA 中有类似 Javas Set 容器的东西吗?

android - 无法通过 Ionic Project 生成 android apk 文件

android - 使用 Edittext 搜索 ArrayList 时忽略字符