How to resolve the algorithm Image convolution step by step in the Nim programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Image convolution step by step in the Nim programming language

Table of Contents

Problem Statement

One class of image digital filters is described by a rectangular matrix of real coefficients called kernel convoluted in a sliding window of image pixels. Usually the kernel is square

K

k l

{\displaystyle K_{kl}}

, where k, l are in the range -R,-R+1,..,R-1,R. W=2R+1 is the kernel width. The filter determines the new value of a grayscale image pixel Pij as a convolution of the image pixels in the window centered in i, j and the kernel values: Color images are usually split into the channels which are filtered independently. A color model can be changed as well, i.e. filtration is performed not necessarily in RGB. Common kernels sizes are 3x3 and 5x5. The complexity of filtrating grows quadratically (O(n2)) with the kernel width. Task: Write a generic convolution 3x3 kernel filter. Optionally show some end user filters that use this generic one. (You can use, to test the functions below, these input and output solutions.)

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Image convolution step by step in the Nim programming language

Source code in the nim programming language

import math, lenientops, strutils
import nimPNG, bitmap, grayscale_image

type ConvolutionFilter = object
  kernel: seq[seq[float]]
  divisor: float
  offset: float
  name: string

func convolve[T: Image|GrayImage](img: T; filter: ConvolutionFilter): T =

  assert not img.isNil
  assert filter.divisor.classify != fcNan and filter.offset.classify != fcNan
  assert filter.divisor != 0
  assert filter.kernel.len > 0 and filter.kernel[0].len > 0
  for row in filter.kernel:
    assert row.len == filter.kernel[0].len
  assert filter.kernel.len mod 2 == 1
  assert filter.kernel[0].len mod 2 == 1
  assert img.h >= filter.kernel.len
  assert img.w >= filter.kernel[0].len

  let knx2 = filter.kernel[0].len div 2
  let kny2 = filter.kernel.len div 2

  when T is Image:
    result = newImage(img.w, img.h)
  else:
    result = newGrayImage(img.w, img.h)

  for y in kny2..<(img.h - kny2):
    for x in knx2..<(img.w - knx2):
      when T is Image:
        var total: array[3, float]
      else:
        var total: float
      for sy, kRow in filter.kernel:
        for sx, k in kRow:
          let p = img[x + sx - knx2, y + sy - kny2]
          when T is Image:
            total[0] += p.r * k
            total[1] += p.g * k
            total[2] += p.b * k
          else:
            total += p * k

      let d = filter.divisor
      let off = filter.offset * Luminance.high
      when T is Image:
        result[x, y] = color(min(max(total[0] / d + off, Luminance.low.float),
                                Luminance.high.float).toInt,
                            min(max(total[1] / d + off, Luminance.low.float),
                                Luminance.high.float).toInt,
                            min(max(total[2] / d + off, Luminance.low.float),
                                Luminance.high.float).toInt)
      else:
        result[x, y] = Luminance(min(max(total / d + off, Luminance.low.float),
                                     Luminance.high.float).toInt)

const
  Input = "lena.png"
  Output1 = "lena_$1.png"
  Output2 = "lena_gray_$1.png"

const Filters = [ConvolutionFilter(kernel: @[@[-2.0, -1.0, 0.0],
                                             @[-1.0,  1.0, 1.0],
                                             @[ 0.0,  1.0, 2.0]],
                                   divisor: 1.0, offset: 0.0, name: "Emboss"),

                 ConvolutionFilter(kernel: @[@[-1.0, -1.0, -1.0],
                                             @[-1.0,  9.0, -1.0],
                                             @[-1.0, -1.0, -1.0]],
                                   divisor: 1.0, offset: 0.0, name: "Sharpen"),

                 ConvolutionFilter(kernel: @[@[-1.0, -2.0, -1.0],
                                             @[ 0.0,  0.0,  0.0],
                                             @[ 1.0,  2.0,  1.0]],
                                   divisor: 1.0, offset: 0.5, name: "Sobel_emboss"),

                 ConvolutionFilter(kernel: @[@[1.0, 1.0, 1.0],
                                             @[1.0, 1.0, 1.0],
                                             @[1.0, 1.0, 1.0]],
                                   divisor: 9.0, offset: 0.0, name: "Box_blur"),

                 ConvolutionFilter(kernel: @[@[1.0,  4.0,  7.0,  4.0, 1.0],
                                             @[4.0, 16.0, 26.0, 16.0, 4.0],
                                             @[7.0, 26.0, 41.0, 26.0, 7.0],
                                             @[4.0, 16.0, 26.0, 16.0, 4.0],
                                             @[1.0,  4.0,  7.0,  4.0, 1.0]],
                                   divisor: 273.0, offset: 0.0, name: "Gaussian_blur")]

let pngImage = loadPNG24(seq[byte], Input).get()

# Convert to an image managed by the "bitmap" module.
let img = newImage(pngImage.width, pngImage.height)
for i in 0..img.pixels.high:
  img.pixels[i] = color(pngImage.data[3 * i],
                        pngImage.data[3 * i + 1],
                        pngImage.data[3 * i + 2])

for filter in Filters:
  let result = img.convolve(filter)
  var data = newSeqOfCap[byte](result.pixels.len * 3)
  for color in result.pixels:
    data.add([color.r, color.g, color.b])
  let output = Output1.format(filter.name)
  if savePNG24(output, data, result.w, result.h).isOk:
    echo "Saved: ", output

let grayImg = img.toGrayImage()
for filter in Filters:
  let result = grayImg.convolve(filter).toImage()
  var data = newSeqOfCap[byte](result.pixels.len * 3)
  for color in result.pixels:
    data.add([color.r, color.g, color.b])
  let output = Output2.format(filter.name)
  if savePNG24(output, data, result.w, result.h).isOk:
    echo "Saved: ", output


  

You may also check:How to resolve the algorithm Digital root step by step in the Go programming language
You may also check:How to resolve the algorithm Hello world/Newline omission step by step in the Nanoquery programming language
You may also check:How to resolve the algorithm Word wheel step by step in the PureBasic programming language
You may also check:How to resolve the algorithm Sockets step by step in the PicoLisp programming language
You may also check:How to resolve the algorithm Loop over multiple arrays simultaneously step by step in the Ruby programming language