Skip to content

Commit

Permalink
refactor(cmake/conan): 简化CMakeLists.txt中的Conan集成并更新conanfile.txt
Browse files Browse the repository at this point in the history
- 移除了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
AstroAir committed Sep 2, 2024
1 parent abdd49d commit bfcbca2
Show file tree
Hide file tree
Showing 46 changed files with 2,407 additions and 64 deletions.
45 changes: 45 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,51 @@ LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/")
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake_modules/")
include(cmake_modules/compiler_options.cmake)

if (USE_CONAN)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake OPTIONAL) # Optional inclusion

# 检查是否已经安装Conan
find_program(CONAN_CMD conan)
if(NOT CONAN_CMD)
message(FATAL_ERROR "Conan is not installed. Please install Conan (pip install conan).")
endif()

# 检测Conan默认配置文件是否存在
execute_process(
COMMAND ${CONAN_CMD} config home
OUTPUT_VARIABLE CONAN_HOME
OUTPUT_STRIP_TRAILING_WHITESPACE
)

set(CONAN_DEFAULT_PROFILE "${CONAN_HOME}/profiles/default")

if(NOT EXISTS "${CONAN_DEFAULT_PROFILE}")
message(STATUS "Conan default profile not found. Creating a new profile based on platform.")
# 根据操作系统创建默认配置
if(WIN32)
execute_process(COMMAND ${CONAN_CMD} profile detect --force)
elseif(UNIX)
execute_process(COMMAND ${CONAN_CMD} profile detect --force)
else()
message(FATAL_ERROR "Unsupported platform for Conan profile detection.")
endif()
endif()

# 如果conanbuildinfo.cmake不存在,执行conan install命令
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake")
message(STATUS "Running Conan install...")
execute_process(
COMMAND ${CONAN_CMD} install ${CMAKE_SOURCE_DIR} --build=missing
RESULT_VARIABLE result
)
if(result)
message(FATAL_ERROR "Conan install failed with error code: ${result}")
endif()
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
endif()
endif()

# Include directories
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_SOURCE_DIR}/libs/)
Expand Down
52 changes: 0 additions & 52 deletions conanfile.py

This file was deleted.

9 changes: 0 additions & 9 deletions conanfile.txt
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
[requires]
argparse/3.0
cfitsio/4.3.1
cpython/3.12.2
cpp-httplib/0.15.3
fmt/10.2.1
loguru/cci.20230406
oatpp/1.3.0
oatpp-websocket/1.3.0
oatpp-openssl/1.3.0
oatpp-swagger/1.3.0
opencv/4.9.0
openssl/3.2.1
pybind11/2.12.0
pybind11_json/0.2.13
tinyxml2/10.0.0
zlib/1.3.1

Expand Down
2 changes: 2 additions & 0 deletions pysrc/image/adaptive_stretch/__init__.py
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
26 changes: 26 additions & 0 deletions pysrc/image/adaptive_stretch/preview.py
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()
97 changes: 97 additions & 0 deletions pysrc/image/adaptive_stretch/stretch.py
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
2 changes: 2 additions & 0 deletions pysrc/image/auto_histogram/__init__.py
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
111 changes: 111 additions & 0 deletions pysrc/image/auto_histogram/histogram.py
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)
28 changes: 28 additions & 0 deletions pysrc/image/auto_histogram/processing.py
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)
Loading

0 comments on commit bfcbca2

Please sign in to comment.