From 85f2b90ae5940f9868a84b61f6cdcd6f6d10328b Mon Sep 17 00:00:00 2001 From: Kkkkateeee Date: Tue, 12 Aug 2025 16:49:30 +0500 Subject: [PATCH] 10 --- Benchmarks/imProcB/Program.fs | 55 +++ Benchmarks/imProcB/imProcB.fsproj | 21 ++ HomeWork.sln | 7 + .../PropertyImageProcessing.fs | 166 +++++++-- Tests/ImageProcessingT/UnitImageProcessing.fs | 152 +++++++-- src/ImageProcessing/ImageProcessing.fs | 318 ++++++++++++++++-- src/ImageProcessing/Program.fs | 197 ++++++++--- 7 files changed, 772 insertions(+), 144 deletions(-) create mode 100644 Benchmarks/imProcB/Program.fs create mode 100644 Benchmarks/imProcB/imProcB.fsproj diff --git a/Benchmarks/imProcB/Program.fs b/Benchmarks/imProcB/Program.fs new file mode 100644 index 00000000..2fdaa4db --- /dev/null +++ b/Benchmarks/imProcB/Program.fs @@ -0,0 +1,55 @@ +namespace ImageProcessingB + +open System +open BenchmarkDotNet.Attributes +open BenchmarkDotNet.Running +open BenchmarkDotNet.Configs +open BenchmarkDotNet.Reports +open Perfolizer.Horology + +open ImageProcessing.ImProcessing +open SixLabors.ImageSharp.PixelFormats + +type ipBenchmark() = + + static member Sizes = [|2; 4|] + member this.Random = System.Random() + + [] + member val MatrixSize = 0 with get, set + member val MatrixToSort = Array2D.zeroCreate 1 1 with get, set + + [] + member this.GetArrayToSort () = + this.MatrixToSort <- Array2D.init this.MatrixSize this.MatrixSize (fun _ _ -> + let r = byte (this.Random.Next 256) + let g = byte (this.Random.Next 256) + let b = byte (this.Random.Next 256) + let a = byte (this.Random.Next 256) + Rgba32 (r, g, b, a) + ) + + [] + member this.NoParallelism () = applyFilterNoParallelism gaussianBlur this.MatrixToSort + + [] + member this.PixelParallelism () = applyFilterPixelParallelism gaussianBlur this.MatrixToSort + + [] + member this.ParallelismInParts () = applyFilterParallelismInParts gaussianBlur this.MatrixToSort + + [] + member this.RowParallelism () = applyFilterRowParallelism gaussianBlur this.MatrixToSort + + [] + member this.ColParallelism () = applyFilterColParallelism gaussianBlur this.MatrixToSort + + +module Main = + [] + let main argv = + let benchmarks = + BenchmarkSwitcher [| typeof |] + + benchmarks.Run argv |> ignore + 0 \ No newline at end of file diff --git a/Benchmarks/imProcB/imProcB.fsproj b/Benchmarks/imProcB/imProcB.fsproj new file mode 100644 index 00000000..e9b982df --- /dev/null +++ b/Benchmarks/imProcB/imProcB.fsproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HomeWork.sln b/HomeWork.sln index 5e8e5d9f..9a589a16 100644 --- a/HomeWork.sln +++ b/HomeWork.sln @@ -31,6 +31,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SortingsB", "Benchmarks\SortingsB\SortingsB.fsproj", "{B9D2A2D8-E4CD-40A4-B96F-211CA90A8865}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "imProcB", "Benchmarks\imProcB\imProcB.fsproj", "{4E4C50C0-E167-4A95-BDA8-B6F5C88BCAE4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -84,6 +86,10 @@ Global {B9D2A2D8-E4CD-40A4-B96F-211CA90A8865}.Debug|Any CPU.Build.0 = Debug|Any CPU {B9D2A2D8-E4CD-40A4-B96F-211CA90A8865}.Release|Any CPU.ActiveCfg = Release|Any CPU {B9D2A2D8-E4CD-40A4-B96F-211CA90A8865}.Release|Any CPU.Build.0 = Release|Any CPU + {4E4C50C0-E167-4A95-BDA8-B6F5C88BCAE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E4C50C0-E167-4A95-BDA8-B6F5C88BCAE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E4C50C0-E167-4A95-BDA8-B6F5C88BCAE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E4C50C0-E167-4A95-BDA8-B6F5C88BCAE4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {F793D6BB-E68A-473E-B11F-1D7888DF4D4A} = {00DFC406-0A95-4C11-8BF2-4FD28C28061D} @@ -97,5 +103,6 @@ Global {C05FDC93-8EE3-442C-868A-031CE2A5F4E4} = {00DFC406-0A95-4C11-8BF2-4FD28C28061D} {C42A6017-8A61-4469-8FCD-EEBF49198D80} = {B4AC50D2-34AD-46DF-B39E-FBCC5D0653C6} {B9D2A2D8-E4CD-40A4-B96F-211CA90A8865} = {1BC2D310-CEB6-4FDE-9A59-D2D02DDA80E9} + {4E4C50C0-E167-4A95-BDA8-B6F5C88BCAE4} = {1BC2D310-CEB6-4FDE-9A59-D2D02DDA80E9} EndGlobalSection EndGlobal diff --git a/Tests/ImageProcessingT/PropertyImageProcessing.fs b/Tests/ImageProcessingT/PropertyImageProcessing.fs index 32d65918..70a6c24b 100644 --- a/Tests/ImageProcessingT/PropertyImageProcessing.fs +++ b/Tests/ImageProcessingT/PropertyImageProcessing.fs @@ -18,12 +18,13 @@ module ImageGen = let a = byte 255 return Rgba32(r, g, b, a) } - let array2DGen (size: int) : Gen = + let array2DGen (size: int) : Gen> = gen { let! pixels = Gen.array2DOfDim (size, size) pixelGen - return pixels + return async { return pixels } } - + + type Image100() = static member Rgba32() = Arb.fromGen (ImageGen.array2DGen 100) @@ -37,40 +38,149 @@ type Image2() = type Filter() = [ |])>] - member _.filterDoesntChangeSize (image: Rgba32[,]) = - let imageAfterFilter = applyFilter gaussianBlur image - Assert.Equal(100, imageAfterFilter.GetLength(0)) - Assert.Equal(100, imageAfterFilter.GetLength(1)) + member _.filterDoesntChangeSize (image: Async) = + let resNoParallelism = + applyFilterNoParallelismA gaussianBlur image + |> Async.RunSynchronously + + let resPixelParallelism = + applyFilterPixelParallelismA gaussianBlur image + |> Async.RunSynchronously + + let resPartsParallelism = + applyFilterParallelismInPartsA gaussianBlur image + |> Async.RunSynchronously + + let resRowParallelism = + applyFilterRowParallelismA gaussianBlur image + |> Async.RunSynchronously + + let resColParallelism = + applyFilterColParallelismA gaussianBlur image + |> Async.RunSynchronously + + Assert.Equal(100, resNoParallelism.GetLength 0) + Assert.Equal(100, resNoParallelism.GetLength 1) + + Assert.Equal(100, resPixelParallelism.GetLength 0) + Assert.Equal(100, resPixelParallelism.GetLength 1) + + Assert.Equal(100, resPartsParallelism.GetLength 0) + Assert.Equal(100, resPartsParallelism.GetLength 1) + + Assert.Equal(100, resRowParallelism.GetLength 0) + Assert.Equal(100, resRowParallelism.GetLength 1) + + Assert.Equal(100, resColParallelism.GetLength 0) + Assert.Equal(100, resColParallelism.GetLength 1) [ |])>] - member _.idDoesntChangeData (image: Rgba32[,]) = - let imageAfterFilter = applyFilter id image - Assert.Equal(image, imageAfterFilter) + member _.idDoesntChangeData (image: Async) = + let resNoParallelism = applyFilterNoParallelismA id image + let resPixelParallelism = applyFilterPixelParallelismA id image + let resPartsParallelism = applyFilterParallelismInPartsA id image + let resRowParallelism = applyFilterRowParallelismA id image + let resColParallelism = applyFilterColParallelismA id image + + Assert.Equal(image |> Async.RunSynchronously, resNoParallelism |> Async.RunSynchronously) + Assert.Equal(image |> Async.RunSynchronously, resPixelParallelism |> Async.RunSynchronously) + Assert.Equal(image |> Async.RunSynchronously, resPartsParallelism |> Async.RunSynchronously) + Assert.Equal(image |> Async.RunSynchronously, resRowParallelism |> Async.RunSynchronously) + Assert.Equal(image |> Async.RunSynchronously, resColParallelism |> Async.RunSynchronously) [ |])>] - member _.imageSmallerThanFilter (image: Rgba32[,]) = - let imageAfterFilter = applyFilter black image - Assert.Equal(true, imageIsBlack imageAfterFilter) + member _.imageSmallerThanFilter (image: Async) = + let resNoParallelism = applyFilterNoParallelismA black image + let resPixelParallelism = applyFilterPixelParallelismA black image + let resPartsParallelism = applyFilterParallelismInPartsA black image + let resRowParallelism = applyFilterRowParallelismA black image + let resColParallelism = applyFilterColParallelismA black image + + Assert.Equal(true, imageIsBlack resNoParallelism) + Assert.Equal(true, imageIsBlack resPixelParallelism) + Assert.Equal(true, imageIsBlack resPartsParallelism) + Assert.Equal(true, imageIsBlack resRowParallelism) + Assert.Equal(true, imageIsBlack resColParallelism) [ |])>] - member _.blackFilter (image: Rgba32[,]) = - let imageBlack = applyFilter black image - Assert.Equal(true, imageIsBlack imageBlack) + member _.blackFilter (image: Async) = + let imageBlack_NoParallelism = applyFilterNoParallelismA black image + let imageBlack_PixelParallelism = applyFilterPixelParallelismA black image + let imageBlack_PartsParallelism = applyFilterParallelismInPartsA black image + let imageBlac_RowParallelism = applyFilterRowParallelismA black image + let imageBlack_ColParallelism = applyFilterColParallelismA black image + + Assert.Equal(true, imageIsBlack imageBlack_NoParallelism) + Assert.Equal(true, imageIsBlack imageBlack_PixelParallelism) + Assert.Equal(true, imageIsBlack imageBlack_PartsParallelism) + Assert.Equal(true, imageIsBlack imageBlac_RowParallelism) + Assert.Equal(true, imageIsBlack imageBlack_ColParallelism) [ |])>] - member _.shiftComposition (image: Rgba32[,]) = - let trivial1and2 = applyFilter shiftRight image |> applyFilter shiftDown - let trivial12 = applyFilter shiftDiagonal image - Assert.Equal(trivial1and2, trivial12) + member _.shiftComposition (image: Async) = + let trivial1and2_NoParallelism = applyFilterNoParallelismA shiftRight image |> applyFilterNoParallelismA shiftDown + let trivial12_NoParallelism = applyFilterNoParallelismA shiftDiagonal image + + let trivial1and2_PixelParallelism = applyFilterPixelParallelismA shiftRight image |> applyFilterPixelParallelismA shiftDown + let trivial12_PixelParallelism = applyFilterPixelParallelismA shiftDiagonal image + + let trivial1and2_PartsParallelism = applyFilterParallelismInPartsA shiftRight image |> applyFilterParallelismInPartsA shiftDown + let trivial12_PartsParallelism = applyFilterParallelismInPartsA shiftDiagonal image + + let trivial1and2_RowParallelism = applyFilterRowParallelismA shiftRight image |> applyFilterRowParallelismA shiftDown + let trivial12_RowParallelism = applyFilterRowParallelismA shiftDiagonal image + + let trivial1and2_ColParallelism = applyFilterColParallelismA shiftRight image |> applyFilterColParallelismA shiftDown + let trivial12_ColParallelism = applyFilterColParallelismA shiftDiagonal image + + Assert.Equal(trivial1and2_NoParallelism |> Async.RunSynchronously, trivial12_NoParallelism |> Async.RunSynchronously) + Assert.Equal(trivial1and2_PixelParallelism |> Async.RunSynchronously, trivial12_PixelParallelism |> Async.RunSynchronously) + Assert.Equal(trivial1and2_PartsParallelism |> Async.RunSynchronously, trivial12_PartsParallelism |> Async.RunSynchronously) + Assert.Equal(trivial1and2_RowParallelism |> Async.RunSynchronously, trivial12_RowParallelism |> Async.RunSynchronously) + Assert.Equal(trivial1and2_ColParallelism |> Async.RunSynchronously, trivial12_ColParallelism |> Async.RunSynchronously) [ |])>] - member _.extendedComposition (image: Rgba32[,]) = - let imageAfterFilter = applyFilter kernel image - let imageAfterExtendedFilter = applyFilter kernelExtended image - Assert.Equal(imageAfterFilter, imageAfterExtendedFilter) + member _.extendedComposition (image: Async) = + let imageAfterFilter_NoParallelism = applyFilterNoParallelismA kernel image + let imageAfterExtendedFilter_NoParallelism = applyFilterNoParallelismA kernelExtended image + + let imageAfterFilter_PixelParallelism = applyFilterPixelParallelismA kernel image + let imageAfterExtendedFilter_PixelParallelism = applyFilterPixelParallelismA kernelExtended image + + let imageAfterFilter_PartsParallelism = applyFilterParallelismInPartsA kernel image + let imageAfterExtendedFilter_PartsParallelism = applyFilterParallelismInPartsA kernelExtended image + + let imageAfterFilter_RowParallelism = applyFilterRowParallelismA kernel image + let imageAfterExtendedFilter_RowParallelism = applyFilterRowParallelismA kernelExtended image + + let imageAfterFilter_ColParallelism = applyFilterColParallelismA kernel image + let imageAfterExtendedFilter_ColParallelism = applyFilterColParallelismA kernelExtended image + + Assert.Equal(imageAfterFilter_NoParallelism |> Async.RunSynchronously, imageAfterExtendedFilter_NoParallelism |> Async.RunSynchronously) + Assert.Equal(imageAfterFilter_PixelParallelism |> Async.RunSynchronously, imageAfterExtendedFilter_PixelParallelism |> Async.RunSynchronously) + Assert.Equal(imageAfterFilter_PartsParallelism |> Async.RunSynchronously, imageAfterExtendedFilter_PartsParallelism |> Async.RunSynchronously) + Assert.Equal(imageAfterFilter_RowParallelism |> Async.RunSynchronously, imageAfterExtendedFilter_RowParallelism |> Async.RunSynchronously) + Assert.Equal(imageAfterFilter_ColParallelism |> Async.RunSynchronously, imageAfterExtendedFilter_ColParallelism |> Async.RunSynchronously) [ |])>] - member _.someAreCommutative (image: Rgba32[,]) = - let image12 = applyFilter shiftRight image |> applyFilter shiftDown - let image21 = applyFilter shiftDown image |> applyFilter shiftRight - Assert.Equal(image12, image21) \ No newline at end of file + member _.someAreCommutative (image: Async) = + let image12_NoParallelism = applyFilterNoParallelismA shiftRight image |> applyFilterNoParallelismA shiftDown + let image21_NoParallelism = applyFilterNoParallelismA shiftDown image |> applyFilterNoParallelismA shiftRight + + let image12_PixelParallelism = applyFilterPixelParallelismA shiftRight image |> applyFilterPixelParallelismA shiftDown + let image21_PixelParallelism = applyFilterPixelParallelismA shiftDown image |> applyFilterPixelParallelismA shiftRight + + let image12_PartsParallelism = applyFilterParallelismInPartsA shiftRight image |> applyFilterParallelismInPartsA shiftDown + let image21_PartsParallelism = applyFilterParallelismInPartsA shiftDown image |> applyFilterParallelismInPartsA shiftRight + + let image12_RowParallelism = applyFilterRowParallelismA shiftRight image |> applyFilterRowParallelismA shiftDown + let image21_RowParallelism = applyFilterRowParallelismA shiftDown image |> applyFilterRowParallelismA shiftRight + + let image12_ColParallelism = applyFilterColParallelismA shiftRight image |> applyFilterColParallelismA shiftDown + let image21_ColParallelism = applyFilterColParallelismA shiftDown image |> applyFilterColParallelismA shiftRight + + Assert.Equal(image12_NoParallelism |> Async.RunSynchronously, image21_NoParallelism |> Async.RunSynchronously) + Assert.Equal(image12_PixelParallelism |> Async.RunSynchronously, image21_PixelParallelism |> Async.RunSynchronously) + Assert.Equal(image12_PartsParallelism |> Async.RunSynchronously, image21_PartsParallelism |> Async.RunSynchronously) + Assert.Equal(image12_RowParallelism |> Async.RunSynchronously, image21_RowParallelism |> Async.RunSynchronously) + Assert.Equal(image12_ColParallelism |> Async.RunSynchronously, image21_ColParallelism |> Async.RunSynchronously) \ No newline at end of file diff --git a/Tests/ImageProcessingT/UnitImageProcessing.fs b/Tests/ImageProcessingT/UnitImageProcessing.fs index 73880dc0..3ac54427 100644 --- a/Tests/ImageProcessingT/UnitImageProcessing.fs +++ b/Tests/ImageProcessingT/UnitImageProcessing.fs @@ -10,8 +10,8 @@ module Data = let im1 = "../../../Images/image1.png" let imSmall = "../../../Images/image2x2px.png" - let image1 = loadAsRgba2D im1 - let imageSmall = loadAsRgba2D imSmall + let image1 = loadAsRgba2DA im1 + let imageSmall = loadAsRgba2DA imSmall let imId = "../../../Images/id.png" let imShiftRightDown = "../../../Images/shiftRightDown.png" @@ -19,12 +19,12 @@ module Data = let imShiftDiagonal = "../../../Images/shiftDiagonal.png" let imKernel = "../../../Images/kernel.png" let imKernelExtended = "../../../Images/kernelExtended.png" - let imageId= loadAsRgba2D imId - let imageShiftRightDown= loadAsRgba2D imShiftRightDown - let imageShiftDownRight= loadAsRgba2D imShiftDownRight - let imageShiftDiagonal= loadAsRgba2D imShiftDiagonal - let imageKernel= loadAsRgba2D imKernel - let imageKernelExtended= loadAsRgba2D imKernelExtended + let imageId= loadAsRgba2DA imId |> Async.RunSynchronously + let imageShiftRightDown= loadAsRgba2DA imShiftRightDown |> Async.RunSynchronously + let imageShiftDownRight= loadAsRgba2DA imShiftDownRight |> Async.RunSynchronously + let imageShiftDiagonal= loadAsRgba2DA imShiftDiagonal |> Async.RunSynchronously + let imageKernel= loadAsRgba2DA imKernel |> Async.RunSynchronously + let imageKernelExtended= loadAsRgba2DA imKernelExtended |> Async.RunSynchronously let id = @@ -86,19 +86,23 @@ module Data = |> Array.map (Array.map float32) - let imageIsBlack (image: Rgba32[,]) = - let h = image.GetLength 0 - let w = image.GetLength 1 + let imageIsBlack (imageAsync: Async) = + async { + let! image = imageAsync + let h = image.GetLength 0 + let w = image.GetLength 1 - let mutable isBlack = true + let mutable isBlack = true - for i in 0 .. h - 1 do - for j in 0 .. w - 1 do - let pixel = image.[i, j] - if pixel <> Rgba32(byte 0, byte 0, byte 0, byte 255) then - isBlack <- false + for i in 0 .. h - 1 do + for j in 0 .. w - 1 do + let pixel = image.[i, j] + if pixel <> Rgba32(byte 0, byte 0, byte 0, byte 255) then + isBlack <- false + + return isBlack + } |> Async.RunSynchronously - isBlack module Filter = @@ -106,29 +110,113 @@ module Filter = [] let idDoesntChangeData () = - let imageAfterFilter = applyFilter id image1 - Assert.Equal(imageId, imageAfterFilter) + let resNoParallelism = applyFilterNoParallelismA id image1 + let resPixelParallelism = applyFilterPixelParallelismA id image1 + let resPartsParallelism = applyFilterParallelismInPartsA id image1 + let resRowParallelism = applyFilterRowParallelismA id image1 + let resColParallelism = applyFilterColParallelismA id image1 + + Assert.Equal(imageId, resNoParallelism |> Async.RunSynchronously) + Assert.Equal(imageId, resPixelParallelism |> Async.RunSynchronously) + Assert.Equal(imageId, resPartsParallelism |> Async.RunSynchronously) + Assert.Equal(imageId, resRowParallelism |> Async.RunSynchronously) + Assert.Equal(imageId, resColParallelism |> Async.RunSynchronously) [] let shiftComposition () = - let shiftRightDown = applyFilter shiftRight image1 |> applyFilter shiftDown - let shiftDiagonal = applyFilter shiftDiagonal image1 + let shiftRightDown_NoParallelism = applyFilterNoParallelismA shiftRight image1 |> applyFilterNoParallelismA shiftDown + let shiftDiagonal_NoParallelism = applyFilterNoParallelismA shiftDiagonal image1 + + let shiftRightDown_PixelParallelism = applyFilterPixelParallelismA shiftRight image1 |> applyFilterPixelParallelismA shiftDown + let shiftDiagonal_PixelParallelism = applyFilterPixelParallelismA shiftDiagonal image1 + + let shiftRightDown_PartsParallelism = applyFilterParallelismInPartsA shiftRight image1 |> applyFilterParallelismInPartsA shiftDown + let shiftDiagonal_PartsParallelism = applyFilterParallelismInPartsA shiftDiagonal image1 + + let shiftRightDown_RowParallelism = applyFilterRowParallelismA shiftRight image1 |> applyFilterRowParallelismA shiftDown + let shiftDiagonal_RowParallelism = applyFilterRowParallelismA shiftDiagonal image1 + + let shiftRightDown_ColParallelism = applyFilterColParallelismA shiftRight image1 |> applyFilterColParallelismA shiftDown + let shiftDiagonal_ColParallelism = applyFilterColParallelismA shiftDiagonal image1 + Assert.Equal(imageShiftRightDown, imageShiftDiagonal) - Assert.Equal(imageShiftRightDown, shiftRightDown) - Assert.Equal(imageShiftRightDown, shiftDiagonal) + Assert.Equal(imageShiftRightDown, shiftRightDown_NoParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftRightDown, shiftDiagonal_NoParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftRightDown, shiftRightDown_PixelParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftRightDown, shiftDiagonal_PixelParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftRightDown, shiftRightDown_PartsParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftRightDown, shiftDiagonal_PartsParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftRightDown, shiftRightDown_RowParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftRightDown, shiftDiagonal_RowParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftRightDown, shiftRightDown_ColParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftRightDown, shiftDiagonal_ColParallelism |> Async.RunSynchronously) [] let extendedComposition () = - let kernel = applyFilter kernel image1 - let kernelExtended = applyFilter kernelExtended image1 + let kernel_NoParallelism = applyFilterNoParallelismA kernel image1 + let kernelExtended_NoParallelism = applyFilterNoParallelismA kernelExtended image1 + + let kernel_PixelParallelism = applyFilterPixelParallelismA kernel image1 + let kernelExtended_PixelParallelism = applyFilterPixelParallelismA kernelExtended image1 + + let kernel_PartsParallelism = applyFilterParallelismInPartsA kernel image1 + let kernelExtended_PartsParallelism = applyFilterParallelismInPartsA kernelExtended image1 + + let kernel_RowParallelism = applyFilterRowParallelismA kernel image1 + let kernelExtended_RowParallelism = applyFilterRowParallelismA kernelExtended image1 + + let kernel_ColParallelism = applyFilterColParallelismA kernel image1 + let kernelExtended_ColParallelism = applyFilterColParallelismA kernelExtended image1 + Assert.Equal(imageKernel, imageKernelExtended) - Assert.Equal(imageKernel, kernel) - Assert.Equal(imageKernel, kernelExtended) + Assert.Equal(imageKernel, kernel_NoParallelism |> Async.RunSynchronously) + Assert.Equal(imageKernel, kernelExtended_NoParallelism |> Async.RunSynchronously) + + Assert.Equal(imageKernel, kernel_PixelParallelism |> Async.RunSynchronously) + Assert.Equal(imageKernel, kernelExtended_PixelParallelism |> Async.RunSynchronously) + + Assert.Equal(imageKernel, kernel_PartsParallelism |> Async.RunSynchronously) + Assert.Equal(imageKernel, kernelExtended_PartsParallelism |> Async.RunSynchronously) + + Assert.Equal(imageKernel, kernel_RowParallelism |> Async.RunSynchronously) + Assert.Equal(imageKernel, kernelExtended_RowParallelism |> Async.RunSynchronously) + + Assert.Equal(imageKernel, kernel_ColParallelism |> Async.RunSynchronously) + Assert.Equal(imageKernel, kernelExtended_ColParallelism |> Async.RunSynchronously) [] let someAreCommutative () = - let imageRD = applyFilter shiftRight image1 |> applyFilter shiftDown - let imageDR = applyFilter shiftDown image1 |> applyFilter shiftRight + let imageRD_NoParallelism = applyFilterNoParallelismA shiftRight image1 |> applyFilterNoParallelismA shiftDown + let imageDR_NoParallelism = applyFilterNoParallelismA shiftDown image1 |> applyFilterNoParallelismA shiftRight + + let imageRD_PixelParallelism = applyFilterPixelParallelismA shiftRight image1 |> applyFilterPixelParallelismA shiftDown + let imageDR_PixelParallelism = applyFilterPixelParallelismA shiftDown image1 |> applyFilterPixelParallelismA shiftRight + + let imageRD_PartsParallelism = applyFilterParallelismInPartsA shiftRight image1 |> applyFilterParallelismInPartsA shiftDown + let imageDR_PartsParallelism = applyFilterParallelismInPartsA shiftDown image1 |> applyFilterParallelismInPartsA shiftRight + + let imageRD_RowParallelism = applyFilterRowParallelismA shiftRight image1 |> applyFilterRowParallelismA shiftDown + let imageDR_RowParallelism = applyFilterRowParallelismA shiftDown image1 |> applyFilterRowParallelismA shiftRight + + let imageRD_ColParallelism = applyFilterColParallelismA shiftRight image1 |> applyFilterColParallelismA shiftDown + let imageDR_ColParallelism = applyFilterColParallelismA shiftDown image1 |> applyFilterColParallelismA shiftRight + Assert.Equal(imageShiftDownRight, imageShiftRightDown) - Assert.Equal(imageShiftDownRight, imageDR) - Assert.Equal(imageShiftDownRight, imageRD) \ No newline at end of file + Assert.Equal(imageShiftDownRight, imageDR_NoParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftDownRight, imageRD_NoParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftDownRight, imageDR_PixelParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftDownRight, imageRD_PixelParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftDownRight, imageDR_PartsParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftDownRight, imageRD_PartsParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftDownRight, imageDR_RowParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftDownRight, imageRD_RowParallelism |> Async.RunSynchronously) + + Assert.Equal(imageShiftDownRight, imageDR_ColParallelism |> Async.RunSynchronously) + Assert.Equal(imageShiftDownRight, imageRD_ColParallelism |> Async.RunSynchronously) \ No newline at end of file diff --git a/src/ImageProcessing/ImageProcessing.fs b/src/ImageProcessing/ImageProcessing.fs index 653a8620..04f686ab 100644 --- a/src/ImageProcessing/ImageProcessing.fs +++ b/src/ImageProcessing/ImageProcessing.fs @@ -2,6 +2,9 @@ namespace ImageProcessing open SixLabors.ImageSharp open SixLabors.ImageSharp.PixelFormats +open System.Threading.Tasks +open Microsoft.FSharp.Control +open System.IO [] type Image = @@ -183,6 +186,193 @@ module ImProcessing = |> Array.map (Array.map (fun x -> (float32 x) / + 128.0f)) + let loadAsRgba2DA (file: string) = + async { + use fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize = 4096, useAsync = true) + + let! img = Image.LoadAsync fileStream |> Async.AwaitTask + let res = Array2D.zeroCreate img.Height img.Width + + for i in 0 .. img.Width - 1 do + for j in 0 .. img.Height - 1 do + res.[j, i] <- img.[i, j] + + return res + } + + let saveRgbaImageA (rgbaDataAsync: Async) file = + async { + let! rgbaData = rgbaDataAsync + + let h = rgbaData.GetLength 0 + let w = rgbaData.GetLength 1 + + use img = new Image(w, h) + + for x in 0 .. h - 1 do + for y in 0 .. w - 1 do + img.[y, x] <- rgbaData.[x, y] + + let! _ = img.SaveAsync file |> Async.AwaitTask + return () + } + + let processPixel px py (filter: float32 array) filterD (img: Rgba32[,]) imgH imgW = + let dataToHandle = [| + for i in px - filterD .. px + filterD do + for j in py - filterD .. py + filterD do + if i < 0 || i >= imgH || j < 0 || j >= imgW then + 0.0f, 0.0f, 0.0f + else + let pixel = img.[i, j] + float32 pixel.R, float32 pixel.G, float32 pixel.B + |] + + let mutable rSum = 0.0f + let mutable gSum = 0.0f + let mutable bSum = 0.0f + + Array.iteri (fun index (r, g, b) -> + rSum <- rSum + r * filter.[index] + gSum <- gSum + g * filter.[index] + bSum <- bSum + b * filter.[index] + ) dataToHandle + + byte rSum, byte gSum, byte bSum // Async<[,]> + + let applyFilterNoParallelismA (filter: float32[][]) (imgAsync: Async) = + async { + let! img = imgAsync + + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let res = + Array2D.mapi (fun x y _ -> + let r, g, b = processPixel x y filter filterD img imgH imgW + Rgba32(r, g, b, img.[x, y].A) + ) img + return res + } + + let applyFilterPixelParallelismA (filter: float32[][]) (imgAsync: Async) = + async { + let! img = imgAsync + + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For(0, imgH * imgW, fun k -> + let i = k / imgW + let j = k % imgW + + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + ) |> ignore + + return res + } + + let applyFilterParallelismInPartsA (filter: float32[][]) (imgAsync: Async) = + async { + let! img = imgAsync + + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let halfImgH = imgH / 2 + let halfImgW = imgW / 2 + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For (0, 4, fun k -> + if k = 0 then + for i in 0 .. halfImgH - 1 do + for j in 0 .. halfImgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + elif k = 1 then + for i in halfImgH .. imgH - 1 do + for j in 0 .. halfImgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + elif k = 2 then + for i in 0 .. halfImgH - 1 do + for j in halfImgW .. imgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + else + for i in halfImgH .. imgH - 1 do + for j in halfImgW .. imgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + ) |> ignore + + return res + } + + let applyFilterRowParallelismA (filter: float32[][]) (imgAsync: Async) = + async { + let! img = imgAsync + + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For (0, imgH, fun k -> + for i in 0 .. imgW - 1 do + let r, g, b = processPixel k i filter filterD img imgH imgW + res.[k, i] <- Rgba32(r, g, b, img.[k, i].A) + ) |> ignore + + return res + } + + let applyFilterColParallelismA (filter: float32[][]) (imgAsync: Async) = + async { + let! img = imgAsync + + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For (0, imgW, fun k -> + for i in 0 .. imgH - 1 do + let r, g, b = processPixel i k filter filterD img imgH imgW + res.[i, k] <- Rgba32(r, g, b, img.[i, k].A) + ) |> ignore + + return res + } + + + +// СИНХРОННАЯ ВЕРСИЯ ФУНКЦИЙ ВЫШЕ + + let loadAsRgba2D (file: string) = let img = Image.Load file let res = Array2D.zeroCreate img.Height img.Width @@ -190,12 +380,11 @@ module ImProcessing = for i in 0 .. img.Width - 1 do for j in 0 .. img.Height - 1 do res.[j, i] <- img.[i, j] - res let saveRgbaImage (rgbaData: Rgba32[,]) file = - let h = rgbaData.GetLength(0) - let w = rgbaData.GetLength(1) + let h = rgbaData.GetLength 0 + let w = rgbaData.GetLength 1 use img = new Image(w, h) @@ -203,39 +392,106 @@ module ImProcessing = for y in 0 .. w - 1 do img.[y, x] <- rgbaData.[x, y] - img.Save(file) + img.Save file - let applyFilter (filter: float32[][]) (img: Rgba32[,]) = + let applyFilterNoParallelism (filter: float32[][]) (img: Rgba32[,]) = let imgH = img.GetLength 0 let imgW = img.GetLength 1 - let filterD = (Array.length filter) / 2 + let filterD = Array.length filter / 2 let filter = Array.concat filter - let processPixel px py = - let dataToHandle = [| - for i in px - filterD .. px + filterD do - for j in py - filterD .. py + filterD do - if i < 0 || i >= imgH || j < 0 || j >= imgW then - (0.0f, 0.0f, 0.0f) - else - let pixel = img.[i, j] - (float32 pixel.R, float32 pixel.G, float32 pixel.B) - |] - - let mutable rSum = 0.0f - let mutable gSum = 0.0f - let mutable bSum = 0.0f - - Array.iteri (fun index (r, g, b) -> - rSum <- rSum + r * filter.[index] - gSum <- gSum + g * filter.[index] - bSum <- bSum + b * filter.[index] - ) dataToHandle - - (byte rSum, byte gSum, byte bSum) - Array2D.mapi (fun x y _ -> - let (r, g, b) = processPixel x y + let r, g, b = processPixel x y filter filterD img imgH imgW Rgba32(r, g, b, img.[x, y].A) - ) img \ No newline at end of file + ) img + + let applyFilterPixelParallelism (filter: float32[][]) (img: Rgba32[,]) = + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For(0, imgH * imgW, fun k -> + let i = k / imgW + let j = k % imgW + + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + ) |> ignore + res + + let applyFilterParallelismInParts (filter: float32[][]) (img: Rgba32[,]) = + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let halfImgH = imgH / 2 + let halfImgW = imgW / 2 + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For (0, 4, fun k -> + if k = 0 then + for i in 0 .. halfImgH - 1 do + for j in 0 .. halfImgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + elif k = 1 then + for i in halfImgH .. imgH - 1 do + for j in 0 .. halfImgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + elif k = 2 then + for i in 0 .. halfImgH - 1 do + for j in halfImgW .. imgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + + else + for i in halfImgH .. imgH - 1 do + for j in halfImgW .. imgW - 1 do + let r, g, b = processPixel i j filter filterD img imgH imgW + res.[i, j] <- Rgba32(r, g, b, img.[i, j].A) + ) |> ignore + res + + let applyFilterRowParallelism (filter: float32[][]) (img: Rgba32[,]) = + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For (0, imgH, fun k -> + for i in 0 .. imgW - 1 do + let r, g, b = processPixel k i filter filterD img imgH imgW + res.[k, i] <- Rgba32(r, g, b, img.[k, i].A) + ) |> ignore + res + + let applyFilterColParallelism (filter: float32[][]) (img: Rgba32[,]) = + let imgH = img.GetLength 0 + let imgW = img.GetLength 1 + + let filterD = Array.length filter / 2 + let filter = Array.concat filter + + let res = Array2D.create imgH imgW (Rgba32(0.0f, 0.0f, 0.0f, 0.0f)) + + Parallel.For (0, imgW, fun k -> + for i in 0 .. imgH - 1 do + let r, g, b = processPixel i k filter filterD img imgH imgW + res.[i, k] <- Rgba32(r, g, b, img.[i, k].A) + ) |> ignore + res \ No newline at end of file diff --git a/src/ImageProcessing/Program.fs b/src/ImageProcessing/Program.fs index f78667ad..e191ff74 100644 --- a/src/ImageProcessing/Program.fs +++ b/src/ImageProcessing/Program.fs @@ -1,72 +1,163 @@ -open ImageProcessing.ImProcessing +open System +open System.Threading.Tasks +open System.IO +open ImageProcessing.ImProcessing open Argu - +open SixLabors.ImageSharp.PixelFormats type Filters = - | gaussianBlur = 1 - | motionDiagonal135deg = 2 - | motionDiagonal315deg = 3 - | motionVertical = 4 - | motionHorizontal = 5 - | edgesHorizontal = 6 - | edgesVertical = 7 - | edgesDioganal135deg = 8 - | edgesDioganal315deg = 9 - | edgesAllDirections = 10 - | sharpen = 11 - | sharpenSoft = 12 - | sharpenWithEdges = 13 - | emboss = 14 - | embossHard = 15 + | GaussianBlur + | MotionDiagonal135deg + | MotionDiagonal315deg + | MotionVertical + | MotionHorizontal + | EdgesHorizontal + | EdgesVertical + | EdgesDioganal135deg + | EdgesDioganal315deg + | EdgesAllDirections + | Sharpen + | SharpenSoft + | SharpenWithEdges + | Emboss + | EmbossHard + + +type ParallelismTypes = + | NoParallelism + | PixelParallelism + | ParallelismInParts + | RowParallelism + | ColParallelism + type Arguments = - | [] Input_File of string - | [] Out_File of string + | [] Input_Folder of string + | [] Out_Folder of string | [] Filters of list + | [] Parallelism of list interface IArgParserTemplate with member this.Usage = match this with - | Input_File _ -> "File to process." - | Out_File _ -> "Where to save result." + | Input_Folder _ -> "Folder with images for processing." + | Out_Folder _ -> "Folder for processed images." | Filters _ -> "Which filters to apply (comma-separated)." + | Parallelism _ -> "Type of parallelism." + + +type FileProcessorMessage = + | ProcessImage of string * string * list * list * AsyncReplyChannel> + | Stop + + +let createFileProcessor () = + let getParallelismType (parallelism: list) (filter: float32[][]) (img: Async) = + match parallelism.[0] with + | NoParallelism -> applyFilterColParallelismA filter img + | PixelParallelism -> applyFilterPixelParallelismA filter img + | ParallelismInParts -> applyFilterParallelismInPartsA filter img + | RowParallelism -> applyFilterRowParallelismA filter img + | ColParallelism -> applyFilterColParallelismA filter img + + MailboxProcessor.Start(fun inbox -> + + let rec loop () = + async { + let! msg = inbox.Receive() + + match msg with + | ProcessImage (sourcePath, destinationPath, filters, parallelism, replyChannel) -> + try + let imageData = loadAsRgba2DA sourcePath + let! filteredImage = + filters |> List.fold (fun img filter -> + match filter with + | GaussianBlur -> getParallelismType parallelism gaussianBlur img + | MotionDiagonal135deg -> getParallelismType parallelism motionDiagonal135deg img + | MotionDiagonal315deg -> getParallelismType parallelism motionDiagonal315deg img + | MotionVertical -> getParallelismType parallelism motionVertical img + | MotionHorizontal -> getParallelismType parallelism motionHorizontal img + | EdgesHorizontal -> getParallelismType parallelism edgesHorizontal img + | EdgesVertical -> getParallelismType parallelism edgesVertical img + | EdgesDioganal135deg -> getParallelismType parallelism edgesDioganal135deg img + | EdgesDioganal315deg -> getParallelismType parallelism edgesDioganal315deg img + | EdgesAllDirections -> getParallelismType parallelism edgesAllDirections img + | Sharpen -> getParallelismType parallelism sharpen img + | SharpenSoft -> getParallelismType parallelism sharpenSoft img + | SharpenWithEdges -> getParallelismType parallelism sharpenWithEdges img + | Emboss -> getParallelismType parallelism emboss img + | EmbossHard -> getParallelismType parallelism embossHard img + + ) imageData + + do! saveRgbaImageA (async.Return filteredImage) destinationPath + replyChannel.Reply(Ok ()) + return! loop () + with + | ex -> + replyChannel.Reply(Error ex.Message) + return! loop () + + | Stop -> + return () + } + loop () + ) [] let main argv = - + + let fileProcessor = createFileProcessor () + + let processImageAsync (sourcePath: string) (destinationPath: string) (filters: list) (parallelism: list) = + async { + let! reply = fileProcessor.PostAndAsyncReply (fun replyChannel -> ProcessImage(sourcePath, destinationPath, filters, parallelism, replyChannel)) + return reply + } + let parser = ArgumentParser.Create(programName = "ImageProcessing") let results = parser.Parse argv - let inFile = results.GetResult Input_File - let outFile = results.GetResult Out_File + let inFolder = results.GetResult Input_Folder + let outFolder = results.GetResult Out_Folder let filters = results.GetResult Filters + let parallelisms = results.GetResult Parallelism + + + if not (Directory.Exists inFolder) then + printfn $"Error: Input folder '{inFolder}' does not exist." + 1 + else + let imageFiles = + try + Directory.GetFiles(inFolder, "*.*") + |> Array.filter (fun file -> + let extension = Path.GetExtension file + extension.ToLower() = ".jpg" || extension.ToLower() = ".jpeg" || extension.ToLower() = ".png" || extension.ToLower() = ".bmp") + |> Array.toList + with ex -> + printfn $"Error getting image files from '{inFolder}': {ex.Message}" + [] + + if imageFiles.IsEmpty then + printfn $"Error: No images found in input folder '{inFolder}'." + 1 + else + Directory.CreateDirectory outFolder |> ignore + + let processingTasks = + imageFiles + |> List.map (fun sourcePath -> + let filename = Path.GetFileName sourcePath + let destinationPath = Path.Combine(outFolder, filename) + processImageAsync sourcePath destinationPath filters parallelisms + ) + + Async.AwaitTask (Task.WhenAll(processingTasks |> List.map Async.StartAsTask |> List.toArray)) |> Async.RunSynchronously |> ignore + + fileProcessor.Post Stop + + printfn "Processing complete." - let inImage = loadAsRgba2D inFile - - let resultImage = - filters |> List.fold (fun img filter -> - match filter with - | Filters.gaussianBlur -> applyFilter gaussianBlur img - | Filters.motionDiagonal135deg -> applyFilter motionDiagonal135deg img - | Filters.motionDiagonal315deg -> applyFilter motionDiagonal315deg img - | Filters.motionVertical -> applyFilter motionVertical img - | Filters.motionHorizontal -> applyFilter motionHorizontal img - | Filters.edgesHorizontal -> applyFilter edgesHorizontal img - | Filters.edgesVertical -> applyFilter edgesVertical img - | Filters.edgesDioganal135deg -> applyFilter edgesDioganal135deg img - | Filters.edgesDioganal315deg -> applyFilter edgesDioganal315deg img - | Filters.edgesAllDirections -> applyFilter edgesAllDirections img - | Filters.sharpen -> applyFilter sharpen img - | Filters.sharpenSoft -> applyFilter sharpenSoft img - | Filters.sharpenWithEdges -> applyFilter sharpenWithEdges img - | Filters.emboss -> applyFilter emboss img - | Filters.embossHard -> applyFilter embossHard img - | _ -> - printfn "Unknown filter" - img - - ) inImage - - saveRgbaImage resultImage outFile - - 0 \ No newline at end of file + 0 \ No newline at end of file