Compare commits

..

No commits in common. "ec4657eda81f0fa448cadc3e09b10437d499da85" and "462bd9250994ad1840978321a2229fedc328e439" have entirely different histories.

View File

@ -3,15 +3,12 @@ import skimage.restoration
import numpy as np
import rawpy
from tqdm import tqdm
from PIL import Image, ImageDraw
from PIL import Image
from skimage import img_as_float
from datetime import datetime
import builtins as __builtin__
from scipy.ndimage import gaussian_filter
import os
from skimage.restoration import estimate_sigma
from scipy import fftpack
import imageio
class Color(Enum):
RED = auto()
@ -27,23 +24,16 @@ class Denoiser(Enum):
BILATERAL = auto()
TV_CHAMBOLLE = auto()
MEAN = auto()
NL_MEANS = auto()
LOW_PASS = auto()
def __str__(self):
return self.name.lower()
# Source: https://stackoverflow.com/a/43346070
def getGaussianKernel(size, sigma):
axis = np.linspace(-(size - 1) / 2., (size - 1) / 2., size)
gauss = np.exp(-0.5 * np.square(axis) / np.square(sigma))
kernel = np.outer(gauss, gauss)
return kernel / np.sum(kernel)
# `denoiser` can be all values of `Denoiser` except `MEAN`.
# Among:
# - `wavelet`
# - `bilateral`
# - `tv_chambolle`
def denoise(imageNpArray, denoiser):
if denoiser != Denoiser.LOW_PASS:
skImageRestorationDenoise = getattr(skimage.restoration, f'denoise_{denoiser}')
skImageRestorationDenoise = getattr(skimage.restoration, f'denoise_{denoiser}')
match denoiser:
case Denoiser.WAVELET:
@ -52,36 +42,6 @@ def denoise(imageNpArray, denoiser):
imageDenoisedNpArray = skImageRestorationDenoise(imageNpArray, sigma_color=0.05, sigma_spatial=15)
case Denoiser.TV_CHAMBOLLE:
imageDenoisedNpArray = skImageRestorationDenoise(imageNpArray, weight=0.2)
case Denoiser.NL_MEANS:
sigmaEst = np.mean(estimate_sigma(imageNpArray, channel_axis = -1))
imageDenoisedNpArray = skImageRestorationDenoise(imageNpArray, patch_size = 7, patch_distance = 4, h = 0.8 * sigmaEst, fast_mode = True)
case Denoiser.LOW_PASS:
# Based on [the Stack Overflow answer 54662336](https://stackoverflow.com/a/54662336).
# fft of image
fft1 = fftpack.fftshift(fftpack.fft2(imageNpArray))
# Padding as follows may not be perfectly correct but seems fine enough.
GAUSSIAN_SIGMA = 50
imageNpArrayShape = imageNpArray.shape
lowPassNpArraySize = min(imageNpArrayShape)
lowPassNpArray = getGaussianKernel(lowPassNpArraySize, GAUSSIAN_SIGMA)
wantedHeight, wantedWidth = imageNpArrayShape
verticalPadSize, horizontalPadSize = [(wantedDimension - lowPassNpArraySize) // 2 for wantedDimension in [wantedHeight, wantedWidth]]
rectangleLowPassNpArray = np.pad(lowPassNpArray, [(verticalPadSize, verticalPadSize), (horizontalPadSize, horizontalPadSize)])
# Unsure that doing such *normalization* is appropriate.
rectangleLowPassNpArray /= rectangleLowPassNpArray.max()
# multiply both the images
filtered = np.multiply(fft1, rectangleLowPassNpArray)
# inverse fft
ifft2 = np.real(fftpack.ifft2(fftpack.ifftshift(filtered)))
ifft2 = np.maximum(0, np.minimum(ifft2, 255))
imageDenoisedNpArray = ifft2.astype(np.uint8)
return imageDenoisedNpArray
class iterativeMean:
@ -131,7 +91,7 @@ def isARawImage(imageFilePath):
def getColorChannel(imageFilePath, color):
if isARawImage(imageFilePath):
numpyFilePath = f'{imageFilePath}.{color}.npy'
if False and os.path.isfile(numpyFilePath):
if os.path.isfile(numpyFilePath):
imageNpArray = np.load(numpyFilePath)
else:
with rawpy.imread(imageFilePath) as raw:
@ -184,8 +144,7 @@ def updateExtremes(imageNpArray, minColor, maxColor):
return minColor, maxColor
def print(*toPrint):
#__builtin__.print(datetime.now(), *toPrint)
tqdm.write(f'{datetime.now()} {str(toPrint)}')
__builtin__.print(datetime.now(), *toPrint)
def getColorMeans(imagesFileNames, colors, singleColorChannelCropResolution = None):
colorMeans = {}
@ -200,18 +159,19 @@ def getColorMeans(imagesFileNames, colors, singleColorChannelCropResolution = No
colorMeans[color] = colorIterativeMean.mean
return colorMeans
def getImageNpArray(imageFilePath, computeExtremes, color, denoiser, minColor, maxColor):
def getImageNpArray(imageFilePath, computeExtremes, color, denoiser):
global minColor, maxColor
imageNpArray = getColorChannel(imageFilePath, color)
if computeExtremes:
minColor, maxColor = updateExtremes(imageNpArray, minColor, maxColor)
return None, minColor, maxColor
return
if isARawImage(imageFilePath) and denoiser != Denoiser.MEAN:
imageNpArray = rescaleRawImageForDenoiser(imageNpArray, minColor, maxColor)
# Pay attention to range of values expected by the denoiser.
# Indeed if provide the thousands valued raw image, then the denoiser only returns values between 0 and 1 and making the difference between both look pointless.
return imageNpArray, minColor, maxColor
return imageNpArray
def getImageCrop(image, cropResolution):
imageCrop = image[:cropResolution[0], :cropResolution[1]]