Skip to content

Commit

Permalink
reorganized code
Browse files Browse the repository at this point in the history
  • Loading branch information
lcmrl committed May 31, 2024
1 parent ad58402 commit 43d9773
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 106 deletions.
70 changes: 47 additions & 23 deletions scripts/convert_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,54 @@
import cv2
import argparse
import numpy as np

import rasterio
from PIL import Image
from deep_image_matching import logger


def load_img(
path_to_img: str,
reader: str,
):
#img = cv2.imread(path_to_img, cv2.IMREAD_ANYDEPTH)
logger.debug(
"Image loaded with pillow .."
)
img = Image.open(path_to_img)
img = img.convert("L")
logger.info(f"Image mode: {img.mode}")
img = np.array(img)
logger.info(f"Image mode: {img.dtype}")
"""
Load an image from the given path using the specified reader.
Args:
path_to_img (str): Path to the image file.
reader (str): The image reader to use. Supported options are "rasterio", "opencv", and "pillow".
Returns:
numpy.ndarray: The loaded image as a NumPy array.
"""
if reader == "rasterio":
with rasterio.open(path_to_img) as dataset:
img = dataset.read(1)

elif reader == "opencv":
img = cv2.imread(path_to_img, cv2.IMREAD_ANYDEPTH)

elif reader == "pillow":
img = Image.open(path_to_img)
img = img.convert("L")
img = np.array(img)

return img


def convert_images(input_folder, output_folder, target_format, normalize, method):
# Create the output folder if it doesn't exist
def convert_images(input_folder, output_folder, target_format, normalize, method, reader):
"""
Convert images in a folder to a target image format using OpenCV.
Args:
input_folder (str): Path to the folder containing input images.
output_folder (str): Path to the folder where converted images will be saved.
target_format (str): Target image format (e.g., 'png', 'jpg', 'jpeg', 'tiff').
normalize (bool): Flag indicating whether to normalize pixel values to cover the whole range 0-255.
method (str): Normalization method. Supported options are "single_image" and "image_set".
reader (str): Library to read the images. Supported options are "opencv", "pillow", and "rasterio".
"""
if not os.path.exists(output_folder):
os.makedirs(output_folder)

# Get a list of all image files in the input folder
image_files = [
f
for f in os.listdir(input_folder)
Expand All @@ -55,7 +76,7 @@ def convert_images(input_folder, output_folder, target_format, normalize, method
)
]
if len(image_files) == 0:
logger.error(
print(
"No images in the folder. Check that the path to the folder is correct or that the format is supported."
)
quit()
Expand All @@ -66,7 +87,7 @@ def convert_images(input_folder, output_folder, target_format, normalize, method

for image_file in image_files:
image_path = os.path.join(input_folder, image_file)
img = load_img(image_path)
img = load_img(image_path, reader)

MIN.append(np.min(img))
MAX.append(np.max(img))
Expand All @@ -75,15 +96,14 @@ def convert_images(input_folder, output_folder, target_format, normalize, method
abs_max = np.max(MAX)

for i, image_file in enumerate(image_files):
# Read the image
logger.info(f"[IMAGE{i}]")
print(f"[IMAGE{i}]")
image_path = os.path.join(input_folder, image_file)
output_path = os.path.join(
output_folder, os.path.splitext(image_file)[0] + "." + target_format
)
img = load_img(image_path)
img = load_img(image_path, reader)
min_original, max_original = np.min(img), np.max(img)
logger.info(f"Range: min_original {min_original} max_original {max_original}")
print(f"Range: min_original {min_original} max_original {max_original}")

if not normalize:
if img.dtype == np.uint8:
Expand Down Expand Up @@ -116,7 +136,6 @@ def convert_images(input_folder, output_folder, target_format, normalize, method


if __name__ == "__main__":
# Parse command-line arguments
parser = argparse.ArgumentParser(
description="Convert images in a folder to a target image format using OpenCV."
)
Expand All @@ -135,18 +154,23 @@ def convert_images(input_folder, output_folder, target_format, normalize, method
help="Normalize pixel values if set to cover the whole range 0-255.",
)
parser.add_argument(
"-m",
"--method",
choices=["single_image", "image_set"],
help="Normalize respect max and min of each single image or respect max and min of the entire image set.",
)
parser.add_argument(
"--reader",
choices=["opencv", "pillow", "rasterio"],
default="pillow",
help="Library to read the images. Default is 'pillow'.",
)
args = parser.parse_args()

# Call the function to convert images
convert_images(
args.input_folder,
args.output_folder,
args.target_format,
args.normalize,
args.method,
args.reader,
)
238 changes: 155 additions & 83 deletions scripts/normalize_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,96 +5,168 @@

from pathlib import Path

def img_gradient(image):
blurred_image = cv2.GaussianBlur(image, (3, 3), 0)
grad_x = cv2.Sobel(blurred_image, cv2.CV_64F, 1, 0, ksize=3)
grad_y = cv2.Sobel(blurred_image, cv2.CV_64F, 0, 1, ksize=3)
gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
gradient_magnitude = cv2.convertScaleAbs(gradient_magnitude)
normalized_gradient = cv2.normalize(gradient_magnitude, None, 0, 255, cv2.NORM_MINMAX)
return normalized_gradient

def normalize_image(image_path, output_path, patch_size=300, method="minmax"):
# Read the image in grayscale mode
image = cv2.imread(str(image_path), cv2.IMREAD_GRAYSCALE)
image = cv2.GaussianBlur(image, (5, 5), 0)
if image is None:
raise ValueError(f"Image at {image_path} could not be read.")

if method == "gradient":
gradient_magnitude = img_gradient(image)
cv2.imwrite(str(output_path), gradient_magnitude)
return

# Get the dimensions of the image
rows, cols = image.shape

# Create an empty array to store the normalized image
normalized_image = np.zeros_like(image, dtype=np.float32)

# Define the half patch size
half_patch_size = patch_size // 2

# Iterate over each pixel in the image
for i in range(rows):
for j in range(cols):
# Define the boundaries of the patch
r_min = max(0, i - half_patch_size)
r_max = min(rows, i + half_patch_size + 1)
c_min = max(0, j - half_patch_size)
c_max = min(cols, j + half_patch_size + 1)

# Extract the patch
patch = image[r_min:r_max, c_min:c_max]

# Calculate the mean and standard deviation of the patch
patch_mean = np.mean(patch)
patch_std = np.std(patch)
#masked_patch = np.ma.masked_outside(patch, patch_mean - 2 * patch_std, patch_mean + 2 * patch_std)
#robust_mean = np.mean(masked_patch)
#robust_std = np.std(masked_patch)
min_value = np.min(patch)
max_value = np.max(patch)

## Normalize the pixel with respect to the patch
#if patch_std > 0:
# normalized_image[i, j] = (image[i, j] - patch_mean) / patch_std
#else:
# normalized_image[i, j] = 0 # If the standard deviation is 0, set the normalized value to 0

#norm_value = int((max_value-min_value)*image[i,j]/255)

if method == "minmax":
norm_value = (image[i,j]-min_value)*255/(max_value-min_value)
if norm_value > 0 and norm_value < 256:
normalized_image[i, j] = norm_value
else:
normalized_image[i, j] = 0
elif method == "robust":
norm_value = (image[i,j]-patch_mean-patch_std)*255/(2*patch_std)
if norm_value > 0 and norm_value < 256:
normalized_image[i, j] = norm_value
else:
normalized_image[i, j] = 0

# Scale the normalized image to the range [0, 255]
#normalized_image = cv2.normalize(normalized_image, None, 0, 255, cv2.NORM_MINMAX)

# Convert the normalized image to uint8 type
normalized_image = normalized_image.astype(np.uint8)

class ImageNormalizer:
"""
A class for normalizing images using different methods.
"""

def __init__(
self,
reduce_noise: bool = True,
noise_kernel: tuple = (5, 5),
):
"""
Initialize the ImageNormalizer object.
Args:
reduce_noise (bool): Whether to apply noise reduction. Default is True.
noise_kernel (tuple): The size of the noise reduction kernel. Default is (5, 5).
"""
self.reduce_noise = reduce_noise
self.noise_kernel = noise_kernel

def img_gradient(
self,
image_path: Path,
output_path: Path,
ksize: int = 3,
):
"""
Export the gradient of the image.
Args:
image_path (Path): The path to the input image.
output_path (Path): The path to save the normalized image.
ksize (int): The size of the Sobel kernel. Default is 3.
"""
image = cv2.imread(str(image_path), cv2.IMREAD_GRAYSCALE)
if image is None:
raise ValueError(f"Image at {image_path} could not be read.")
if self.reduce_noise:
image = cv2.GaussianBlur(image, self.noise_kernel, 0)
grad_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=ksize)
grad_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=ksize)
gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
gradient_magnitude = cv2.convertScaleAbs(gradient_magnitude)
normalized_gradient = cv2.normalize(
gradient_magnitude, None, 0, 255, cv2.NORM_MINMAX
)
cv2.imwrite(str(output_path), normalized_gradient)

# Save the normalized image
cv2.imwrite(str(output_path), normalized_image)
def normalize_kernel(
self,
image_path: Path,
output_path: Path,
patch_size: int = 30,
method: str = "minmax",
):
"""
Normalize an image using kernel method.
Args:
image_path (Path): The path to the input image.
output_path (Path): The path to save the normalized image.
patch_size (int): The size of the patch. Default is 30.
method (str): The normalization method to use. Default is "minmax".
"""
image = cv2.imread(str(image_path), cv2.IMREAD_GRAYSCALE)
if image is None:
raise ValueError(f"Image at {image_path} could not be read.")
if self.reduce_noise:
image = cv2.GaussianBlur(image, self.noise_kernel, 0)

rows, cols = image.shape
normalized_image = np.zeros_like(image, dtype=np.float32)
half_patch_size = patch_size // 2

for i in range(rows):
for j in range(cols):
# Define the boundaries of the patch
r_min = max(0, i - half_patch_size)
r_max = min(rows, i + half_patch_size + 1)
c_min = max(0, j - half_patch_size)
c_max = min(cols, j + half_patch_size + 1)

# Extract the patch
patch = image[r_min:r_max, c_min:c_max]

if method == "minmax":
min_value = np.min(patch)
max_value = np.max(patch)
norm_value = (image[i, j] - min_value) * 255 / (max_value - min_value)
if norm_value > 0 and norm_value < 256:
normalized_image[i, j] = norm_value
else:
normalized_image[i, j] = 0

elif method == "meanstd":
patch_mean = np.mean(patch)
patch_std = np.std(patch)
# masked_patch = np.ma.masked_outside(patch, patch_mean - 2 * patch_std, patch_mean + 2 * patch_std)
# robust_mean = np.mean(masked_patch)
# robust_std = np.std(masked_patch)
norm_value = (
(image[i, j] - patch_mean - patch_std) * 255 / (2 * patch_std)
)
if norm_value > 0 and norm_value < 256:
normalized_image[i, j] = norm_value
else:
normalized_image[i, j] = 0

normalized_image = normalized_image.astype(np.uint8)
cv2.imwrite(str(output_path), normalized_image)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Normalize an image")
parser.add_argument("images", type=Path, help="Path to image folder to normalize")
parser.add_argument("output", type=Path, help="Path to output folder to save normalized images")
parser.add_argument("--patch_size", type=int, default=30, help="Size of the patch (default: 30)")
parser.add_argument(
"output", type=Path, help="Path to output folder to save normalized images"
)
parser.add_argument(
"--patch_size", type=int, default=30, help="Size of the patch (default: 30)"
)
parser.add_argument(
"--reduce_noise", type=bool, default=True, help="Apply noise reduction (default: True)"
)
parser.add_argument(
"--noise_kernel", type=tuple, default=(5, 5), help="Apply noise reduction (default: True)"
)
parser.add_argument(
"--method", type=str, choices=["norm_kernel_minmax", "norm_kernel_meanstd", "gradient"], default=(5, 5), help="Apply noise reduction (default: True)"
)
args = parser.parse_args()

images = os.listdir(args.images)
if not os.path.exists(args.output):
os.makedirs(args.output)

image_normalizer = ImageNormalizer(
reduce_noise = args.reduce_noise,
noise_kernel = args.noise_kernel,
)

for img in images:
normalize_image(args.images / img, args.output / img, args.patch_size)
if args.method == "norm_kernel_minmax":
image_normalizer.normalize_kernel(
args.images / img,
args.output / img,
patch_size=args.patch_size,
method="minmax",
)

elif args.method == "norm_kernel_meanstd":
image_normalizer.normalize_kernel(
args.images / img,
args.output / img,
patch_size=args.patch_size,
method="meanstd",
)

elif args.method == "gradient":
image_normalizer.img_gradient(
args.images / img,
args.output / img,
ksize=3,
)

0 comments on commit 43d9773

Please sign in to comment.