diff --git a/src/ImageProcessing/Agents.fs b/src/ImageProcessing/Agents.fs
index e0fe625..0d85d9d 100644
--- a/src/ImageProcessing/Agents.fs
+++ b/src/ImageProcessing/Agents.fs
@@ -2,6 +2,10 @@ module Agents
open MyImage
+///
+/// Represents a logger, which receives messages and prints them
+/// to the console
+///
let logger =
MailboxProcessor.Start(fun inbox ->
@@ -11,10 +15,20 @@ let logger =
printfn $"{message}"
})
+///
+/// Represents a message type that can either contain an image or
+/// an end-of-stream signal, which is used with an asynchronous reply
+/// channel
+///
type imageMessage =
| Image of MyImage
| EOS of AsyncReplyChannel
+///
+/// Defines an image saver agent that listens to image-messages and
+/// saves them to a specified output directory
+///
+/// The directory where the images will be saved.
let imageSaver outputDirectory =
let initial (inbox: MailboxProcessor) =
@@ -35,6 +49,12 @@ let imageSaver outputDirectory =
MailboxProcessor.Start initial
+///
+/// Defines an image processing agent that listens to image-messages and
+/// applies the given function
+///
+/// The image editing function to apply to incoming images.
+/// The MailboxProcessor that receives the processed images.
let imageProcessor imageEditor (receiver: MailboxProcessor<_>) =
let initial (inbox: MailboxProcessor) =
@@ -56,10 +76,21 @@ let imageProcessor imageEditor (receiver: MailboxProcessor<_>) =
MailboxProcessor.Start initial
+///
+/// Represents a message type encapsulated either a string path
+/// or an end-of-stream message, which is used with an asynchronous
+/// reply channel
+///
type pathMessage =
| Path of string
| EOS of AsyncReplyChannel
+///
+/// Creates an image processing and saving MailboxProcessor that continuously receives path messages from
+/// an input mailbox. It loads, edits, and saves images to the specified output directory
+///
+/// The image editing function to apply to the loaded images.
+/// The directory where the edited images will be saved.
let imageFullProcessor imageEditor outputDirectory =
let initial (inbox: MailboxProcessor) =
diff --git a/src/ImageProcessing/ArguCommands.fs b/src/ImageProcessing/ArguCommands.fs
index 02afb77..a6f3c0c 100644
--- a/src/ImageProcessing/ArguCommands.fs
+++ b/src/ImageProcessing/ArguCommands.fs
@@ -2,6 +2,9 @@ module ArguCommands
open Argu
+///
+/// Represents the available CPU/GPU platforms for image processing.
+///
type ArguProcessingUnits =
| CPU
| NvidiaGPU
diff --git a/src/ImageProcessing/CPU.fs b/src/ImageProcessing/CPU.fs
index 5812954..bca48de 100644
--- a/src/ImageProcessing/CPU.fs
+++ b/src/ImageProcessing/CPU.fs
@@ -2,6 +2,12 @@ module CPU
open MyImage
+///
+/// Applies a filter to an input image
+///
+/// The filter kernel to apply to the image.
+/// The input image to process.
+/// A new processed image
let applyFilter filter (image: MyImage) =
let filterDiameter = (Array2D.length1 filter) / 2
@@ -24,6 +30,12 @@ let applyFilter filter (image: MyImage) =
MyImage(Array.mapi (fun p _ -> byte (pixelProcessing p)) image.Data, image.Width, image.Height, image.Name)
+///
+/// Rotates an input image either clockwise or counterclockwise.
+///
+/// True to rotate the image clockwise or false for counterclockwise rotation.
+/// The input image to rotate.
+/// A new rotated image
let rotate (isClockwise: bool) (image: MyImage) =
let buffer = Array.zeroCreate (image.Width * image.Height)
@@ -39,6 +51,12 @@ let rotate (isClockwise: bool) (image: MyImage) =
MyImage(buffer, image.Height, image.Width, image.Name)
+///
+/// Flips an input image either vertically or horizontally.
+///
+/// True to flip the image vertically or false for horizontal flip.
+/// The input image to flip.
+/// A new flipped image
let flip (isVertical: bool) (image: MyImage) =
let buffer = Array.zeroCreate (image.Height * image.Width)
diff --git a/src/ImageProcessing/GPU.fs b/src/ImageProcessing/GPU.fs
index b764059..e6e7727 100644
--- a/src/ImageProcessing/GPU.fs
+++ b/src/ImageProcessing/GPU.fs
@@ -2,6 +2,16 @@ module GPU
open Brahma.FSharp
+///
+/// Creates compiled GPU filter kernel.
+///
+/// The representation of OpenCL context.
+/// The size of the local work group.
+///
+/// A function that takes a command queue, filter parameters, image data, and result array as inputs
+/// and asynchronously applies the filter kernel to the image using the GPU.
+/// The resulting image data is stored in the result array.
+///
let applyFilterGPUKernel (clContext: ClContext) localWorkSize =
let kernel =
@@ -36,6 +46,16 @@ let applyFilterGPUKernel (clContext: ClContext) localWorkSize =
commandQueue.Post(Msg.CreateRunMsg kernel)
result
+///
+/// Creates compiled GPU rotation kernel.
+///
+/// The representation of OpenCL context.
+/// The size of the local work group.
+///
+/// A function that takes a command queue, rotation parameter, image data, and result array as inputs
+/// and asynchronously rotates the image using the GPU.
+/// The resulting image data is stored in the result array.
+///
let rotateGPUKernel (clContext: ClContext) localWorkSize =
let kernel =
@@ -63,6 +83,16 @@ let rotateGPUKernel (clContext: ClContext) localWorkSize =
commandQueue.Post(Msg.CreateRunMsg kernel)
result
+///
+/// Creates compiled GPU flip kernel.
+///
+/// The representation of OpenCL context.
+/// The size of the local work group.
+///
+/// A function that takes a command queue, flip parameter, image data, and result array as inputs
+/// and asynchronously flips the image using the GPU.
+/// The resulting image data is stored in the result array.
+///
let flipGPUKernel (clContext: ClContext) localWorkSize =
let kernel =
@@ -90,6 +120,14 @@ let flipGPUKernel (clContext: ClContext) localWorkSize =
commandQueue.Post(Msg.CreateRunMsg kernel)
result
+///
+/// Applies a filter to the specified image using GPU.
+///
+/// The representation of OpenCL context.
+/// The size of the local work group.
+///
+/// A function that takes a filter kernel and an image, and asynchronously applies the filter to the image using GPU.
+///
let applyFilter (clContext: ClContext) (localWorkSize: int) =
let applyFilterKernel = applyFilterGPUKernel clContext localWorkSize
@@ -120,6 +158,15 @@ let applyFilter (clContext: ClContext) (localWorkSize: int) =
MyImage.MyImage(result, image.Width, image.Height, image.Name)
+///
+/// Rotates the image clockwise or counterclockwise using GPU.
+///
+/// The representation of OpenCL context.
+/// The size of the local work group.
+///
+/// A function that takes a boolean value indicating the rotation direction,
+/// and an image, and asynchronously rotates the image using the GPU.
+///
let rotate (clContext: ClContext) (localWorkSize: int) =
let rotateKernel = rotateGPUKernel clContext localWorkSize
@@ -147,6 +194,15 @@ let rotate (clContext: ClContext) (localWorkSize: int) =
MyImage.MyImage(result, image.Height, image.Width, image.Name)
+///
+/// Flips the image vertically or horizontally using GPU.
+///
+/// The representation of OpenCL context.
+/// The size of the local work group.
+///
+/// A function that takes a boolean value indicating the flip direction,
+/// and an image, and asynchronously flips the image using the GPU.
+///
let flip (clContext: ClContext) (localWorkSize: int) =
let flipKernel = flipGPUKernel clContext localWorkSize
diff --git a/src/ImageProcessing/Helper.fs b/src/ImageProcessing/Helper.fs
index d8e648b..3ed516b 100644
--- a/src/ImageProcessing/Helper.fs
+++ b/src/ImageProcessing/Helper.fs
@@ -1,5 +1,10 @@
module Helper
+///
+/// Converts a 2D array to a flat 1D array
+///
+/// The input 2D array to be flattened
+/// A 1D array containing all the elements of the input 2D array.
let toFlatArray array2D =
seq {
for x in [ 0 .. (Array2D.length1 array2D) - 1 ] do
@@ -8,5 +13,11 @@ let toFlatArray array2D =
}
|> Array.ofSeq
+///
+/// Generates a file path by combining an output directory and an image name.
+///
+/// The directory where the file will be located.
+/// The name of the file or image.
+/// A full file path obtained by combining the output directory and image name.
let generatePath outputDirectory (imageName: string) =
System.IO.Path.Combine(outputDirectory, imageName)
diff --git a/src/ImageProcessing/Kernels.fs b/src/ImageProcessing/Kernels.fs
index 759663f..8a2348c 100644
--- a/src/ImageProcessing/Kernels.fs
+++ b/src/ImageProcessing/Kernels.fs
@@ -1,16 +1,31 @@
module Kernels
+///
+/// Represents a Gaussian blur kernel for filter applying on CPU as a 2D array
+///
let gaussianBlurKernel =
array2D [ [ 1; 4; 6; 4; 1 ]; [ 4; 16; 24; 16; 4 ]; [ 6; 24; 36; 24; 6 ]; [ 4; 16; 24; 16; 4 ]; [ 1; 4; 6; 4; 1 ] ]
|> Array2D.map (fun x -> (float32 x) / 256.0f)
+///
+/// Represents a edges kernel for filter applying on CPU as a 2D array
+///
let edgesKernel =
array2D [ [ 0; 0; -1; 0; 0 ]; [ 0; 0; -1; 0; 0 ]; [ 0; 0; 2; 0; 0 ]; [ 0; 0; 0; 0; 0 ]; [ 0; 0; 0; 0; 0 ] ]
|> Array2D.map float32
+///
+/// Represents a darken kernel for filter applying on CPU as a 2D array
+///
let darkenKernel = array2D [ [ 2 ] ] |> Array2D.map (fun x -> (float32 x) / 10.0f)
+///
+/// Represents a lighten kernel for filter applying on CPU as a 2D array
+///
let lightenKernel = array2D [ [ 2 ] ] |> Array2D.map float32
+///
+/// Represents a sharpen kernel for filter applying on CPU as a 2D array
+///
let sharpenKernel =
array2D [ [ 0; -1; 0 ]; [ -1; 5; -1 ]; [ 0; -1; -0 ] ] |> Array2D.map float32
diff --git a/src/ImageProcessing/MyImage.fs b/src/ImageProcessing/MyImage.fs
index 3ac8733..419bce9 100644
--- a/src/ImageProcessing/MyImage.fs
+++ b/src/ImageProcessing/MyImage.fs
@@ -3,6 +3,9 @@ module MyImage
open SixLabors.ImageSharp
open SixLabors.ImageSharp.PixelFormats
+///
+/// Encapsulates an image, which includes both the byte pixel data and its associated attributes.
+///
type MyImage =
val Data: array
val Width: int
@@ -15,6 +18,10 @@ type MyImage =
Height = height
Name = name }
+///
+/// Loads the image located at the specified file path.
+///
+/// The path where the image is located. The image name and extension are required.
let load (filePath: string) =
let image = Image.Load filePath
@@ -23,6 +30,11 @@ let load (filePath: string) =
MyImage(buffer, image.Width, image.Height, System.IO.Path.GetFileName filePath)
+///
+/// Saves the image to the specified directory in the same extension as the input.
+///
+/// Saved image.
+/// Path to the directory where the image will be saved.
let save (image: MyImage) filePath =
let image = Image.LoadPixelData(image.Data, image.Width, image.Height)
diff --git a/src/ImageProcessing/Process.fs b/src/ImageProcessing/Process.fs
index 821f85b..12b678a 100644
--- a/src/ImageProcessing/Process.fs
+++ b/src/ImageProcessing/Process.fs
@@ -5,12 +5,18 @@ open MyImage
open Kernels
open Brahma.FSharp
+///
+/// Specifies the level of agents support.
+///
type AgentsSupport =
| Full // Uses a single agent to open, process and save
| Partial // Uses different agents for each transformation and saving
| PartialUsingComposition // Uses one agent for transformation and one for save
| No // Uses naive image processing function
+///
+/// Represents the available image transformations.
+///
type Transformations =
| Gauss
| Sharpen
@@ -22,10 +28,20 @@ type Transformations =
| FlipV // Vertical flip
| FlipH // Horizontal flip
+///
+/// Represents processing device.
+///
type ProcessingUnits =
| CPU
| GPU of Platform
+///
+/// Parses an image transformation and returns the corresponding CPU-based transformation function.
+///
+/// The transformation type to apply.
+///
+/// A CPU-based transformation function corresponding to the specified transformation type.
+///
let transformationsParserCPU transformation =
match transformation with
| Gauss -> CPU.applyFilter gaussianBlurKernel
@@ -38,6 +54,15 @@ let transformationsParserCPU transformation =
| FlipV -> CPU.rotate true
| FlipH -> CPU.flip false
+///
+/// Generates GPU kernel functions for applying various image transformations.
+///
+/// The OpenCL context for GPU processing.
+/// The local work size for GPU computation.
+///
+/// A function that takes a transformation type and returns the corresponding GPU
+/// kernel function for applying the specified transformation.
+///
let transformationsParserGPU (clContext: ClContext) (localWorkSize: int) =
let applyFilterKernel = GPU.applyFilter clContext localWorkSize
@@ -56,6 +81,14 @@ let transformationsParserGPU (clContext: ClContext) (localWorkSize: int) =
| FlipV -> flipKernel true
| FlipH -> flipKernel false
+///
+/// Processes images located at the specified input path and saves the processed images to the specified output path.
+///
+/// The path where the input images are located.
+/// The path where the processed images will be saved.
+/// The GPU platform to be used for processing.
+/// A list of functions to be applied to the images.
+/// Specifies the level of agent support.
let processImages inputPath outputPath processingUnit imageEditorsList agentsSupport =
let listAllImages directory =