From f288546f3e788ac9ae5eac591638844e0a73f61e Mon Sep 17 00:00:00 2001 From: Francesco Ioli Date: Wed, 2 Aug 2023 17:55:04 +0200 Subject: [PATCH] added low_light_image_enhancement as thirdparty library --- .github/workflows/build-package.yml | 0 .github/workflows/deploy-docs.yaml | 0 .gitignore | 0 .vscode/launch.json | 0 .vscode/settings.json | 0 LICENSE | 0 README.md | 0 docs/assets/GitHub-icon.png | Bin docs/getting_started.md | 0 docs/index.md | 0 docs/installation.md | 0 docs/modules/camera.md | 0 docs/modules/conversion.md | 0 docs/modules/dji.md | 0 docs/modules/images.md | 0 docs/modules/renaming.md | 0 docs/modules/transformations.md | 0 main.py | 0 mkdocs.yml | 0 notebooks/dji_mrk.ipynb | 0 notebooks/image_renaming.ipynb | 118 +++++++-- notebooks/image_renaming_sandbox.py | 0 notebooks/potree_preview_sandbox.ipynb | 0 notebooks/potree_preview_sandbox.py | 0 notebooks/raw_conversion.ipynb | 0 notebooks/utm_projection.ipynb | 0 notebooks/utm_projection_sandbox.py | 0 pyproject.toml | 0 requirements-dev.txt | 0 requirements.txt | 11 - src/impreproc/__init__.py | 0 src/impreproc/camera.py | 0 src/impreproc/conversion.py | 0 src/impreproc/dji.py | 0 src/impreproc/gui/__init__.py | 0 src/impreproc/gui/dji2metashape.py | 0 src/impreproc/images.py | 0 src/impreproc/renaming.py | 73 +++--- src/impreproc/thirdparty/__init__.py | 0 .../low_light_image_enhancement/README.md | 67 +++++ .../low_light_image_enhancement/__init__.py | 1 + .../exposure_enhancement.py | 246 ++++++++++++++++++ src/impreproc/transformations.py | 0 .../utils/CameraSensorSizeDatabase/LICENSE | 0 .../utils/CameraSensorSizeDatabase/README.md | 0 .../sensor_database.csv | 0 .../sensor_database_detailed.csv | 0 src/impreproc/utils/__init__.py | 0 src/impreproc/utils/geometry.py | 0 src/impreproc/utils/logger.py | 0 src/impreproc/utils/parser.py | 0 src/impreproc/utils/sensor_width_database.py | 0 src/impreproc/utils/timer.py | 0 test/test_djimrk.py | 0 test/test_transformations.py | 0 55 files changed, 443 insertions(+), 73 deletions(-) mode change 100644 => 100755 .github/workflows/build-package.yml mode change 100644 => 100755 .github/workflows/deploy-docs.yaml mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .vscode/launch.json mode change 100644 => 100755 .vscode/settings.json mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 docs/assets/GitHub-icon.png mode change 100644 => 100755 docs/getting_started.md mode change 100644 => 100755 docs/index.md mode change 100644 => 100755 docs/installation.md mode change 100644 => 100755 docs/modules/camera.md mode change 100644 => 100755 docs/modules/conversion.md mode change 100644 => 100755 docs/modules/dji.md mode change 100644 => 100755 docs/modules/images.md mode change 100644 => 100755 docs/modules/renaming.md mode change 100644 => 100755 docs/modules/transformations.md mode change 100644 => 100755 main.py mode change 100644 => 100755 mkdocs.yml mode change 100644 => 100755 notebooks/dji_mrk.ipynb mode change 100644 => 100755 notebooks/image_renaming.ipynb mode change 100644 => 100755 notebooks/image_renaming_sandbox.py mode change 100644 => 100755 notebooks/potree_preview_sandbox.ipynb mode change 100644 => 100755 notebooks/potree_preview_sandbox.py mode change 100644 => 100755 notebooks/raw_conversion.ipynb mode change 100644 => 100755 notebooks/utm_projection.ipynb mode change 100644 => 100755 notebooks/utm_projection_sandbox.py mode change 100644 => 100755 pyproject.toml mode change 100644 => 100755 requirements-dev.txt delete mode 100644 requirements.txt mode change 100644 => 100755 src/impreproc/__init__.py mode change 100644 => 100755 src/impreproc/camera.py mode change 100644 => 100755 src/impreproc/conversion.py mode change 100644 => 100755 src/impreproc/dji.py mode change 100644 => 100755 src/impreproc/gui/__init__.py mode change 100644 => 100755 src/impreproc/gui/dji2metashape.py mode change 100644 => 100755 src/impreproc/images.py mode change 100644 => 100755 src/impreproc/renaming.py create mode 100644 src/impreproc/thirdparty/__init__.py create mode 100644 src/impreproc/thirdparty/low_light_image_enhancement/README.md create mode 100644 src/impreproc/thirdparty/low_light_image_enhancement/__init__.py create mode 100644 src/impreproc/thirdparty/low_light_image_enhancement/exposure_enhancement.py mode change 100644 => 100755 src/impreproc/transformations.py mode change 100644 => 100755 src/impreproc/utils/CameraSensorSizeDatabase/LICENSE mode change 100644 => 100755 src/impreproc/utils/CameraSensorSizeDatabase/README.md mode change 100644 => 100755 src/impreproc/utils/CameraSensorSizeDatabase/sensor_database.csv mode change 100644 => 100755 src/impreproc/utils/CameraSensorSizeDatabase/sensor_database_detailed.csv mode change 100644 => 100755 src/impreproc/utils/__init__.py mode change 100644 => 100755 src/impreproc/utils/geometry.py mode change 100644 => 100755 src/impreproc/utils/logger.py mode change 100644 => 100755 src/impreproc/utils/parser.py mode change 100644 => 100755 src/impreproc/utils/sensor_width_database.py mode change 100644 => 100755 src/impreproc/utils/timer.py mode change 100644 => 100755 test/test_djimrk.py mode change 100644 => 100755 test/test_transformations.py diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.vscode/launch.json b/.vscode/launch.json old mode 100644 new mode 100755 diff --git a/.vscode/settings.json b/.vscode/settings.json old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/docs/assets/GitHub-icon.png b/docs/assets/GitHub-icon.png old mode 100644 new mode 100755 diff --git a/docs/getting_started.md b/docs/getting_started.md old mode 100644 new mode 100755 diff --git a/docs/index.md b/docs/index.md old mode 100644 new mode 100755 diff --git a/docs/installation.md b/docs/installation.md old mode 100644 new mode 100755 diff --git a/docs/modules/camera.md b/docs/modules/camera.md old mode 100644 new mode 100755 diff --git a/docs/modules/conversion.md b/docs/modules/conversion.md old mode 100644 new mode 100755 diff --git a/docs/modules/dji.md b/docs/modules/dji.md old mode 100644 new mode 100755 diff --git a/docs/modules/images.md b/docs/modules/images.md old mode 100644 new mode 100755 diff --git a/docs/modules/renaming.md b/docs/modules/renaming.md old mode 100644 new mode 100755 diff --git a/docs/modules/transformations.md b/docs/modules/transformations.md old mode 100644 new mode 100755 diff --git a/main.py b/main.py old mode 100644 new mode 100755 diff --git a/mkdocs.yml b/mkdocs.yml old mode 100644 new mode 100755 diff --git a/notebooks/dji_mrk.ipynb b/notebooks/dji_mrk.ipynb old mode 100644 new mode 100755 diff --git a/notebooks/image_renaming.ipynb b/notebooks/image_renaming.ipynb old mode 100644 new mode 100755 index e588eb5..fe42d78 --- a/notebooks/image_renaming.ipynb +++ b/notebooks/image_renaming.ipynb @@ -10,9 +10,18 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/francesco/phd/impreproc/src/impreproc/renaming.py:211: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", + " if dest_folder is \"previews\":\n" + ] + } + ], "source": [ "from pathlib import Path\n", "\n", @@ -25,23 +34,23 @@ "# Note that in Jupyter Notebooks, the path are relative to the notebooks folder!\n", "\n", "# Image path. It can be a single folder containing all images or a folder cointiaing subfolders with the images (recursive option must be set to True)\n", - "data_dir = Path(\"../data/renaming/\")\n", + "data_dir = Path(\"/mnt/phd/ronconi/img_potree\")\n", "\n", "# Path to the prior classes file (optional). It is a .csv file with two columns: \"name\" and \"class\", without the header. If present, a column \"class\" will be added to the output Pandas Dataframe\n", - "prior_class_file = data_dir / \"prior_classes.csv\"\n", + "# prior_class_file = data_dir / \"prior_classes.csv\"\n", "\n", "# Define extensions of the file to read as a list of strings. \n", "# It can be a single file extension [\"JPG\"] or multiples extensions [\"jpg\", \"PNG\", \"DNG\"] \n", "image_ext = [\"jpg\"] \n", "\n", "# Read images recursively in subbofolders\n", - "recursive = True\n", + "recursive = False\n", "\n", "# Destination folder\n", - "dest_folder = Path(\"../res/renamed/\")\n", + "dest_folder = data_dir / \"renamed/\"\n", "\n", "# Base name for the renamed images \n", - "base_name = \"IMG\"\n", + "base_name = \"DJI\"\n", "\n", "# Add a progressive ID after the base name\n", "progressive_id = True\n", @@ -66,18 +75,18 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "../data/renaming/d800/_DSC1549.JPG\n", - "../data/renaming/d800/_DSC1550.JPG\n", - "../data/renaming/d800/_DSC1551.JPG\n", - "../data/renaming/d800/_DSC1552.JPG\n", - "../data/renaming/d800/_DSC1553.JPG\n" + "/mnt/phd/ronconi/img_potree/DJI_0009.JPG\n", + "/mnt/phd/ronconi/img_potree/DJI_0012.JPG\n", + "/mnt/phd/ronconi/img_potree/DJI_0013.JPG\n", + "/mnt/phd/ronconi/img_potree/DJI_0020.JPG\n", + "/mnt/phd/ronconi/img_potree/DJI_0021.JPG\n" ] } ], @@ -99,20 +108,14 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - " 0%| | 0/33 [00:00 pd.DataFrame: def make_previews( self, - dest_folder: Union[str, Path], + dest_folder: Union[str, Path] = "previews", resize_factor: float = -1, preview_size=None, **kwargs, @@ -178,13 +207,16 @@ def make_previews( RuntimeError: If unable to rename a file. """ - dest_folder = Path(dest_folder) + + if dest_folder is "previews": + dest_folder = self.dest_folder / "previews" + else: + dest_folder = Path(dest_folder) dest_folder.mkdir(parents=True, exist_ok=True) func = partial( make_previews, dest_folder=dest_folder, - preview_size=preview_size, - base_name=self.base_name, + # base_name=self.base_name, **kwargs, ) if self.parallel: @@ -197,35 +229,6 @@ def make_previews( raise RuntimeError(f"Unable to rename file {file.name}") -class RenamingDict(TypedDict): - """A dictionary for storing metadata about an image being renamed. It maps the old image name to the new one and stores additional metadata about the image. - - Fields: - old_name (str): The original name of the image file. - new_name (str): The new name of the image file. - date (str): The date the image was taken in the format YYYY:MM:DD. - time (str): The time the image was taken in the format HH:MM:SS. - camera (str): The camera model that captured the image. - focal (float): The focal length of the lens that captured the image. - GPSlat (float): The latitude of the location where the image was taken. - GPSlon (float): The longitude of the location where the image was taken. - GPSh (float): The altitude of the location where the image was taken. - classification (int or None): The classification of the image, if applicable. - """ - - id: int - old_name: str - new_name: str - date: str - time: str - camera: str - focal: float - GPSlat: float - GPSlon: float - GPSh: float - classification: Union[int, None] - - def name_from_exif( fname: Union[str, Path], base_name: str = "IMG", @@ -329,8 +332,8 @@ def make_previews( dest_folder: Union[str, Path] = "previews", resize_factor: float = -1, resize_to: Union[int, Tuple[int]] = -1, - camera: Camera = None, undistort: bool = False, + camera: Camera = None, overlay_name: bool = True, output_format: str = "jpg", **kwargs, @@ -348,7 +351,7 @@ def make_previews( image = cv2.imread(str(fname)) # Resize image - if resize_factor is not None: + if resize_factor != -1: if "interpolation_flag" in kwargs.keys(): intep_flag = kwargs["interpolation_flag"] else: diff --git a/src/impreproc/thirdparty/__init__.py b/src/impreproc/thirdparty/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/impreproc/thirdparty/low_light_image_enhancement/README.md b/src/impreproc/thirdparty/low_light_image_enhancement/README.md new file mode 100644 index 0000000..b4965b4 --- /dev/null +++ b/src/impreproc/thirdparty/low_light_image_enhancement/README.md @@ -0,0 +1,67 @@ +# Low-light-Image-Enhancement +Python implementation of two low-light image enhancement techniques via illumination map estimation, based on the following papers: + * Dual Illumination Estimation for Robust Exposure Correction [[link](https://arxiv.org/pdf/1910.13688.pdf)] + * LIME: Low-light Image Enhancement via Illumination Map Estimation [[link](https://ieeexplore.ieee.org/document/7782813)] + +Both methods are based on retinex modelling, and aim at estimating the illumination map by preserving the prominent structure of the image, while removing the redundant texture details. To do this, the same optimization formulation is used by both papers (see references). The novelty introduced by the first paper (called DUAL below) compared to the second (called LIME below) is the estimation of this map for the original image, and for its inverted version, which allows to correct both the under-exposed and over-exposed parts of the image. + +The code implemented in this repository allows the use of both methods, which can be easily selected from the script parameters. + +## Installation +This implementation runs on python >= 3.7, use pip to install dependencies: +``` +pip3 install -r requirements.txt +``` + +## Usage +Use the `demo.py` script to enhance your images. +``` +usage: demo.py [-h] [-f FOLDER] [-g GAMMA] [-l LAMBDA_] [-ul] [-s SIGMA] + [-bc BC] [-bs BS] [-be BE] [-eps EPS] + +Python implementation of two low-light image enhancement techniques via illumination map estimation. + +optional arguments: + -h, --help show this help message and exit + -f FOLDER, --folder FOLDER + folder path to test images. + -g GAMMA, --gamma GAMMA + the gamma correction parameter. + -l LAMBDA_, --lambda_ LAMBDA_ + the weight for balancing the two terms in the illumination refinement optimization objective. + -ul, --lime Use the LIME method. By default, the DUAL method is used. + -s SIGMA, --sigma SIGMA + Spatial standard deviation for spatial affinity based Gaussian weights. + -bc BC parameter for controlling the influence of Mertens's contrast measure. + -bs BS parameter for controlling the influence of Mertens's saturation measure. + -be BE parameter for controlling the influence of Mertens's well exposedness measure. + -eps EPS constant to avoid computation instability. +``` + +### Example +``` +python3 demo.py -f ./demo/ -l 0.15 -g 0.6 +``` + +### Result +Low Light Image | Enhanced Image +:-------------------------:|:-------------------------: +![](demo/2.bmp) | ![](demo/enhanced/2_DUAL_g0.6_l0.15.bmp) + +### TODO + - [ ] Add a fourier based solver to speed up the inference + + +### :mortar_board: Citation +If you find this work useful in your research, please consider citing: +``` +@misc{lowlightpython, + author = {Souhaib Attaiki}, + title = {Low light Image Enhancement}, + year = {2020}, + publisher = {GitHub}, + journal = {GitHub repository}, + howpublished = {\url{https://github.com/pvnieo/Low-light-Image-Enhancement}}, +} + +``` diff --git a/src/impreproc/thirdparty/low_light_image_enhancement/__init__.py b/src/impreproc/thirdparty/low_light_image_enhancement/__init__.py new file mode 100644 index 0000000..c12ee1f --- /dev/null +++ b/src/impreproc/thirdparty/low_light_image_enhancement/__init__.py @@ -0,0 +1 @@ +from .exposure_enhancement import enhance_image_exposure diff --git a/src/impreproc/thirdparty/low_light_image_enhancement/exposure_enhancement.py b/src/impreproc/thirdparty/low_light_image_enhancement/exposure_enhancement.py new file mode 100644 index 0000000..79e158e --- /dev/null +++ b/src/impreproc/thirdparty/low_light_image_enhancement/exposure_enhancement.py @@ -0,0 +1,246 @@ +# 3p +import numpy as np +import cv2 +from scipy.spatial import distance +from scipy.ndimage.filters import convolve +from scipy.sparse import diags, csr_matrix +from scipy.sparse.linalg import spsolve + + +def get_sparse_neighbor(p: int, n: int, m: int): + """Returns a dictionnary, where the keys are index of 4-neighbor of `p` in the sparse matrix, + and values are tuples (i, j, x), where `i`, `j` are index of neighbor in the normal matrix, + and x is the direction of neighbor. + + Arguments: + p {int} -- index in the sparse matrix. + n {int} -- number of rows in the original matrix (non sparse). + m {int} -- number of columns in the original matrix. + + Returns: + dict -- dictionnary containing indices of 4-neighbors of `p`. + """ + i, j = p // m, p % m + d = {} + if i - 1 >= 0: + d[(i - 1) * m + j] = (i - 1, j, 0) + if i + 1 < n: + d[(i + 1) * m + j] = (i + 1, j, 0) + if j - 1 >= 0: + d[i * m + j - 1] = (i, j - 1, 1) + if j + 1 < m: + d[i * m + j + 1] = (i, j + 1, 1) + return d + + +def create_spacial_affinity_kernel(spatial_sigma: float, size: int = 15): + """Create a kernel (`size` * `size` matrix) that will be used to compute the he spatial affinity based Gaussian weights. + + Arguments: + spatial_sigma {float} -- Spatial standard deviation. + + Keyword Arguments: + size {int} -- size of the kernel. (default: {15}) + + Returns: + np.ndarray - `size` * `size` kernel + """ + kernel = np.zeros((size, size)) + for i in range(size): + for j in range(size): + kernel[i, j] = np.exp( + -0.5 + * (distance.euclidean((i, j), (size // 2, size // 2)) ** 2) + / (spatial_sigma**2) + ) + + return kernel + + +def compute_smoothness_weights( + L: np.ndarray, x: int, kernel: np.ndarray, eps: float = 1e-3 +): + """Compute the smoothness weights used in refining the illumination map optimization problem. + + Arguments: + L {np.ndarray} -- the initial illumination map to be refined. + x {int} -- the direction of the weights. Can either be x=1 for horizontal or x=0 for vertical. + kernel {np.ndarray} -- spatial affinity matrix + + Keyword Arguments: + eps {float} -- small constant to avoid computation instability. (default: {1e-3}) + + Returns: + np.ndarray - smoothness weights according to direction x. same dimension as `L`. + """ + Lp = cv2.Sobel(L, cv2.CV_64F, int(x == 1), int(x == 0), ksize=1) + T = convolve(np.ones_like(L), kernel, mode="constant") + T = T / (np.abs(convolve(Lp, kernel, mode="constant")) + eps) + return T / (np.abs(Lp) + eps) + + +def fuse_multi_exposure_images( + im: np.ndarray, + under_ex: np.ndarray, + over_ex: np.ndarray, + bc: float = 1, + bs: float = 1, + be: float = 1, +): + """perform the exposure fusion method used in the DUAL paper. + + Arguments: + im {np.ndarray} -- input image to be enhanced. + under_ex {np.ndarray} -- under-exposure corrected image. same dimension as `im`. + over_ex {np.ndarray} -- over-exposure corrected image. same dimension as `im`. + + Keyword Arguments: + bc {float} -- parameter for controlling the influence of Mertens's contrast measure. (default: {1}) + bs {float} -- parameter for controlling the influence of Mertens's saturation measure. (default: {1}) + be {float} -- parameter for controlling the influence of Mertens's well exposedness measure. (default: {1}) + + Returns: + np.ndarray -- the fused image. same dimension as `im`. + """ + merge_mertens = cv2.createMergeMertens(bc, bs, be) + images = [np.clip(x * 255, 0, 255).astype("uint8") for x in [im, under_ex, over_ex]] + fused_images = merge_mertens.process(images) + return fused_images + + +def refine_illumination_map_linear( + L: np.ndarray, gamma: float, lambda_: float, kernel: np.ndarray, eps: float = 1e-3 +): + """Refine the illumination map based on the optimization problem described in the two papers. + This function use the sped-up solver presented in the LIME paper. + + Arguments: + L {np.ndarray} -- the illumination map to be refined. + gamma {float} -- gamma correction factor. + lambda_ {float} -- coefficient to balance the terms in the optimization problem. + kernel {np.ndarray} -- spatial affinity matrix. + + Keyword Arguments: + eps {float} -- small constant to avoid computation instability (default: {1e-3}). + + Returns: + np.ndarray -- refined illumination map. same shape as `L`. + """ + # compute smoothness weights + wx = compute_smoothness_weights(L, x=1, kernel=kernel, eps=eps) + wy = compute_smoothness_weights(L, x=0, kernel=kernel, eps=eps) + + n, m = L.shape + L_1d = L.copy().flatten() + + # compute the five-point spatially inhomogeneous Laplacian matrix + row, column, data = [], [], [] + for p in range(n * m): + diag = 0 + for q, (k, l, x) in get_sparse_neighbor(p, n, m).items(): + weight = wx[k, l] if x else wy[k, l] + row.append(p) + column.append(q) + data.append(-weight) + diag += weight + row.append(p) + column.append(p) + data.append(diag) + F = csr_matrix((data, (row, column)), shape=(n * m, n * m)) + + # solve the linear system + Id = diags([np.ones(n * m)], [0]) + A = Id + lambda_ * F + L_refined = spsolve(csr_matrix(A), L_1d, permc_spec=None, use_umfpack=True).reshape( + (n, m) + ) + + # gamma correction + L_refined = np.clip(L_refined, eps, 1) ** gamma + + return L_refined + + +def correct_underexposure( + im: np.ndarray, gamma: float, lambda_: float, kernel: np.ndarray, eps: float = 1e-3 +): + """correct underexposudness using the retinex based algorithm presented in DUAL and LIME paper. + + Arguments: + im {np.ndarray} -- input image to be corrected. + gamma {float} -- gamma correction factor. + lambda_ {float} -- coefficient to balance the terms in the optimization problem. + kernel {np.ndarray} -- spatial affinity matrix. + + Keyword Arguments: + eps {float} -- small constant to avoid computation instability (default: {1e-3}) + + Returns: + np.ndarray -- image underexposudness corrected. same shape as `im`. + """ + + # first estimation of the illumination map + L = np.max(im, axis=-1) + # illumination refinement + L_refined = refine_illumination_map_linear(L, gamma, lambda_, kernel, eps) + + # correct image underexposure + L_refined_3d = np.repeat(L_refined[..., None], 3, axis=-1) + im_corrected = im / L_refined_3d + return im_corrected + + +# TODO: resize image if too large, optimization take too much time + + +def enhance_image_exposure( + im: np.ndarray, + gamma: float, + lambda_: float, + dual: bool = True, + sigma: int = 3, + bc: float = 1, + bs: float = 1, + be: float = 1, + eps: float = 1e-3, +): + """Enhance input image, using either DUAL method, or LIME method. For more info, please see original papers. + + Arguments: + im {np.ndarray} -- input image to be corrected. + gamma {float} -- gamma correction factor. + lambda_ {float} -- coefficient to balance the terms in the optimization problem (in DUAL and LIME). + + Keyword Arguments: + dual {bool} -- boolean variable to indicate enhancement method to be used (either DUAL or LIME) (default: {True}) + sigma {int} -- Spatial standard deviation for spatial affinity based Gaussian weights. (default: {3}) + bc {float} -- parameter for controlling the influence of Mertens's contrast measure. (default: {1}) + bs {float} -- parameter for controlling the influence of Mertens's saturation measure. (default: {1}) + be {float} -- parameter for controlling the influence of Mertens's well exposedness measure. (default: {1}) + eps {float} -- small constant to avoid computation instability (default: {1e-3}) + + Returns: + np.ndarray -- image exposure enhanced. same shape as `im`. + """ + # create spacial affinity kernel + kernel = create_spacial_affinity_kernel(sigma) + + # correct underexposudness + im_normalized = im.astype(float) / 255.0 + under_corrected = correct_underexposure(im_normalized, gamma, lambda_, kernel, eps) + + if dual: + # correct overexposure and merge if DUAL method is selected + inv_im_normalized = 1 - im_normalized + over_corrected = 1 - correct_underexposure( + inv_im_normalized, gamma, lambda_, kernel, eps + ) + # fuse images + im_corrected = fuse_multi_exposure_images( + im_normalized, under_corrected, over_corrected, bc, bs, be + ) + else: + im_corrected = under_corrected + + # convert to 8 bits and returns + return np.clip(im_corrected * 255, 0, 255).astype("uint8") diff --git a/src/impreproc/transformations.py b/src/impreproc/transformations.py old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/CameraSensorSizeDatabase/LICENSE b/src/impreproc/utils/CameraSensorSizeDatabase/LICENSE old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/CameraSensorSizeDatabase/README.md b/src/impreproc/utils/CameraSensorSizeDatabase/README.md old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/CameraSensorSizeDatabase/sensor_database.csv b/src/impreproc/utils/CameraSensorSizeDatabase/sensor_database.csv old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/CameraSensorSizeDatabase/sensor_database_detailed.csv b/src/impreproc/utils/CameraSensorSizeDatabase/sensor_database_detailed.csv old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/__init__.py b/src/impreproc/utils/__init__.py old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/geometry.py b/src/impreproc/utils/geometry.py old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/logger.py b/src/impreproc/utils/logger.py old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/parser.py b/src/impreproc/utils/parser.py old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/sensor_width_database.py b/src/impreproc/utils/sensor_width_database.py old mode 100644 new mode 100755 diff --git a/src/impreproc/utils/timer.py b/src/impreproc/utils/timer.py old mode 100644 new mode 100755 diff --git a/test/test_djimrk.py b/test/test_djimrk.py old mode 100644 new mode 100755 diff --git a/test/test_transformations.py b/test/test_transformations.py old mode 100644 new mode 100755