How to resolve the algorithm Image convolution step by step in the Kotlin programming language
How to resolve the algorithm Image convolution step by step in the Kotlin 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 Kotlin programming language
This Kotlin code performs image processing by applying a convolution operation on an input image. A step-by-step explanation of the code:
-
Data Structures:
ArrayData
class: Represents a 2D array of integers. It provides getters and setters to access and modify elements.- Kernel is represented as an
ArrayData
object.
-
Bounds Checking:
bound
function ensures that values are within the specified bounds. It prevents accessing out-of-range indices.
-
Convolution Function:
convolute
function applies a convolution operation on an input data array using a specified kernel. It iterates over each pixel in the input, calculates a new value based on the kernel and neighbor pixel values, and stores the result in the output array. The result is divided by thekernelDivisor
to normalize the values.
-
Image Loading and Conversion:
getArrayDatasFromImage
function loads an image from a file and extracts the Red, Green, and Blue channel data as separateArrayData
objects.
-
Output Image Generation:
writeOutputImage
function creates a new image with the modified Red, Green, and Blue channels and saves it to a file.
-
Main Function:
- Parses command line arguments for kernel size and divisor.
- Initializes the kernel
ArrayData
with the specified values from the arguments. - Loads the input image and applies the convolution operation to each channel of the image.
- Generates and saves the output image with the modified pixel values.
This code allows you to apply convolution effects, such as Gaussian blur, sharpening, or edge detection, to images. Convolution is a fundamental operation in image processing and computer vision, used to modify and enhance images based on mathematical operations.
Source code in the kotlin programming language
// version 1.2.10
import kotlin.math.round
import java.awt.image.*
import java.io.File
import javax.imageio.*
class ArrayData(val width: Int, val height: Int) {
var dataArray = IntArray(width * height)
operator fun get(x: Int, y: Int) = dataArray[y * width + x]
operator fun set(x: Int, y: Int, value: Int) {
dataArray[y * width + x] = value
}
}
fun bound(value: Int, endIndex: Int) = when {
value < 0 -> 0
value < endIndex -> value
else -> endIndex - 1
}
fun convolute(
inputData: ArrayData,
kernel: ArrayData,
kernelDivisor: Int
): ArrayData {
val inputWidth = inputData.width
val inputHeight = inputData.height
val kernelWidth = kernel.width
val kernelHeight = kernel.height
if (kernelWidth <= 0 || (kernelWidth and 1) != 1)
throw IllegalArgumentException("Kernel must have odd width")
if (kernelHeight <= 0 || (kernelHeight and 1) != 1)
throw IllegalArgumentException("Kernel must have odd height")
val kernelWidthRadius = kernelWidth ushr 1
val kernelHeightRadius = kernelHeight ushr 1
val outputData = ArrayData(inputWidth, inputHeight)
for (i in inputWidth - 1 downTo 0) {
for (j in inputHeight - 1 downTo 0) {
var newValue = 0.0
for (kw in kernelWidth - 1 downTo 0) {
for (kh in kernelHeight - 1 downTo 0) {
newValue += kernel[kw, kh] * inputData[
bound(i + kw - kernelWidthRadius, inputWidth),
bound(j + kh - kernelHeightRadius, inputHeight)
].toDouble()
outputData[i, j] = round(newValue / kernelDivisor).toInt()
}
}
}
}
return outputData
}
fun getArrayDatasFromImage(filename: String): Array<ArrayData> {
val inputImage = ImageIO.read(File(filename))
val width = inputImage.width
val height = inputImage.height
val rgbData = inputImage.getRGB(0, 0, width, height, null, 0, width)
val reds = ArrayData(width, height)
val greens = ArrayData(width, height)
val blues = ArrayData(width, height)
for (y in 0 until height) {
for (x in 0 until width) {
val rgbValue = rgbData[y * width + x]
reds[x, y] = (rgbValue ushr 16) and 0xFF
greens[x,y] = (rgbValue ushr 8) and 0xFF
blues[x, y] = rgbValue and 0xFF
}
}
return arrayOf(reds, greens, blues)
}
fun writeOutputImage(filename: String, redGreenBlue: Array<ArrayData>) {
val (reds, greens, blues) = redGreenBlue
val outputImage = BufferedImage(
reds.width, reds.height, BufferedImage.TYPE_INT_ARGB
)
for (y in 0 until reds.height) {
for (x in 0 until reds.width) {
val red = bound(reds[x , y], 256)
val green = bound(greens[x , y], 256)
val blue = bound(blues[x, y], 256)
outputImage.setRGB(
x, y, (red shl 16) or (green shl 8) or blue or -0x01000000
)
}
}
ImageIO.write(outputImage, "PNG", File(filename))
}
fun main(args: Array<String>) {
val kernelWidth = args[2].toInt()
val kernelHeight = args[3].toInt()
val kernelDivisor = args[4].toInt()
println("Kernel size: $kernelWidth x $kernelHeight, divisor = $kernelDivisor")
var y = 5
val kernel = ArrayData(kernelWidth, kernelHeight)
for (i in 0 until kernelHeight) {
print("[")
for (j in 0 until kernelWidth) {
kernel[j, i] = args[y++].toInt()
print(" ${kernel[j, i]} ")
}
println("]")
}
val dataArrays = getArrayDatasFromImage(args[0])
for (i in 0 until dataArrays.size) {
dataArrays[i] = convolute(dataArrays[i], kernel, kernelDivisor)
}
writeOutputImage(args[1], dataArrays)
}
You may also check:How to resolve the algorithm Guess the number step by step in the Delphi programming language
You may also check:How to resolve the algorithm Read a configuration file step by step in the PicoLisp programming language
You may also check:How to resolve the algorithm Jordan-Pólya numbers step by step in the Raku programming language
You may also check:How to resolve the algorithm System time step by step in the AmigaBASIC programming language
You may also check:How to resolve the algorithm Split a character string based on change of character step by step in the XPL0 programming language