Task: Image convolution




Go: package main

import (
    "fmt"
    "image"
    "image/color"
    "image/jpeg"
    "math"
    "os"
)



func kf3(k *[9]float64, src, dst *image.Gray) {
    for y := src.Rect.Min.Y; y < src.Rect.Max.Y; y++ {
        for x := src.Rect.Min.X; x < src.Rect.Max.X; x++ {
            var sum float64
            var i int
            for yo := y - 1; yo <= y+1; yo++ {
                for xo := x - 1; xo <= x+1; xo++ {
                    if (image.Point{xo, yo}).In(src.Rect) {
                        sum += k[i] * float64(src.At(xo, yo).(color.Gray).Y)
                    } else {
                        sum += k[i] * float64(src.At(x, y).(color.Gray).Y)
                    }
                    i++
                }
            }
            dst.SetGray(x, y,
                color.Gray{uint8(math.Min(255, math.Max(0, sum)))})
        }
    }
}

var blur = [9]float64{
    1. / 9, 1. / 9, 1. / 9,
    1. / 9, 1. / 9, 1. / 9,
    1. / 9, 1. / 9, 1. / 9}



func blurY(src *image.YCbCr) *image.YCbCr {
    dst := *src

    
    if src.Rect.Max.X == src.Rect.Min.X || src.Rect.Max.Y == src.Rect.Min.Y {
        return &dst
    }

    
    srcGray := image.Gray{src.Y, src.YStride, src.Rect}
    dstGray := srcGray
    dstGray.Pix = make([]uint8, len(src.Y))
    kf3(&blur, &srcGray, &dstGray) 

    
    dst.Y = dstGray.Pix                   
    dst.Cb = append([]uint8{}, src.Cb...) 
    dst.Cr = append([]uint8{}, src.Cr...)
    return &dst
}

func main() {
    
    
    f, err := os.Open("Lenna100.jpg")
    if err != nil {
        fmt.Println(err)
        return
    }
    img, err := jpeg.Decode(f)
    if err != nil {
        fmt.Println(err)
        return
    }
    f.Close()
    y, ok := img.(*image.YCbCr)
    if !ok {
        fmt.Println("expected color jpeg")
        return
    }
    f, err = os.Create("blur.jpg")
    if err != nil {
        fmt.Println(err)
        return
    }
    err = jpeg.Encode(f, blurY(y), &jpeg.Options{90})
    if err != nil {
        fmt.Println(err)
    }
}


Java: import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.*;

public class ImageConvolution
{
  public static class ArrayData
  {
    public final int[] dataArray;
    public final int width;
    public final int height;
    
    public ArrayData(int width, int height)
    {
      this(new int[width * height], width, height);
    }
    
    public ArrayData(int[] dataArray, int width, int height)
    {
      this.dataArray = dataArray;
      this.width = width;
      this.height = height;
    }
    
    public int get(int x, int y)
    {  return dataArray[y * width + x];  }
    
    public void set(int x, int y, int value)
    {  dataArray[y * width + x] = value;  }
  }
  
  private static int bound(int value, int endIndex)
  {
    if (value < 0)
      return 0;
    if (value < endIndex)
      return value;
    return endIndex - 1;
  }
  
  public static ArrayData convolute(ArrayData inputData, ArrayData kernel, int kernelDivisor)
  {
    int inputWidth = inputData.width;
    int inputHeight = inputData.height;
    int kernelWidth = kernel.width;
    int kernelHeight = kernel.height;
    if ((kernelWidth <= 0) || ((kernelWidth & 1) != 1))
      throw new IllegalArgumentException("Kernel must have odd width");
    if ((kernelHeight <= 0) || ((kernelHeight & 1) != 1))
      throw new IllegalArgumentException("Kernel must have odd height");
    int kernelWidthRadius = kernelWidth >>> 1;
    int kernelHeightRadius = kernelHeight >>> 1;
    
    ArrayData outputData = new ArrayData(inputWidth, inputHeight);
    for (int i = inputWidth - 1; i >= 0; i--)
    {
      for (int j = inputHeight - 1; j >= 0; j--)
      {
        double newValue = 0.0;
        for (int kw = kernelWidth - 1; kw >= 0; kw--)
          for (int kh = kernelHeight - 1; kh >= 0; kh--)
            newValue += kernel.get(kw, kh) * inputData.get(
                          bound(i + kw - kernelWidthRadius, inputWidth),
                          bound(j + kh - kernelHeightRadius, inputHeight));
        outputData.set(i, j, (int)Math.round(newValue / kernelDivisor));
      }
    }
    return outputData;
  }
  
  public static ArrayData[] getArrayDatasFromImage(String filename) throws IOException
  {
    BufferedImage inputImage = ImageIO.read(new File(filename));
    int width = inputImage.getWidth();
    int height = inputImage.getHeight();
    int[] rgbData = inputImage.getRGB(0, 0, width, height, null, 0, width);
    ArrayData reds = new ArrayData(width, height);
    ArrayData greens = new ArrayData(width, height);
    ArrayData blues = new ArrayData(width, height);
    for (int y = 0; y < height; y++)
    {
      for (int x = 0; x < width; x++)
      {
        int rgbValue = rgbData[y * width + x];
        reds.set(x, y, (rgbValue >>> 16) & 0xFF);
        greens.set(x, y, (rgbValue >>> 8) & 0xFF);
        blues.set(x, y, rgbValue & 0xFF);
      }
    }
    return new ArrayData[] { reds, greens, blues };
  }
  
  public static void writeOutputImage(String filename, ArrayData[] redGreenBlue) throws IOException
  {
    ArrayData reds = redGreenBlue[0];
    ArrayData greens = redGreenBlue[1];
    ArrayData blues = redGreenBlue[2];
    BufferedImage outputImage = new BufferedImage(reds.width, reds.height,
                                                  BufferedImage.TYPE_INT_ARGB);
    for (int y = 0; y < reds.height; y++)
    {
      for (int x = 0; x < reds.width; x++)
      {
        int red = bound(reds.get(x, y), 256);
        int green = bound(greens.get(x, y), 256);
        int blue = bound(blues.get(x, y), 256);
        outputImage.setRGB(x, y, (red << 16) | (green << 8) | blue | -0x01000000);
      }
    }
    ImageIO.write(outputImage, "PNG", new File(filename));
    return;
  }
  
  public static void main(String[] args) throws IOException
  {
    int kernelWidth = Integer.parseInt(args[2]);
    int kernelHeight = Integer.parseInt(args[3]);
    int kernelDivisor = Integer.parseInt(args[4]);
    System.out.println("Kernel size: " + kernelWidth + "x" + kernelHeight +
                       ", divisor=" + kernelDivisor);
    int y = 5;
    ArrayData kernel = new ArrayData(kernelWidth, kernelHeight);
    for (int i = 0; i < kernelHeight; i++)
    {
      System.out.print("[");
      for (int j = 0; j < kernelWidth; j++)
      {
        kernel.set(j, i, Integer.parseInt(args[y++]));
        System.out.print(" " + kernel.get(j, i) + " ");
      }
      System.out.println("]");
    }
    
    ArrayData[] dataArrays = getArrayDatasFromImage(args[0]);
    for (int i = 0; i < dataArrays.length; i++)
      dataArrays[i] = convolute(dataArrays[i], kernel, kernelDivisor);
    writeOutputImage(args[1], dataArrays);
    return;
  }
}