Skip to content

Commit

Permalink
added low_light_image_enhancement as thirdparty library
Browse files Browse the repository at this point in the history
  • Loading branch information
franioli committed Aug 2, 2023
1 parent 0e17bba commit f288546
Show file tree
Hide file tree
Showing 55 changed files with 443 additions and 73 deletions.
Empty file modified .github/workflows/build-package.yml
100644 → 100755
Empty file.
Empty file modified .github/workflows/deploy-docs.yaml
100644 → 100755
Empty file.
Empty file modified .gitignore
100644 → 100755
Empty file.
Empty file modified .vscode/launch.json
100644 → 100755
Empty file.
Empty file modified .vscode/settings.json
100644 → 100755
Empty file.
Empty file modified LICENSE
100644 → 100755
Empty file.
Empty file modified README.md
100644 → 100755
Empty file.
Empty file modified docs/assets/GitHub-icon.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified docs/getting_started.md
100644 → 100755
Empty file.
Empty file modified docs/index.md
100644 → 100755
Empty file.
Empty file modified docs/installation.md
100644 → 100755
Empty file.
Empty file modified docs/modules/camera.md
100644 → 100755
Empty file.
Empty file modified docs/modules/conversion.md
100644 → 100755
Empty file.
Empty file modified docs/modules/dji.md
100644 → 100755
Empty file.
Empty file modified docs/modules/images.md
100644 → 100755
Empty file.
Empty file modified docs/modules/renaming.md
100644 → 100755
Empty file.
Empty file modified docs/modules/transformations.md
100644 → 100755
Empty file.
Empty file modified main.py
100644 → 100755
Empty file.
Empty file modified mkdocs.yml
100644 → 100755
Empty file.
Empty file modified notebooks/dji_mrk.ipynb
100644 → 100755
Empty file.
118 changes: 91 additions & 27 deletions notebooks/image_renaming.ipynb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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"
]
}
],
Expand All @@ -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<?, ?it/s]WARNING:root:Unable to get GPS coordinates from exif from image _DSC1549.JPG.\n",
"WARNING:root:Unable to get GPS coordinates from exif from image _DSC1550.JPG.\n",
"WARNING:root:Unable to get GPS coordinates from exif from image _DSC1551.JPG.\n",
"WARNING:root:Unable to get GPS coordinates from exif from image _DSC1552.JPG.\n",
"WARNING:root:Unable to get GPS coordinates from exif from image _DSC1553.JPG.\n",
"WARNING:root:Unable to get GPS coordinates from exif from image _DSC1554.JPG.\n",
"100%|██████████| 33/33 [00:00<00:00, 122.89it/s]\n"
"100%|██████████| 25/25 [00:06<00:00, 3.97it/s]\n"
]
}
],
Expand Down Expand Up @@ -157,22 +160,83 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 5,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" 0%| | 0/25 [00:00<?, ?it/s]"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 25/25 [00:12<00:00, 2.07it/s]\n"
]
}
],
"source": [
"# Make previews for Potree by overlaying the name on the image\n",
"# This method is not fully implemented yet (resizing and normalizing by the focal length is missing)\n",
"# renamer.make_previews(dest_folder=dest_folder / \"previews\")"
"\n",
"# Create ImageRenamer object\n",
"renamer = ImageRenamer(\n",
" image_list=files,\n",
" dest_folder=data_dir / \"previews\",\n",
" base_name=base_name,\n",
")\n",
"renamer.make_previews()"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"img = Image(files[0])\n"
"# Low light image enhancement\n",
"from impreproc.thirdparty.low_light_image_enhancement import enhance_image_exposure\n",
"from pathlib import Path\n",
"import cv2 \n",
"\n",
"file = \"data/DJI_20230516113434_0070.JPG\"\n",
"# parser.add_argument(\"-g\", '--gamma', default=0.6, type=float,\n",
"# help=\"the gamma correction parameter.\")\n",
"# parser.add_argument(\"-l\", '--lambda_', default=0.15, type=float,\n",
"# help=\"the weight for balancing the two terms in the illumination refinement optimization objective.\")\n",
"# parser.add_argument(\"-ul\", \"--lime\", action='store_true',\n",
"# help=\"Use the LIME method. By default, the DUAL method is used.\")\n",
"# parser.add_argument(\"-s\", '--sigma', default=3, type=int,\n",
"# help=\"Spatial standard deviation for spatial affinity based Gaussian weights.\")\n",
"# parser.add_argument(\"-bc\", default=1, type=float,\n",
"# help=\"parameter for controlling the influence of Mertens's contrast measure.\")\n",
"# parser.add_argument(\"-bs\", default=1, type=float,\n",
"# help=\"parameter for controlling the influence of Mertens's saturation measure.\")\n",
"# parser.add_argument(\"-be\", default=1, type=float,\n",
"# help=\"parameter for controlling the influence of Mertens's well exposedness measure.\")\n",
"# parser.add_argument(\"-eps\", default=1e-3, type=float,\n",
"# help=\"constant to avoid computation instability.\")\n",
"\n",
"gamma = 0.6\n",
"lambda_ = 0.15\n",
"lime = True\n",
"sigma = 3\n",
"bc = 1\n",
"bs = 1\n",
"be = 1\n",
"eps = 1e-3\n",
"\n",
"fname = Path(file)\n",
"root_dir = Path.cwd().parent\n",
"image = cv2.imread(str(root_dir/fname)) \n",
"enhanced_image = enhance_image_exposure(image, gamma, lambda_, not lime,\n",
" sigma=sigma, bc=bc, bs=bs, be=be, eps=eps)\n",
"method = \"LIME\" if lime else \"DUAL\"\n",
"out_name = f\"{fname.stem}_{method}_g{gamma}_l{lambda_}{fname.suffix}\"\n",
"cv2.imwrite(str(fname.parent/out_name), enhanced_image)"
]
}
],
Expand All @@ -192,7 +256,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.16"
"version": "3.10.12"
},
"orig_nbformat": 4
},
Expand Down
Empty file modified notebooks/image_renaming_sandbox.py
100644 → 100755
Empty file.
Empty file modified notebooks/potree_preview_sandbox.ipynb
100644 → 100755
Empty file.
Empty file modified notebooks/potree_preview_sandbox.py
100644 → 100755
Empty file.
Empty file modified notebooks/raw_conversion.ipynb
100644 → 100755
Empty file.
Empty file modified notebooks/utm_projection.ipynb
100644 → 100755
Empty file.
Empty file modified notebooks/utm_projection_sandbox.py
100644 → 100755
Empty file.
Empty file modified pyproject.toml
100644 → 100755
Empty file.
Empty file modified requirements-dev.txt
100644 → 100755
Empty file.
11 changes: 0 additions & 11 deletions requirements.txt

This file was deleted.

Empty file modified src/impreproc/__init__.py
100644 → 100755
Empty file.
Empty file modified src/impreproc/camera.py
100644 → 100755
Empty file.
Empty file modified src/impreproc/conversion.py
100644 → 100755
Empty file.
Empty file modified src/impreproc/dji.py
100644 → 100755
Empty file.
Empty file modified src/impreproc/gui/__init__.py
100644 → 100755
Empty file.
Empty file modified src/impreproc/gui/dji2metashape.py
100644 → 100755
Empty file.
Empty file modified src/impreproc/images.py
100644 → 100755
Empty file.
73 changes: 38 additions & 35 deletions src/impreproc/renaming.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,35 @@
from impreproc.images import Image, ImageList, latlonalt_from_exif


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]


class ImageRenamer:
"""
A class for renaming a list of images.
Expand Down Expand Up @@ -158,7 +187,7 @@ def rename(self) -> 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,
Expand All @@ -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:
Expand All @@ -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",
Expand Down Expand Up @@ -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,
Expand All @@ -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:
Expand Down
Empty file.
67 changes: 67 additions & 0 deletions src/impreproc/thirdparty/low_light_image_enhancement/README.md
Original file line number Diff line number Diff line change
@@ -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}},
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .exposure_enhancement import enhance_image_exposure
Loading

0 comments on commit f288546

Please sign in to comment.