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 =