{"id":16099290,"url":"https://github.com/kagof/julia-image-processing","last_synced_at":"2026-05-18T09:33:18.669Z","repository":{"id":199366731,"uuid":"702726116","full_name":"kagof/julia-image-processing","owner":"kagof","description":"Image processing programs written in Julia","archived":false,"fork":false,"pushed_at":"2023-10-11T22:08:39.000Z","size":4085,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-12T05:13:31.680Z","etag":null,"topics":["cuda","image-processing","julia"],"latest_commit_sha":null,"homepage":"","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kagof.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-10-09T22:16:20.000Z","updated_at":"2024-04-23T23:04:37.000Z","dependencies_parsed_at":"2023-11-21T07:03:40.803Z","dependency_job_id":"9ef5e64b-ff62-4844-9c75-0513f1f76772","html_url":"https://github.com/kagof/julia-image-processing","commit_stats":{"total_commits":3,"total_committers":1,"mean_commits":3.0,"dds":0.0,"last_synced_commit":"3c11c25740a9b89b29b45f82e61fea7c1bcd9447"},"previous_names":["kagof/julia-image-processing"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kagof%2Fjulia-image-processing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kagof%2Fjulia-image-processing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kagof%2Fjulia-image-processing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kagof%2Fjulia-image-processing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kagof","download_url":"https://codeload.github.com/kagof/julia-image-processing/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411194,"owners_count":20934650,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cuda","image-processing","julia"],"created_at":"2024-10-09T18:26:41.428Z","updated_at":"2026-05-18T09:33:13.648Z","avatar_url":"https://github.com/kagof.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Image Processing in Julia\n\nThis repository contains a collection of image processing algorithms written in Julia. Some of them take advantage of the CUDA module to run on an NVidia GPU.\n\n## Processes\n\n### Kuwahara\n\nAn implementation of the basic [Kuwahara filter](https://en.wikipedia.org/wiki/Kuwahara_filter). This filter creates a an oil painting-esque look. I've not (yet?) implemented more advanced versions of the Kuwahara filter like the Generalized version. The traditional version does have a blocky appearance and doesn't preserve edges very well.\n\n| Original | Kuwahara |\n|----------|----------|\n|![](https://upload.wikimedia.org/wikipedia/commons/c/c1/Wikipedia-sipi-image-db-mandrill-4.2.03.png)|![Example application of Kuwahara filter](/examples/mandrill_kuwahara.png)|\n\n\n\nThe results are deterministic, although the GPU and CPU versions' outputs vary slightly as can be seen below:\n\n![Difference between GPU and CPU versions](/examples/mandrill_kuwahara_difference.png)\n\nI believe this results from some minor bug in the GPU version that I've not yet resolved.\n\n#### [Kuwahara CPU](/src/Kuwahara.jl)\n\nThis takes advantage of however many threads are available to Julia.\n\n```julia\nkuwahara(image_in, region_size::Int=13)\n```\n\n#### [Kuwahara GPU](/src/KuwaharaGpu.jl)\n\nThis uses an NVidia GPU to dramatically speed up the process. The difference in performance is especially seen with larger region sizes. The code took much more effort as it seems that many standard functions (such as `sum`, `map`, `std`, etc) cannot be used in a kernel, and thus have to essentially be re-invented.\n\n```julia\nkuwaharaGpu(image_in, \n    region_size::Int = 13, \n    threads_per_block::Int = 256)\n```\n\n### Bokeh Blur\n\nThis is a blur effect that is able to imitate the [Bokeh](https://en.wikipedia.org/wiki/Bokeh) effect that occurs in cameras. An image to blur and an aperture can be passed in. The aperture should be a greyscale images which will be used to blur the source image. Points of light blurred with this filter will take the shape of the white part of the aperture.\n\nNote that if a pure white square image is used as the aperture, then this blur is equivalent to a [box blur](https://en.wikipedia.org/wiki/Box_blur).\n\nThe results are deterministic, and the GPU and CPU versions' seem to output the exact same image.\n\n| Original | default aperture (box blur) | circle aperture | star aperture |\n|----------|--------------------|-----------------|---------------|\n|![](examples/stars.png)|![](examples/stars_box_blur.png)|![](examples/stars_circle_blur.png)|![](examples/stars_star_blur.png)|\n|![](https://upload.wikimedia.org/wikipedia/commons/c/c1/Wikipedia-sipi-image-db-mandrill-4.2.03.png)|![](examples/mandrill_box_blur.png)|![](examples/mandrill_circle_blur.png)|![](examples/mandrill_star_blur.png)|\n\nThis can also be used with specific matrices to approximate a [Gaussian blur](https://en.wikipedia.org/wiki/Gaussian_blur).\n\nFor example, using the following matrix results in a `5x5` Gaussian blur effect:\n\n```julia\nImageProcessing.bokehBlur(testimage(\"mandrill\"), [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] / 256)\n```\n![](examples/mandrill_gaussian_5x5_approx.png)\n\nand the following results in a [difference of Gaussians](https://en.wikipedia.org/wiki/Difference_of_Gaussians) (of a `3x3` and `5x5` approximation):\n\n```julia\nImageProcessing.bokehBlur(testimage(\"mandrill\"), [1 2 1; 2 4 2; 1 2 1] / 16) - ImageProcessing.bokehBlur(testimage(\"mandrill\"), [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] / 256)\n```\n![](examples/mandrill_difference_of_gaussians_approx.png)\n\nRunning multiple iterations can make the effect stronger; the following is from 16 iterations:\n\n![](examples/mandrill_diff_gaussians_16_iter.png)\n\nMathematically, this effect is just convolution with an arbitrary image (or `nxm` array of values between 0 and 1) used as the kernel, then normalized. It doesn't accept values outside of that range because I've used the `Gray` datatype for the kernel. It should be pretty simple for me to generify this in the future to support convolution by any `nxm` matrix which will make it easy for me to implement effects like the Sobel operator, sharpening, etc. \n\n#### [Bokeh Blur CPU](src/BokehBlur.jl)\n\nThis takes advantage of however many threads are available to Julia.\n\n```julia\nbokehBlur(image_in, aperture=ones(Gray, 12, 12))\n```\n\n#### [Bokeh Blur GPU](src/BokehBlurGpu.jl)\n\nThis uses an NVidia GPU to speed up the process.\n\n```julia\nbokehBlurGpu(image_in,\n    aperture=map(Gray, ones(12, 12)),\n    threads_per_block::Int=256)\n```\n\n## Testing\n\nNo unit tests written yet, however there is [a util](/src/Test.jl) to play around with these processors and display the output side-by-side with the input.\n\n```julia\ntestKuwahara(img_name::String=\"mandrill\";\n    region_size::Int=13,\n    use_gpu::Bool=false,\n    threads_per_block::Int=512)\n```\n\n```julia\ntestBoxBlur(img_name::String=\"mandrill\";\n    box_size::Int=12,\n    use_gpu=false,\n    threads_per_block::Int=512)\n```\n\n```julia\ntestCircleBokehBlur(img_name::String=\"mandrill\";\n    use_gpu=false,\n    threads_per_block::Int=512)\n```\n\n```julia\ntestStarBokehBlur(img_name::String=\"mandrill\";\n    use_gpu=false,\n    threads_per_block::Int=512)\n```\n\n```julia\ntestGaussianBlur(img_name::String=\"mandrill\";\n    iterations::Int=1,\n    use_gpu=false,\n    threads_per_block::Int=512)\n```\n\n## On Edges\n\nSo far, every effect I've implemented takes a sample of values surrounding a pixel to determine what that pixel's value should be. When a pixel lies on the edge of the image, there isn't a set in stone way to decide how the algorithm handles pixels that are outside the image bounds. I've not seen a ton of opinions on what the best handling of this is. There are [a few different ways of handling it that I've seen outlined](https://en.wikipedia.org/wiki/Kernel_(image_processing)#Edge_handling). I naturally have chosen the \"Kernel Crop\" approach, which ignores anything outside the bounds and adjusts the normalization to compensate. This does however seem to lead to a slight vignette effect on the image, at least in the case of the bokeh blur.\n\nInterestingly, I suspect for the Kuwahara filter the \"Kernel Crop\" approach leads to the same result as the Extend approach, or certainly at least will lead to a very similar result. I do also want to try the Extend approach for the bokeh blur, as I think it should be the best option to get rid of the vignette in most cases.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkagof%2Fjulia-image-processing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkagof%2Fjulia-image-processing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkagof%2Fjulia-image-processing/lists"}