-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(cmake/conan): 简化CMakeLists.txt中的Conan集成并更新conanfile.txt
- 移除了CMakeLists.txt中冗余的Conan检测和配置代码。 - 直接在CMakeLists.txt中使用fetchcontent进行依赖管理。 - 更新了conanfile.txt,移除了未使用的依赖项并添加了pybind11和pybind11_json。 - 调整了scripts/pip.sh中的更新报告逻辑以修复输出格式。 此更改通过移除过时的Conan集成方法并采用fetchcontent简化依赖管理,从而显著清理了CMakeLists.txt。同时,对conanfile.txt的更新确保项目依赖项与当前需求保持一致。此外,pip.sh脚本的调整修复了更新报告的输出格式问题。
- Loading branch information
Showing
46 changed files
with
2,407 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .stretch import AdaptiveStretch | ||
from .preview import apply_real_time_preview |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import matplotlib.pyplot as plt | ||
import cv2 | ||
import numpy as np | ||
from .stretch import AdaptiveStretch | ||
from typing import Optional, Tuple | ||
|
||
def apply_real_time_preview(image: np.ndarray, noise_threshold: float = 1e-4, contrast_protection: Optional[float] = None, max_curve_points: int = 106, roi: Optional[Tuple[int, int, int, int]] = None): | ||
""" | ||
Simulate real-time preview by iteratively applying the adaptive stretch | ||
transformation and displaying the result. | ||
:param image: Input image as a numpy array (grayscale or color). | ||
:param noise_threshold: Threshold for treating brightness differences as noise. | ||
:param contrast_protection: Optional contrast protection parameter. | ||
:param max_curve_points: Maximum points for the transformation curve. | ||
:param roi: Tuple (x, y, width, height) defining the region of interest. | ||
""" | ||
adaptive_stretch = AdaptiveStretch(noise_threshold, contrast_protection, max_curve_points) | ||
preview_image = adaptive_stretch.stretch(image, roi) | ||
|
||
if len(preview_image.shape) == 3: | ||
preview_image = cv2.cvtColor(preview_image, cv2.COLOR_BGR2RGB) | ||
|
||
plt.imshow(preview_image, cmap='gray' if len(preview_image.shape) == 2 else None) | ||
plt.title(f"Noise Threshold: {noise_threshold}, Contrast Protection: {contrast_protection}") | ||
plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import numpy as np | ||
import cv2 | ||
from typing import Optional, Tuple | ||
|
||
|
||
class AdaptiveStretch: | ||
def __init__(self, noise_threshold: float = 1e-4, contrast_protection: Optional[float] = None, max_curve_points: int = 106): | ||
""" | ||
Initialize the AdaptiveStretch object with specific parameters. | ||
:param noise_threshold: Threshold for treating brightness differences as noise. | ||
:param contrast_protection: Optional contrast protection parameter. | ||
:param max_curve_points: Maximum points for the transformation curve. | ||
""" | ||
self.noise_threshold = noise_threshold | ||
self.contrast_protection = contrast_protection | ||
self.max_curve_points = max_curve_points | ||
|
||
def compute_brightness_diff(self, image: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: | ||
""" | ||
Compute brightness differences between adjacent pixels. | ||
Returns matrices of differences along the x and y axes. | ||
:param image: Input image as a numpy array (grayscale). | ||
:return: Tuple of differences along x and y axes. | ||
""" | ||
diff_x = np.diff(image, axis=1) # differences between columns | ||
diff_y = np.diff(image, axis=0) # differences between rows | ||
|
||
# Pad the differences to match the original image size | ||
diff_x = np.pad(diff_x, ((0, 0), (0, 1)), mode='constant') | ||
diff_y = np.pad(diff_y, ((0, 1), (0, 0)), mode='constant') | ||
|
||
return diff_x, diff_y | ||
|
||
def stretch(self, image: np.ndarray, roi: Optional[Tuple[int, int, int, int]] = None) -> np.ndarray: | ||
""" | ||
Apply the AdaptiveStretch transformation to the image. | ||
:param image: Input image as a numpy array (grayscale or color). | ||
:param roi: Tuple (x, y, width, height) defining the region of interest. | ||
:return: Stretched image. | ||
""" | ||
if len(image.shape) == 3: | ||
channels = cv2.split(image) | ||
else: | ||
channels = [image] | ||
|
||
stretched_channels = [] | ||
|
||
for channel in channels: | ||
# Normalize the channel to the range [0, 1] | ||
channel = channel.astype(np.float32) / 255.0 | ||
|
||
if roi is not None: | ||
x, y, w, h = roi | ||
channel_roi = channel[y:y+h, x:x+w] | ||
else: | ||
channel_roi = channel | ||
|
||
diff_x, diff_y = self.compute_brightness_diff(channel_roi) | ||
|
||
positive_forces = np.maximum(diff_x, 0) + np.maximum(diff_y, 0) | ||
negative_forces = np.minimum(diff_x, 0) + np.minimum(diff_y, 0) | ||
|
||
positive_forces[positive_forces < self.noise_threshold] = 0 | ||
negative_forces[negative_forces > -self.noise_threshold] = 0 | ||
|
||
transformation_curve = positive_forces + negative_forces | ||
|
||
if self.contrast_protection is not None: | ||
transformation_curve = np.clip( | ||
transformation_curve, -self.contrast_protection, self.contrast_protection) | ||
|
||
resampled_curve = cv2.resize( | ||
transformation_curve, (self.max_curve_points, 1), interpolation=cv2.INTER_LINEAR) | ||
|
||
interpolated_curve = cv2.resize( | ||
resampled_curve, (channel_roi.shape[1], channel_roi.shape[0]), interpolation=cv2.INTER_LINEAR) | ||
|
||
stretched_channel = channel_roi + interpolated_curve | ||
|
||
stretched_channel = np.clip( | ||
stretched_channel * 255, 0, 255).astype(np.uint8) | ||
|
||
if roi is not None: | ||
channel[y:y+h, x:x+w] = stretched_channel | ||
stretched_channel = channel | ||
|
||
stretched_channels.append(stretched_channel) | ||
|
||
if len(stretched_channels) > 1: | ||
stretched_image = cv2.merge(stretched_channels) | ||
else: | ||
stretched_image = stretched_channels[0] | ||
|
||
return stretched_image |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .histogram import auto_histogram | ||
from .utils import save_image, load_image |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import cv2 | ||
import numpy as np | ||
from typing import Optional, List, Tuple | ||
from .utils import save_image, load_image | ||
|
||
|
||
def auto_histogram(image: Optional[np.ndarray] = None, clip_shadow: float = 0.01, clip_highlight: float = 0.01, | ||
target_median: int = 128, method: str = 'gamma', apply_clahe: bool = False, | ||
clahe_clip_limit: float = 2.0, clahe_tile_grid_size: Tuple[int, int] = (8, 8), | ||
apply_noise_reduction: bool = False, noise_reduction_method: str = 'median', | ||
apply_sharpening: bool = False, sharpening_strength: float = 1.0, | ||
batch_process: bool = False, file_list: Optional[List[str]] = None) -> Optional[List[np.ndarray]]: | ||
""" | ||
Apply automated histogram transformations and enhancements to an image or a batch of images. | ||
Parameters: | ||
- image: Input image, grayscale or RGB. | ||
- clip_shadow: Percentage of shadow pixels to clip. | ||
- clip_highlight: Percentage of highlight pixels to clip. | ||
- target_median: Target median value for histogram stretching. | ||
- method: Stretching method ('gamma', 'logarithmic', 'mtf'). | ||
- apply_clahe: Apply CLAHE (Contrast Limited Adaptive Histogram Equalization). | ||
- clahe_clip_limit: CLAHE clip limit. | ||
- clahe_tile_grid_size: CLAHE grid size. | ||
- apply_noise_reduction: Apply noise reduction. | ||
- noise_reduction_method: Noise reduction method ('median', 'gaussian'). | ||
- apply_sharpening: Apply image sharpening. | ||
- sharpening_strength: Strength of sharpening. | ||
- batch_process: Enable batch processing mode. | ||
- file_list: List of file paths for batch processing. | ||
Returns: | ||
- Processed image or list of processed images. | ||
""" | ||
def histogram_clipping(image: np.ndarray, clip_shadow: float, clip_highlight: float) -> np.ndarray: | ||
flat = image.flatten() | ||
low_val = np.percentile(flat, clip_shadow * 100) | ||
high_val = np.percentile(flat, 100 - clip_highlight * 100) | ||
return np.clip(image, low_val, high_val) | ||
|
||
def gamma_transformation(image: np.ndarray, target_median: int) -> np.ndarray: | ||
mean_val = np.median(image) | ||
gamma = np.log(target_median / 255.0) / np.log(mean_val / 255.0) | ||
return np.array(255 * (image / 255.0) ** gamma, dtype='uint8') | ||
|
||
def logarithmic_transformation(image: np.ndarray) -> np.ndarray: | ||
c = 255 / np.log(1 + np.max(image)) | ||
return np.array(c * np.log(1 + image), dtype='uint8') | ||
|
||
def mtf_transformation(image: np.ndarray, target_median: int) -> np.ndarray: | ||
mean_val = np.median(image) | ||
mtf = target_median / mean_val | ||
return np.array(image * mtf, dtype='uint8') | ||
|
||
def apply_clahe_method(image: np.ndarray, clip_limit: float, tile_grid_size: Tuple[int, int]) -> np.ndarray: | ||
clahe = cv2.createCLAHE(clipLimit=clip_limit, | ||
tileGridSize=tile_grid_size) | ||
if len(image.shape) == 2: | ||
return clahe.apply(image) | ||
else: | ||
return cv2.merge([clahe.apply(channel) for channel in cv2.split(image)]) | ||
|
||
def noise_reduction(image: np.ndarray, method: str) -> np.ndarray: | ||
if method == 'median': | ||
return cv2.medianBlur(image, 3) | ||
elif method == 'gaussian': | ||
return cv2.GaussianBlur(image, (3, 3), 0) | ||
else: | ||
raise ValueError("Invalid noise reduction method specified.") | ||
|
||
def sharpen_image(image: np.ndarray, strength: float) -> np.ndarray: | ||
kernel = np.array([[-1, -1, -1], [-1, 9 + strength, -1], [-1, -1, -1]]) | ||
return cv2.filter2D(image, -1, kernel) | ||
|
||
def process_single_image(image: np.ndarray) -> np.ndarray: | ||
if apply_noise_reduction: | ||
image = noise_reduction(image, noise_reduction_method) | ||
|
||
image = histogram_clipping(image, clip_shadow, clip_highlight) | ||
|
||
if method == 'gamma': | ||
image = gamma_transformation(image, target_median) | ||
elif method == 'logarithmic': | ||
image = logarithmic_transformation(image) | ||
elif method == 'mtf': | ||
image = mtf_transformation(image, target_median) | ||
else: | ||
raise ValueError("Invalid method specified.") | ||
|
||
if apply_clahe: | ||
image = apply_clahe_method( | ||
image, clahe_clip_limit, clahe_tile_grid_size) | ||
|
||
if apply_sharpening: | ||
image = sharpen_image(image, sharpening_strength) | ||
|
||
return image | ||
|
||
if batch_process: | ||
if file_list is None: | ||
raise ValueError( | ||
"File list cannot be None when batch processing is enabled.") | ||
processed_images = [] | ||
for file_path in file_list: | ||
image = load_image(file_path, method != 'mtf') | ||
processed_image = process_single_image(image) | ||
processed_images.append(processed_image) | ||
save_image(f'processed_{file_path}', processed_image) | ||
return processed_images | ||
else: | ||
return process_single_image(image) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from .histogram import auto_histogram | ||
from .utils import save_image, load_image | ||
import os | ||
from typing import List | ||
|
||
|
||
def process_directory(directory: str, output_directory: str, method: str = 'gamma', **kwargs): | ||
""" | ||
Process all images in a directory using the auto_histogram function. | ||
:param directory: Input directory containing images to process. | ||
:param output_directory: Directory to save processed images. | ||
:param method: Histogram stretching method ('gamma', 'logarithmic', 'mtf'). | ||
:param kwargs: Additional parameters for auto_histogram. | ||
""" | ||
if not os.path.exists(output_directory): | ||
os.makedirs(output_directory) | ||
|
||
file_list = [os.path.join(directory, file) for file in os.listdir( | ||
directory) if file.endswith(('.jpg', '.png'))] | ||
|
||
processed_images = auto_histogram( | ||
None, method=method, batch_process=True, file_list=file_list, **kwargs) | ||
|
||
for file, image in zip(file_list, processed_images): | ||
output_path = os.path.join( | ||
output_directory, f'processed_{os.path.basename(file)}') | ||
save_image(output_path, image) |
Oops, something went wrong.