Skip to content

Commit

Permalink
Add BM3D(sparse 3D transform-domain collaborative filtering) denoisin…
Browse files Browse the repository at this point in the history
…g algorithm (#21)
  • Loading branch information
Longhao-Chen authored Aug 4, 2021
1 parent 2cf0300 commit f662676
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ authors = ["Johnny Chen <[email protected]>"]
version = "0.1.2"

[deps]
BM3DDenoise = "95fb3b36-088a-43fb-bb1b-b1f34fadbd7d"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[compat]
BM3DDenoise = "1.0.1"
Distributions = "0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25"
ImageCore = "0.9"
Reexport = "0.2, 1.0"
Expand Down
32 changes: 32 additions & 0 deletions docs/examples/reduce_noise/BM3D.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# ---
# title: The BM3D(sparse 3D transform-domain collaborative filtering) denoising algorithm.
# id: bm3d-demo
# cover: assets/bm3d_cover.png
# date: 2021-08-04
# author: "[Longhao Chen](https:/Longhao-Chen)"
# description: Use the BM3D denoising algorithm to reduce gaussian noise
# ---

using ImageNoise
using TestImages, ImageShow, ImageCore, ImageQualityIndexes, ImageTransformations
using FileIO, Random #src

# First, load an image and and add gaussian noise to it

gray_img = float.(imresize(testimage("cameraman"), ratio=0.5))
n = AdditiveWhiteGaussianNoise(0.1)
noisy_img = apply_noise(gray_img, n)

# Then calling the standard `reduce_noise` API

f_bm3d = BM3D(0.1)
denoised_img = reduce_noise(noisy_img, f_bm3d)

mosaicview(gray_img, noisy_img, denoised_img; nrow=1)

# Get the PSNR using ImageQualityIndexes package:

assess_psnr(gray_img, denoised_img)

mkpath("assets") #src
save("assets/bm3d_cover.png", denoised_img) #src
55 changes: 55 additions & 0 deletions src/ReduceNoise/BM3DDenoise.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using UUIDs
const BM3DDenoise = Base.PkgId(UUID("95fb3b36-088a-43fb-bb1b-b1f34fadbd7d"), "BM3DDenoise")

# Rewrite from ImageIO.jl
function checked_import(pkgid)
Base.root_module_exists(pkgid) && return Base.root_module(pkgid)
# If not available, load the library or throw an error.
Base.require(pkgid)
end


@doc raw"""
BM3D(σ [, config=bm3d_config()])
The BM3D(sparse 3D transform-domain collaborative filtering) denoising algorithm.
# Arguments
* `σ::Float64` is the variance of the noise.
* `config::bm3d_config` is `BM3DDenoise.bm3d_config`.
# Examples
```julia
img = testimage("lena_color_256")
n = AdditiveWhiteGaussianNoise(0.1)
noisy_img = apply_noise(img, n)
# use default arguments
f_denoise = BM3D(0.1)
denoised_img = reduce_noise(noisy_img, f_denoise)
```
See also: [`reduce_noise`](@ref), [`reduce_noise!`](@ref)
"""
struct BM3D <: AbstractImageDenoiseAlgorithm
"""degree of filtering"""
σ::Float64
"""bm3d_config"""
config
function BM3D(σ, config)
σ > 0 || @warn "σ is supposed to be positive"
new(σ, config)
end
end
BM3D(σ) = BM3D(σ, Base.invokelatest(checked_import(BM3DDenoise).bm3d_config))

function (f::BM3D)(out::AbstractArray{T},
img::AbstractArray) where T
axes(out) == axes(img) || ArgumentError("Images should have the same axes.")
out .= T.(Base.invokelatest(checked_import(BM3DDenoise).bm3d, img, f.σ, f.config))
return out
end
3 changes: 3 additions & 0 deletions src/ReduceNoise/ReduceNoise.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ using ImageCore: NumberLike, GenericGrayImage, GenericImage
import ..NoiseAPI: AbstractImageDenoiseAlgorithm, reduce_noise, reduce_noise!

include("compat.jl")
include("BM3DDenoise.jl")
include("NonlocalMean.jl")

export
reduce_noise, reduce_noise!,

# BM3D
BM3D,
# Non-local mean filter for gaussian noise
NonlocalMean, get_NonlocalMean_rp

Expand Down
14 changes: 14 additions & 0 deletions test/ReduceNoise/BM3DDenoise.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@testset "BM3DDenoise" begin
@info "Test: BM3DDenoise"
@testset "Numeric" begin
img_gray = n0f8.(imresize(testimage("lena_gray_256"); ratio=0.25))
n = AdditiveWhiteGaussianNoise(0.05)
noisy_img = apply_noise(img_gray, n; rng=MersenneTwister(0))

f = BM3D(0.05)
denoised_img = reduce_noise(noisy_img, f)
# further modification shall not decrease psnr and ssim
@test assess(PSNR(), denoised_img, img_gray) >= 28.
@test assess(SSIM(), denoised_img, img_gray) >= 0.9
end
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ include("ApplyNoise/AdditiveWhiteGaussianNoise.jl")
# ReduceNoise
@info "Test: ReduceNoise"
include("ReduceNoise/NonlocalMean.jl")
include("ReduceNoise/BM3DDenoise.jl")
end

nothing

0 comments on commit f662676

Please sign in to comment.