【问题标题】:Vector Convolution - Calculating the Index of a Neighbour Element矢量卷积 - 计算相邻元素的索引
【发布时间】:2015-07-29 12:57:50
【问题描述】:

我正在尝试实现采用两个向量的卷积方法:图像;和一个内核。我的问题是,当我将内核“滑动”到图像矢量上时,我不知道如何计算图像相邻元素的索引。例如,使用两个相同的向量 {0, 1, 2, 3, 4, 5, 6, 7, 8} 我想实现以下结果:

到目前为止我的代码如下:

public int[] convolve(int[] image, int[] kernel)
{       
  int imageValue; 
  int kernelValue;
  int outputValue;
  int[] outputImage = new int[image.length()];

  // loop through image
  for(int i = 0; i < image.length(); i++)
  {      
    outputValue = 0;

    // loop through kernel
    for(int j = 0; j < kernel.length(); j++)
    {
      neighbour = ?;

      // discard out of bound neighbours 
      if (neighbour >= 0 && neighbour < imageSize)
      {
        imageValue = image[neighbour];
        kernelValue = kernel[j];          
        outputValue += imageValue * kernelValue;
      }
    }

    output[i] = outputValue;
  }        

  return output;
}

【问题讨论】:

    标签: java image-processing vector convolution neighbours


    【解决方案1】:

    i + j - (kernel.length / 2) 可能太短而无法回答:

    public class Convolution
    {
        public static void main(String[] args)
        {
            int image[] = { 0,1,2,3,4,5,6,7,8 };
            int kernel[] = { 0,1,2,3,4,5,6,7,8 };
    
            int output[] = convolve(image, kernel);
    
            for (int i=0; i<image.length; i++)
            {
                System.out.printf(output[i]+" ");
            }
        }
    
        public static int[] convolve(int[] image, int[] kernel)
        {       
            int[] output = new int[image.length];
    
            // loop through image
            for(int i = 0; i < image.length; i++)
            {      
                System.out.println("Compute output["+i+"]");
                int outputValue = 0;
    
                // loop through kernel
                for(int j = 0; j < kernel.length; j++)
                {
                    int neighbour = i + j - (kernel.length / 2);
    
                    // discard out of bound neighbours 
                    if (neighbour >= 0 && neighbour < image.length)
                    {
                        int imageValue = image[neighbour];
                        int kernelValue = kernel[j];          
                        outputValue += imageValue * kernelValue;
    
                        System.out.println("image["+neighbour+"] and kernel["+j+"]");
                    }
                }
    
                output[i] = outputValue;
            }        
    
            return output;
        }
    }
    

    请注意,这仅在内核具有 奇数 长度时才能正常工作。实际上,您所做的是将内核的 center 移动到图像空间(这就是 kernel.length/2 的来源)。对于 even 长度的内核,例如 0 1 2 3,您必须决定是否要包含...

    0 1 2 3 4 (image)
    3                   <- This line and/or ...
    2 3
    1 2 3
    0 1 2 3
      0 1 2 3
        0 1 2
          0 1 
            0           <- ... this line
    

    【讨论】:

    • 谢谢,这正是我一直在寻找的,不幸的是,我刚刚意识到我还有另一个与邻域索引有关的问题。我在这里打开了一个单独的问题:stackoverflow.com/questions/31704468/…
    • 图片和滤镜大小不同时如何工作?
    • @Raffaele 它应该可以正常工作 - 但我只是用 9 号图像和 7 号过滤器快速测试它,它似乎是正确的。您是否发现我遗漏的任何问题?
    • 我正在回答 OP 链接的另一个问题。我正在关注这个,我想知道如何在不知道宽度的情况下计算邻居。我可能在这里忽略了这一点(不是图像处理专家),但在我看来,当图像为 100 或 10000 像素宽时,该程序会计算相同的邻居,因为它没有考虑图像宽度。输入向量实际上是一个二维图像
    • @Raffaele 是的,我也查看了另一个问题,而且(对我而言)这种方法基本上是有缺陷的。或者这样说:是的,当您想要访问以 1D 数组形式给出的 2D 图像中的“邻居”时,您必须知道图像宽度。
    【解决方案2】:

    听起来你想要一个滑块之类的东西:

    static class Slider implements Iterable<List<Integer>> {
    
        final List<Integer> kernel;
        final int imageWidth;
        final int center;
    
        public Slider(int imageWidth, int kernelWidth) {
            // Build my kernel offsets list.
            this.kernel = new ArrayList<>(kernelWidth);
            for (int i = 0; i < kernelWidth; i++) {
                kernel.add(i, i);
            }
            // Which kernel cell is in the center.
            center = kernelWidth / 2;
            // Remember the image width.
            this.imageWidth = imageWidth;
        }
    
        @Override
        public Iterator<List<Integer>> iterator() {
            return new Iterator<List<Integer>>() {
                int x = 0;
    
                @Override
                public boolean hasNext() {
                    return x < imageWidth;
                }
    
                @Override
                public List<Integer> next() {
                    List<Integer> slice = kernel.subList(Math.max(0, center - x), Math.min(kernel.size(), center - x + kernel.size()));
                    x += 1;
                    return slice;
                }
    
            };
        }
    
    }
    
    public void test() {
        List<Integer> image = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> kernel = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
        // Keep track of image position.
        int x = 0;
        for (List<Integer> slice : new Slider(image.size(), kernel.size())) {
            System.out.println(slice);
            int outputValue = 0;
            int imageValue = image.get(x++);
            for (Integer o : slice) {
                int kernelValue = kernel.get(o);
                outputValue += imageValue * kernelValue;
            }
            System.out.println("outputValue=" + outputValue);
        }
    }
    

    【讨论】:

    • 谢谢,这绝对是一个有趣的方法,最后我需要一些不那么面向对象的东西,但我对此进行了测试并且确实有效。我现在意识到我并没有完全意识到我的问题,并就此提出了一个单独的问题:stackoverflow.com/questions/31704468/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多