修改这段代码以支持打包
import os
import cv2
import numpy as np
import screeninfo
from PIL import Image, ImageDraw, ImageFont
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import os
import logging
# 配置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
def resource_path(relative_path):
"""获取资源绝对路径,支持PyInstaller打包后运行"""
try:
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
class EnhancedImageViewer:
"""增强版图像查看器,支持亮度查看(Lux值)"""
def __init__(self, image_path, title="图像查看器", base_output_name=None):
self.image_path = image_path
self.original_image = cv2.imread(image_path)
if self.original_image is None:
raise FileNotFoundError(f"无法加载图像: {image_path}")
# 保存基础输出名称
self.base_output_name = base_output_name if base_output_name else \
os.path.splitext(os.path.basename(image_path))[0]
# 获取灰度图像用于亮度分析
self.gray_image = cv2.cvtColor(self.original_image, cv2.COLOR_BGR2GRAY)
# 获取屏幕尺寸
try:
screen = screeninfo.get_monitors()[0]
self.max_width = screen.width - 100
self.max_height = screen.height - 100
except:
self.max_width = 1620
self.max_height = 880
# 初始缩放状态
self.scale_factor = 1.0
self.offset_x = 0
self.offset_y = 0
# 创建显示图像
self.display_image = self.original_image.copy()
self.resized_display = self.resize_to_fit(self.original_image)
# 创建窗口
self.window_name = title
cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL)
cv2.setMouseCallback(self.window_name, self.mouse_callback)
# 更新显示
self.update_display()
def resize_to_fit(self, image):
"""调整图像尺寸以适应屏幕"""
height, width = image.shape[:2]
scale_width = self.max_width / width
scale_height = self.max_height / height
self.scale_factor = min(scale_width, scale_height, 1.0)
new_width = int(width * self.scale_factor)
new_height = int(height * self.scale_factor)
if self.scale_factor < 1.0:
return cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_AREA)
return image.copy()
def put_chinese_text(self, image, text, position, font_size=20, color=(255, 255, 255)):
"""在图像上添加中文文本"""
if image is None or image.size == 0:
return image
# 确保图像是3通道的
if len(image.shape) == 2:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
# 转换为PIL图像 (RGB格式)
pil_img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(pil_img)
# 使用支持中文的字体
try:
font = ImageFont.truetype("simhei.ttf", font_size)
except:
try:
font = ImageFont.truetype("msyh.ttc", font_size)
except:
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", font_size)
except:
font = ImageFont.load_default()
# 添加文本
draw.text(position, text, font=font, fill=color)
# 转换回OpenCV格式
return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
def update_display(self):
"""更新显示图像"""
# 缩放小于等于1.0时直接显示
self.display_image = self.resized_display.copy()
# 添加帮助文本
help_text = "左键:查看亮度(Lux) | ESC:退出 | R:重置 | S:保存"
self.display_image = self.put_chinese_text(
self.display_image, help_text,
(10, 20), font_size=20, color=(0, 255, 0)
)
# 显示图像
cv2.imshow(self.window_name, self.display_image)
def mouse_callback(self, event, x, y, flags, param):
"""鼠标事件回调函数 - 仅保留左键点击功能"""
# 鼠标左键点击 - 显示亮度值(Lux)
if event == cv2.EVENT_LBUTTONDOWN:
# 计算原始图像坐标
orig_x, orig_y = self.convert_coords(x, y)
# 获取原始图像中的灰度值
if 0 <= orig_x < self.gray_image.shape[1] and 0 <= orig_y < self.gray_image.shape[0]:
gray_value = self.gray_image[orig_y, orig_x]
# 将灰度值转换为Lux值 (0-255范围映射到0-740 Lux)
lux_value = int((gray_value / 255.0) * 740)
# 在当前显示图像上添加标记和信息
display_copy = self.display_image.copy()
cv2.circle(display_copy, (x, y), 5, (0, 0, 255), -1)
# 计算文本位置(避免遮挡)
text_x = x + 10
text_y = y - 10
# 如果靠近右侧边缘,向左移动文本
if text_x > self.display_image.shape[1] - 250:
text_x = x - 250
# 如果靠近底部边缘,向上移动文本
if text_y < 30:
text_y = y + 20
# 添加中文文本(显示Lux值)
text = f"位置: ({orig_x}, {orig_y}) 亮度: {lux_value} Lux"
display_copy = self.put_chinese_text(
display_copy, text,
(text_x, text_y),
font_size=18, color=(0, 255, 255)
)
cv2.imshow(self.window_name, display_copy)
def convert_coords(self, x, y):
"""将显示坐标转换为原始图像坐标"""
# 缩小或正常状态下的坐标转换
orig_x = int(x / self.scale_factor)
orig_y = int(y / self.scale_factor)
# 确保坐标在有效范围内
orig_x = max(0, min(orig_x, self.original_image.shape[1] - 1))
orig_y = max(0, min(orig_y, self.original_image.shape[0] - 1))
return orig_x, orig_y
def run(self):
"""运行查看器主循环"""
while True:
key = cv2.waitKey(1) & 0xFF
if key == 27: # ESC退出
break
elif key == ord('r'): # 重置视图
self.scale_factor = 1.0
self.offset_x = 0
self.offset_y = 0
self.resized_display = self.resize_to_fit(self.original_image)
self.update_display()
elif key == ord('s'): # 保存当前视图
# 使用基础输出名称生成截图文件名
screenshot_path = f"{self.base_output_name}_screenshot.png"
cv2.imwrite(screenshot_path, self.display_image)
print(f"截图已保存为 {screenshot_path}")
cv2.destroyAllWindows()
def preprocess_image(image_path):
"""图像预处理"""
image = cv2.imread(image_path)
if image is None:
raise FileNotFoundError(f"无法加载图像: {image_path}")
# 转换为灰度图并降噪
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (9, 9), 0)
# 对比度增强
clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(8, 8))
enhanced = clahe.apply(blurred)
return image, gray, enhanced
def analyze_intensity(enhanced):
"""亮度分析"""
min_intensity = np.min(enhanced)
max_intensity = np.max(enhanced)
normalized = cv2.normalize(enhanced.astype('float'), None, 0, 1, cv2.NORM_MINMAX)
return min_intensity, max_intensity, normalized
def generate_heatmap(image, normalized):
"""生成热力图"""
heatmap = cv2.applyColorMap((normalized * 255).astype(np.uint8), cv2.COLORMAP_JET)
blended = cv2.addWeighted(image, 0.7, heatmap, 0.3, 0)
return heatmap, blended
def process_contours(image, enhanced, min_intensity, max_intensity, num_levels=7):
"""处理等高线"""
height, width = image.shape[:2]
contour_image = np.zeros_like(image)
color_intensity_map = []
# 计算亮度层级
levels = np.linspace(min_intensity, max_intensity, num_levels).astype(np.uint8)
kernel = np.ones((3, 3), np.uint8)
for i, level in enumerate(levels):
# 创建当前亮度层级的掩膜
lower_val = max(int(level) - 10, 0)
upper_val = min(int(level) + 10, 255)
mask = cv2.inRange(enhanced, lower_val, upper_val)
# 形态学处理
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# 查找等高线
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 计算颜色(冷色调到暖色调渐变)
normalized_level = (level - min_intensity) / (max_intensity - min_intensity)
if normalized_level < 0.5:
hue = 120 + 60 * normalized_level * 2
else:
hue = 60 * (1 - (normalized_level - 0.5) * 2)
hue = np.clip(hue, 0, 180)
# 转换为BGR颜色
color = cv2.cvtColor(np.uint8([[[hue, 255, 255]]]), cv2.COLOR_HSV2BGR)[0][0]
color = tuple(map(int, color))
# 存储颜色-亮度映射
# 将灰度值转换为Lux值 (0-255范围映射到0-740 Lux)
lux_value = int((level / 255.0) * 740)
color_intensity_map.append((color, lux_value))
# 绘制等高线
cv2.drawContours(contour_image, contours, -1, color, 2)
return contour_image, color_intensity_map
def create_color_bar(image, color_intensity_map, min_intensity, max_intensity):
"""创建颜色条 - 显示Lux值"""
height, width = image.shape[:2]
bar_width = int(width * 0.03)
bar_height = int(height * 0.3)
bar_x = width - int(width * 0.05) - bar_width
bar_y = int(height * 0.05)
# 生成颜色条
color_bar = np.zeros((bar_height, bar_width, 3), dtype=np.uint8)
for i in range(bar_height):
idx = int((1 - i / bar_height) * (len(color_intensity_map) - 1))
color_bar[i, :] = color_intensity_map[idx][0]
# 添加到图像
result = image.copy()
result[bar_y:bar_y + bar_height, bar_x:bar_x + bar_width] = color_bar
# 添加边框
cv2.rectangle(result, (bar_x, bar_y),
(bar_x + bar_width, bar_y + bar_height), (255, 255, 255), 1)
# 添加刻度和标签(显示Lux值)
num_ticks = 5
min_lux = 0
max_lux = 740
for i, pos in enumerate(np.linspace(0, bar_height, num_ticks)):
y_pos = int(bar_y + pos)
cv2.line(result, (bar_x - 5, y_pos), (bar_x, y_pos), (255, 255, 255), 1)
# 计算标签位置
value = int(min_lux + (max_lux - min_lux) * (1 - pos / bar_height))
text_x = bar_x - 50
text_y = y_pos + (15 if i == 0 else -10 if i == num_ticks - 1 else 0)
# 添加带描边的文本
cv2.putText(result, f"{value} Lux", (text_x, text_y),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 0), 2)
cv2.putText(result, f"{value} Lux", (text_x, text_y),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
# 添加标题
cv2.putText(result, 'Light Intensity (Lux)', (bar_x - 150, bar_y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 0), 2)
cv2.putText(result, 'Light Intensity (Lux)', (bar_x - 150, bar_y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
return result
class ImageHandler(FileSystemEventHandler):
def __init__(self, processing_function):
self.processing_function = processing_function
self.supported_exts = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
def on_created(self, event):
"""当新文件创建时触发"""
if not event.is_directory:
file_path = event.src_path
if self._is_image(file_path):
logging.info(f"检测到新图片: {file_path}")
# 等待文件完全写入(根据实际需求调整)
time.sleep(0.5)
self.processing_function(file_path)
def _is_image(self, file_path):
"""检查是否为支持的图片格式"""
ext = os.path.splitext(file_path)[1].lower()
return ext in self.supported_exts
def start_monitoring(path_to_watch, processing_function):
"""启动文件夹监控服务"""
event_handler = ImageHandler(processing_function)
observer = Observer()
observer.schedule(event_handler, path_to_watch, recursive=False)
observer.start()
logging.info(f"开始监控文件夹: {path_to_watch}")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
def main(image_path):
"""主处理流程"""
try:
# 从路径中提取基础文件名(不含扩展名)
base_name = os.path.splitext(os.path.basename(image_path))[0]
# 1. 图像预处理
image, gray, enhanced = preprocess_image(image_path)
# 2. 亮度分析
min_intensity, max_intensity, normalized = analyze_intensity(enhanced)
# 3. 生成热力图
heatmap, blended = generate_heatmap(image, normalized)
# 4. 处理等高线
contour_image, color_intensity_map = process_contours(
image, enhanced, min_intensity, max_intensity
)
# 5. 创建最终结果(显示Lux值)
base_image = cv2.addWeighted(image, 0.7, contour_image, 0.3, 0)
# 使用固定的0-740 Lux范围
min_lux = 0
max_lux = 740
final_result = create_color_bar(base_image, color_intensity_map, min_lux, max_lux)
# 保存结果(使用基础文件名作为前缀)
cv2.imwrite(f"{base_name}_result.png", final_result)
cv2.imwrite(f"{base_name}_Contours.png", contour_image)
cv2.imwrite(f"{base_name}_Heatmap.png", heatmap)
cv2.imwrite(f"{base_name}_Blended.png", blended)
print(f"处理完成! 结果已保存为 {base_name}_*.png")
# 启动交互式查看器(传递基础文件名)
viewer = EnhancedImageViewer('4.jpg', 'photo_view', base_output_name=base_name)
viewer.run()
except Exception as e:
print(f"处理过程中发生错误: {str(e)}")
if __name__ == "__main__":
# 修改这里的图片路径
image_path = '4.jpg'
main(image_path)
最新发布