Skip to content
This repository has been archived by the owner on Jul 6, 2024. It is now read-only.

Commit

Permalink
Add XML documentation comments
Browse files Browse the repository at this point in the history
  • Loading branch information
PolinaSavelyeva committed Sep 8, 2023
1 parent 6e032f0 commit 034de1d
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/ImageProcessing/Agents.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ module Agents

open MyImage

/// <summary>
/// Represents a logger, which receives messages and prints them
/// to the console
/// </summary>
let logger =

MailboxProcessor.Start(fun inbox ->
Expand All @@ -11,10 +15,20 @@ let logger =
printfn $"{message}"
})

/// <summary>
/// Represents a message type that can either contain an image or
/// an end-of-stream signal, which is used with an asynchronous reply
/// channel
/// </summary>
type imageMessage =
| Image of MyImage
| EOS of AsyncReplyChannel<unit>

/// <summary>
/// Defines an image saver agent that listens to image-messages and
/// saves them to a specified output directory
/// </summary>
/// <param name="outputDirectory">The directory where the images will be saved.</param>
let imageSaver outputDirectory =

let initial (inbox: MailboxProcessor<imageMessage>) =
Expand All @@ -35,6 +49,12 @@ let imageSaver outputDirectory =

MailboxProcessor.Start initial

/// <summary>
/// Defines an image processing agent that listens to image-messages and
/// applies the given function
/// </summary>
/// <param name="imageEditor">The image editing function to apply to incoming images.</param>
/// <param name="receiver">The MailboxProcessor that receives the processed images.</param>
let imageProcessor imageEditor (receiver: MailboxProcessor<_>) =

let initial (inbox: MailboxProcessor<imageMessage>) =
Expand All @@ -56,10 +76,21 @@ let imageProcessor imageEditor (receiver: MailboxProcessor<_>) =

MailboxProcessor.Start initial

/// <summary>
/// Represents a message type encapsulated either a string path
/// or an end-of-stream message, which is used with an asynchronous
/// reply channel
/// </summary>
type pathMessage =
| Path of string
| EOS of AsyncReplyChannel<unit>

/// <summary>
/// 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
/// </summary>
/// <param name="imageEditor">The image editing function to apply to the loaded images.</param>
/// <param name="outputDirectory">The directory where the edited images will be saved.</param>
let imageFullProcessor imageEditor outputDirectory =

let initial (inbox: MailboxProcessor<pathMessage>) =
Expand Down
3 changes: 3 additions & 0 deletions src/ImageProcessing/ArguCommands.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ module ArguCommands

open Argu

/// <summary>
/// Represents the available CPU/GPU platforms for image processing.
/// </summary>
type ArguProcessingUnits =
| CPU
| NvidiaGPU
Expand Down
18 changes: 18 additions & 0 deletions src/ImageProcessing/CPU.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ module CPU

open MyImage

/// <summary>
/// Applies a filter to an input image
/// </summary>
/// <param name="filter">The filter kernel to apply to the image.</param>
/// <param name="image">The input image to process.</param>
/// <returns>A new processed image</returns>
let applyFilter filter (image: MyImage) =

let filterDiameter = (Array2D.length1 filter) / 2
Expand All @@ -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)

/// <summary>
/// Rotates an input image either clockwise or counterclockwise.
/// </summary>
/// <param name="isClockwise">True to rotate the image clockwise or false for counterclockwise rotation.</param>
/// <param name="image">The input image to rotate.</param>
/// <returns>A new rotated image</returns>
let rotate (isClockwise: bool) (image: MyImage) =

let buffer = Array.zeroCreate (image.Width * image.Height)
Expand All @@ -39,6 +51,12 @@ let rotate (isClockwise: bool) (image: MyImage) =

MyImage(buffer, image.Height, image.Width, image.Name)

/// <summary>
/// Flips an input image either vertically or horizontally.
/// </summary>
/// <param name="isVertical">True to flip the image vertically or false for horizontal flip.</param>
/// <param name="image">The input image to flip.</param>
/// <returns>A new flipped image</returns>
let flip (isVertical: bool) (image: MyImage) =

let buffer = Array.zeroCreate (image.Height * image.Width)
Expand Down
56 changes: 56 additions & 0 deletions src/ImageProcessing/GPU.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ module GPU

open Brahma.FSharp

/// <summary>
/// Creates compiled GPU filter kernel.
/// </summary>
/// <param name="clContext">The representation of OpenCL context.</param>
/// <param name="localWorkSize">The size of the local work group.</param>
/// <returns>
/// 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.
/// </returns>
let applyFilterGPUKernel (clContext: ClContext) localWorkSize =

let kernel =
Expand Down Expand Up @@ -36,6 +46,16 @@ let applyFilterGPUKernel (clContext: ClContext) localWorkSize =
commandQueue.Post(Msg.CreateRunMsg<INDRange, obj> kernel)
result

/// <summary>
/// Creates compiled GPU rotation kernel.
/// </summary>
/// <param name="clContext">The representation of OpenCL context.</param>
/// <param name="localWorkSize">The size of the local work group.</param>
/// <returns>
/// 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.
/// </returns>
let rotateGPUKernel (clContext: ClContext) localWorkSize =

let kernel =
Expand Down Expand Up @@ -63,6 +83,16 @@ let rotateGPUKernel (clContext: ClContext) localWorkSize =
commandQueue.Post(Msg.CreateRunMsg<INDRange, obj> kernel)
result

/// <summary>
/// Creates compiled GPU flip kernel.
/// </summary>
/// <param name="clContext">The representation of OpenCL context.</param>
/// <param name="localWorkSize">The size of the local work group.</param>
/// <returns>
/// 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.
/// </returns>
let flipGPUKernel (clContext: ClContext) localWorkSize =

let kernel =
Expand Down Expand Up @@ -90,6 +120,14 @@ let flipGPUKernel (clContext: ClContext) localWorkSize =
commandQueue.Post(Msg.CreateRunMsg<INDRange, obj> kernel)
result

/// <summary>
/// Applies a filter to the specified image using GPU.
/// </summary>
/// <param name="clContext">The representation of OpenCL context.</param>
/// <param name="localWorkSize">The size of the local work group.</param>
/// <returns>
/// A function that takes a filter kernel and an image, and asynchronously applies the filter to the image using GPU.
/// </returns>
let applyFilter (clContext: ClContext) (localWorkSize: int) =

let applyFilterKernel = applyFilterGPUKernel clContext localWorkSize
Expand Down Expand Up @@ -120,6 +158,15 @@ let applyFilter (clContext: ClContext) (localWorkSize: int) =

MyImage.MyImage(result, image.Width, image.Height, image.Name)

/// <summary>
/// Rotates the image clockwise or counterclockwise using GPU.
/// </summary>
/// <param name="clContext">The representation of OpenCL context.</param>
/// <param name="localWorkSize">The size of the local work group.</param>
/// <returns>
/// A function that takes a boolean value indicating the rotation direction,
/// and an image, and asynchronously rotates the image using the GPU.
/// </returns>
let rotate (clContext: ClContext) (localWorkSize: int) =

let rotateKernel = rotateGPUKernel clContext localWorkSize
Expand Down Expand Up @@ -147,6 +194,15 @@ let rotate (clContext: ClContext) (localWorkSize: int) =

MyImage.MyImage(result, image.Height, image.Width, image.Name)

/// <summary>
/// Flips the image vertically or horizontally using GPU.
/// </summary>
/// <param name="clContext">The representation of OpenCL context.</param>
/// <param name="localWorkSize">The size of the local work group.</param>
/// <returns>
/// A function that takes a boolean value indicating the flip direction,
/// and an image, and asynchronously flips the image using the GPU.
/// </returns>
let flip (clContext: ClContext) (localWorkSize: int) =

let flipKernel = flipGPUKernel clContext localWorkSize
Expand Down
11 changes: 11 additions & 0 deletions src/ImageProcessing/Helper.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
module Helper

/// <summary>
/// Converts a 2D array to a flat 1D array
/// </summary>
/// <param name="array2D">The input 2D array to be flattened</param>
/// <returns>A 1D array containing all the elements of the input 2D array.</returns>
let toFlatArray array2D =
seq {
for x in [ 0 .. (Array2D.length1 array2D) - 1 ] do
Expand All @@ -8,5 +13,11 @@ let toFlatArray array2D =
}
|> Array.ofSeq

/// <summary>
/// Generates a file path by combining an output directory and an image name.
/// </summary>
/// <param name="outputDirectory">The directory where the file will be located.</param>
/// <param name="imageName">The name of the file or image.</param>
/// <returns>A full file path obtained by combining the output directory and image name.</returns>
let generatePath outputDirectory (imageName: string) =
System.IO.Path.Combine(outputDirectory, imageName)
15 changes: 15 additions & 0 deletions src/ImageProcessing/Kernels.fs
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
module Kernels

/// <summary>
/// Represents a Gaussian blur kernel for filter applying on CPU as a 2D array
/// </summary>
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)

/// <summary>
/// Represents a edges kernel for filter applying on CPU as a 2D array
/// </summary>
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

/// <summary>
/// Represents a darken kernel for filter applying on CPU as a 2D array
/// </summary>
let darkenKernel = array2D [ [ 2 ] ] |> Array2D.map (fun x -> (float32 x) / 10.0f)

/// <summary>
/// Represents a lighten kernel for filter applying on CPU as a 2D array
/// </summary>
let lightenKernel = array2D [ [ 2 ] ] |> Array2D.map float32

/// <summary>
/// Represents a sharpen kernel for filter applying on CPU as a 2D array
/// </summary>
let sharpenKernel =
array2D [ [ 0; -1; 0 ]; [ -1; 5; -1 ]; [ 0; -1; -0 ] ] |> Array2D.map float32
12 changes: 12 additions & 0 deletions src/ImageProcessing/MyImage.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ module MyImage
open SixLabors.ImageSharp
open SixLabors.ImageSharp.PixelFormats

/// <summary>
/// Encapsulates an image, which includes both the byte pixel data and its associated attributes.
/// </summary>
type MyImage =
val Data: array<byte>
val Width: int
Expand All @@ -15,6 +18,10 @@ type MyImage =
Height = height
Name = name }

/// <summary>
/// Loads the image located at the specified file path.
/// </summary>
/// <param name="filePath">The path where the image is located. The image name and extension are required.</param>
let load (filePath: string) =

let image = Image.Load<L8> filePath
Expand All @@ -23,6 +30,11 @@ let load (filePath: string) =

MyImage(buffer, image.Width, image.Height, System.IO.Path.GetFileName filePath)

/// <summary>
/// Saves the image to the specified directory in the same extension as the input.
/// </summary>
/// <param name="image">Saved image.</param>
/// <param name="filePath">Path to the directory where the image will be saved.</param>
let save (image: MyImage) filePath =

let image = Image.LoadPixelData<L8>(image.Data, image.Width, image.Height)
Expand Down
33 changes: 33 additions & 0 deletions src/ImageProcessing/Process.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ open MyImage
open Kernels
open Brahma.FSharp

/// <summary>
/// Specifies the level of agents support.
/// </summary>
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

/// <summary>
/// Represents the available image transformations.
/// </summary>
type Transformations =
| Gauss
| Sharpen
Expand All @@ -22,10 +28,20 @@ type Transformations =
| FlipV // Vertical flip
| FlipH // Horizontal flip

/// <summary>
/// Represents processing device.
/// </summary>
type ProcessingUnits =
| CPU
| GPU of Platform

/// <summary>
/// Parses an image transformation and returns the corresponding CPU-based transformation function.
/// </summary>
/// <param name="transformation">The transformation type to apply.</param>
/// <returns>
/// A CPU-based transformation function corresponding to the specified transformation type.
/// </returns>
let transformationsParserCPU transformation =
match transformation with
| Gauss -> CPU.applyFilter gaussianBlurKernel
Expand All @@ -38,6 +54,15 @@ let transformationsParserCPU transformation =
| FlipV -> CPU.rotate true
| FlipH -> CPU.flip false

/// <summary>
/// Generates GPU kernel functions for applying various image transformations.
/// </summary>
/// <param name="clContext">The OpenCL context for GPU processing.</param>
/// <param name="localWorkSize">The local work size for GPU computation.</param>
/// <returns>
/// A function that takes a transformation type and returns the corresponding GPU
/// kernel function for applying the specified transformation.
/// </returns>
let transformationsParserGPU (clContext: ClContext) (localWorkSize: int) =

let applyFilterKernel = GPU.applyFilter clContext localWorkSize
Expand All @@ -56,6 +81,14 @@ let transformationsParserGPU (clContext: ClContext) (localWorkSize: int) =
| FlipV -> flipKernel true
| FlipH -> flipKernel false

/// <summary>
/// Processes images located at the specified input path and saves the processed images to the specified output path.
/// </summary>
/// <param name="inputPath">The path where the input images are located.</param>
/// <param name="outputPath">The path where the processed images will be saved.</param>
/// <param name="processingUnit">The GPU platform to be used for processing.</param>
/// <param name="imageEditorsList">A list of functions to be applied to the images.</param>
/// <param name="agentsSupport">Specifies the level of agent support.</param>
let processImages inputPath outputPath processingUnit imageEditorsList agentsSupport =

let listAllImages directory =
Expand Down

0 comments on commit 034de1d

Please sign in to comment.