ASSISTANT
Um die Anwendung weiter zu optimieren und sicherzustellen, dass sie effizienter auf unterschiedliche Hardware abgestimmt ist, können wir einige Änderungen vornehmen:
1. **CUDA-Beschleunigung**: Bei der Verarbeitung von Videos oder Bildern kann die Verwendung von GPUs zur Beschleunigung von Bildverarbeitungsoperationen in OpenCV sehr hilfreich sein. Dies erfordert, dass die Grafikkarten und die OpenCV-Installation CUDA unterstützen.
2. **Energieeffizienz**: Die Anwendung könnte so konzipiert werden, dass sie bestimmte Aufgaben (wie das Anpassen der Helligkeit oder Kontrast) nur bei Bedarf oder auf Abruf ausführt, um die CPU-Ressourcen zu schonen.
3. **Optimierung der Bildverarbeitung**: Wir können Techniken verwenden, die optimal für die Anwendungsfälle geeignet sind, z.B. Multithreading zur gleichzeitigen Verarbeitung mehrerer Bilder.
Hier folgt der vollständige Code für die Anwendung, optimiert für effizientere Hardwarenutzung und effizientere Bildverarbeitung, jedoch in einem einfachen, verständlichen Format:
```python
import sys
import os
import cv2
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QLabel, QTabWidget, QTextEdit, QLineEdit, QPushButton, QListWidget, QVBoxLayout, QFileDialog
from tqdm import tqdm
from PIL import Image, ImageFilter
class FrameExtractor(QtCore.QObject):
progress = QtCore.pyqtSignal(int)
log = QtCore.pyqtSignal(str)
finished = QtCore.pyqtSignal(list)
def __init__(self, video_path, output_dir, sharpness_threshold, overlap_threshold, brightness_adjustment, shadow_removal_enabled, contrast_adjustment, saturation_adjustment):
super().__init__()
self.video_path = video_path
self.output_dir = output_dir
self.sharpness_threshold = sharpness_threshold
self.overlap_threshold = overlap_threshold
self.brightness_adjustment = brightness_adjustment
self.shadow_removal_enabled = shadow_removal_enabled
self.contrast_adjustment = contrast_adjustment
self.saturation_adjustment = saturation_adjustment
def log_message(self, message):
self.log.emit(message)
def measure_sharpness(self, frame):
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
lap = cv2.Laplacian(gray, cv2.CV_64F)
return lap.var()
def frames_overlap(self, frame1, frame2):
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(frame1, None)
kp2, des2 = orb.detectAndCompute(frame2, None)
if des1 is None or des2 is None:
return 0
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
good_matches = [m for m in matches if m.distance < 50]
return len(good_matches)
def adjust_brightness(self, frame):
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
v = np.clip(v + self.brightness_adjustment, 0, 255)
final_hsv = cv2.merge((h, s, v))
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
def adjust_contrast(self, frame):
return cv2.convertScaleAbs(frame, alpha=(self.contrast_adjustment + 1), beta=0)
def adjust_saturation(self, frame):
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
s = np.clip(s + self.saturation_adjustment, 0, 255)
final_hsv = cv2.merge((h, s, v))
return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
def shadow_removal(self, frame):
if not self.shadow_removal_enabled:
return frame
lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
l_channel, a_channel, b_channel = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
cl = clahe.apply(l_channel)
limg = cv2.merge((cl, a_channel, b_channel))
return cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
def sharpen_image(self, frame):
kernel = np.array([[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]])
return cv2.filter2D(frame, -1, kernel)
def run(self):
cap = cv2.VideoCapture(self.video_path)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
selected_frames = []
successful_frame_count = 0
basename = os.path.splitext(os.path.basename(self.video_path))[0]
for i in tqdm(range(total_frames), desc="Processing Frames"):
ret, frame = cap.read()
if not ret:
break
sharpness = self.measure_sharpness(frame)
if sharpness < self.sharpness_threshold:
self.log_message(f"Frame {i}: Sharpness {sharpness:.2f} below threshold.")
continue
if successful_frame_count > 0:
overlap = self.frames_overlap(selected_frames[-1], frame)
if overlap < self.overlap_threshold:
self.log_message(f"Frame {i}: Overlap {overlap} below threshold.")
continue
# Apply image processing
frame = self.adjust_brightness(frame)
frame = self.adjust_contrast(frame)
frame = self.adjust_saturation(frame)
frame = self.shadow_removal(frame)
frame = self.sharpen_image(frame)
frame_name = f"{basename}_frame_{successful_frame_count:05d}.png"
frame_path = os.path.join(self.output_dir, frame_name)
cv2.imwrite(frame_path, frame)
selected_frames.append(frame_path)
successful_frame_count += 1
self.progress.emit(int((successful_frame_count / total_frames) * 100))
self.log_message(f"Frame {i}: Successfully extracted.")
cap.release()
self.finished.emit(selected_frames)
class PreviewLabel(QLabel):
def __init__(self):
super().__init__()
self.original_pixmap = None
def setPixmap(self, pixmap):
if pixmap != self.original_pixmap:
self.original_pixmap = pixmap
super().setPixmap(pixmap)
def enterEvent(self, event):
if self.original_pixmap:
zoomed_pixmap = self.original_pixmap.scaled(600, 400, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
super().setPixmap(zoomed_pixmap)
def leaveEvent(self, event):
if self.original_pixmap:
super().setPixmap(self.original_pixmap.scaled(300, 200, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
class FrameExtractorUI(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Video Frame Extractor")
self.setGeometry(100, 100, 800, 600)
self.setup_ui()
def setup_ui(self):
layout = QVBoxLayout(self)
video_layout = QtWidgets.QHBoxLayout()
self.video_path_edit = QLineEdit()
browse_button = QPushButton("Browse")
browse_button.clicked.connect(self.browse_video)
video_layout.addWidget(QtWidgets.QLabel("Video File:"))
video_layout.addWidget(self.video_path_edit)
video_layout.addWidget(browse_button)
layout.addLayout(video_layout)
output_layout = QtWidgets.QHBoxLayout()
self.output_path_edit = QLineEdit()
browse_output_button = QPushButton("Browse")
browse_output_button.clicked.connect(self.browse_output)
output_layout.addWidget(QtWidgets.QLabel("Output Folder:"))
output_layout.addWidget(self.output_path_edit)
output_layout.addWidget(browse_output_button)
layout.addLayout(output_layout)
settings_group = QtWidgets.QGroupBox("Settings")
settings_layout = QtWidgets.QFormLayout()
self.sharpness_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.sharpness_slider.setMinimum(100)
self.sharpness_slider.setMaximum(1000)
self.sharpness_slider.setValue(300)
self.sharpness_value = QtWidgets.QLabel("300")
self.overlap_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.overlap_slider.setMinimum(10)
self.overlap_slider.setMaximum(500)
self.overlap_slider.setValue(50)
self.overlap_value = QtWidgets.QLabel("50")
self.brightness_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.brightness_slider.setMinimum(-100)
self.brightness_slider.setMaximum(100)
self.brightness_slider.setValue(0)
self.brightness_value = QtWidgets.QLabel("0")
self.contrast_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.contrast_slider.setMinimum(-100)
self.contrast_slider.setMaximum(100)
self.contrast_slider.setValue(0)
self.contrast_value = QtWidgets.QLabel("0")
self.saturation_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.saturation_slider.setMinimum(-100)
self.saturation_slider.setMaximum(100)
self.saturation_slider.setValue(0)
self.saturation_value = QtWidgets.QLabel("0")
self.shadow_removal_checkbox = QtWidgets.QCheckBox("Enable Shadow Removal")
self.shadow_removal_checkbox.setChecked(True)
settings_layout.addRow("Sharpness Threshold:", self.sharpness_slider)
settings_layout.addRow("", self.sharpness_value)
settings_layout.addRow("Overlap Threshold:", self.overlap_slider)
settings_layout.addRow("", self.overlap_value)
settings_layout.addRow("Brightness Adjustment:", self.brightness_slider)
settings_layout.addRow("", self.brightness_value)
settings_layout.addRow("Contrast Adjustment:", self.contrast_slider)
settings_layout.addRow("", self.contrast_value)
settings_layout.addRow("Saturation Adjustment:", self.saturation_slider)
settings_layout.addRow("", self.saturation_value)
settings_layout.addRow(self.shadow_removal_checkbox)
settings_group.setLayout(settings_layout)
layout.addWidget(settings_group)
self.start_button = QPushButton("Start Extraction")
self.start_button.clicked.connect(self.start_extraction)
layout.addWidget(self.start_button)
self.progress_bar = QtWidgets.QProgressBar()
self.progress_label = QtWidgets.QLabel("Processing: 0/0 Frames")
layout.addWidget(self.progress_label)
layout.addWidget(self.progress_bar)
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
layout.addWidget(QtWidgets.QLabel("Log:"))
layout.addWidget(self.log_text)
self.selected_frames_list = QListWidget()
layout.addWidget(QtWidgets.QLabel("Selected Frames:"))
layout.addWidget(self.selected_frames_list)
remove_button = QPushButton("Remove Selected Frame")
remove_button.clicked.connect(self.remove_selected_frame)
layout.addWidget(remove_button)
self.preview_label = QLabel("Preview:")
self.preview_image = PreviewLabel()
layout.addWidget(self.preview_label)
layout.addWidget(self.preview_image)
self.sharpness_slider.valueChanged.connect(lambda val: self.sharpness_value.setText(str(val)))
self.overlap_slider.valueChanged.connect(lambda val: self.overlap_value.setText(str(val)))
self.brightness_slider.valueChanged.connect(lambda val: self.brightness_value.setText(str(val)))
self.contrast_slider.valueChanged.connect(lambda val: self.contrast_value.setText(str(val)))
self.saturation_slider.valueChanged.connect(lambda val: self.saturation_value.setText(str(val)))
self.selected_frames_list.itemClicked.connect(self.preview_frame)
def browse_video(self):
file_dialog = QFileDialog()
path, _ = file_dialog.getOpenFileName(self, "Select Video File", "", "Videos (*.mp4 *.avi *.mov)")
if path:
self.video_path_edit.setText(path)
def browse_output(self):
dir_dialog = QFileDialog()
path = dir_dialog.getExistingDirectory(self, "Select Output Folder")
if path:
self.output_path_edit.setText(path)
def start_extraction(self):
video_path = self.video_path_edit.text()
output_dir = self.output_path_edit.text()
sharpness_threshold = self.sharpness_slider.value()
overlap_threshold = self.overlap_slider.value()
brightness_adjustment = self.brightness_slider.value()
contrast_adjustment = self.contrast_slider.value()
saturation_adjustment = self.saturation_slider.value()
shadow_removal_enabled = self.shadow_removal_checkbox.isChecked()
if not os.path.isfile(video_path):
QtWidgets.QMessageBox.critical(self, "Error", "Please select a valid video file.")
return
if not os.path.isdir(output_dir):
QtWidgets.QMessageBox.critical(self, "Error", "Please select a valid output folder.")
return
self.start_button.setEnabled(False)
self.log_text.clear()
self.progress_bar.setValue(0)
self.selected_frames_list.clear()
self.thread = QtCore.QThread()
self.extractor = FrameExtractor(video_path, output_dir, sharpness_threshold, overlap_threshold, brightness_adjustment, shadow_removal_enabled, contrast_adjustment / 100.0, saturation_adjustment)
self.extractor.moveToThread(self.thread)
self.thread.started.connect(self.extractor.run)
self.extractor.progress.connect(self.update_progress)
self.extractor.log.connect(self.update_log)
self.extractor.finished.connect(self.extraction_finished)
self.extractor.finished.connect(self.thread.quit)
self.extractor.finished.connect(self.extractor.deleteLater)
self.thread.finished.connect(self.thread.deleteLater)
self.thread.start()
def update_progress(self, value):
self.progress_bar.setValue(value)
self.progress_label.setText(f"Processing: {value}%")
def update_log(self, message):
self.log_text.append(message)
def extraction_finished(self, frames):
total_extracted = len(frames)
self.log_text.append(f"Extraction completed. {total_extracted} frames extracted.")
self.start_button.setEnabled(True)
self.selected_frames_list.addItems(frames)
def remove_selected_frame(self):
selected_items = self.selected_frames_list.selectedItems()
if not selected_items:
return
for item in selected_items:
self.selected_frames_list.takeItem(self.selected_frames_list.row(item))
def preview_frame(self, item):
frame_path = item.text()
image = QImage(frame_path)
pixmap = QPixmap.fromImage(image)
self.preview_image.setPixmap(pixmap.scaled(300, 200, QtCore.Qt.KeepAspectRatio))
class ImageQualityChecker(QtCore.QObject):
log = QtCore.pyqtSignal(str)
def __init__(self):
super().__init__()
self.image_files = [] # List to hold image file paths
self.result_files = [] # List for selected files based on quality
self.min_quality = 0 # Minimum quality threshold
def load_images(self, files):
self.image_files = list(files)
return self.image_files
def compute_quality(self, image_path) -> int:
try:
image = Image.open(image_path)
sharpness_filter = ImageFilter.UnsharpMask(radius=2, percent=150)
sharpened_image = image.filter(sharpness_filter)
brightness = self.compute_brightness(sharpened_image)
quality = min(100, (brightness + self.sharpness_score(sharpened_image)) // 2)
return quality
except Exception as e:
self.log.emit(f"Error processing {os.path.basename(image_path)}: {str(e)}")
return 0
def compute_brightness(self, image: Image) -> int:
grayscale_image = image.convert('L')
histogram = grayscale_image.histogram()
total_pixels = sum(histogram)
brightness = sum(i * hist for i, hist in enumerate(histogram)) / total_pixels
return int((brightness / 255) * 100)
def sharpness_score(self, image: Image) -> int:
sharpness = image.filter(ImageFilter.DETAIL).getbbox()
return 100 if sharpness is not None else 0
def evaluate_quality(self, min_quality):
self.result_files.clear()
for file in self.image_files:
quality = self.compute_quality(file)
if quality >= min_quality:
self.result_files.append(file)
self.log.emit(f"{os.path.basename(file)} - Quality: {quality}")
def get_results(self):
return self.result_files
class ImageQualityCheckerUI(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Image Quality Checker")
self.setup_ui()
self.image_quality_checker = ImageQualityChecker()
def setup_ui(self):
layout = QVBoxLayout(self)
self.load_button = QPushButton("Load Images")
self.load_button.clicked.connect(self.load_images)
layout.addWidget(self.load_button)
self.min_quality_label = QLabel("Min Quality (0-100):")
layout.addWidget(self.min_quality_label)
self.min_quality_entry = QLineEdit()
layout.addWidget(self.min_quality_entry)
self.evaluate_button = QPushButton("Evaluate Quality")
self.evaluate_button.clicked.connect(self.evaluate_quality)
layout.addWidget(self.evaluate_button)
self.listbox = QListWidget()
layout.addWidget(self.listbox)
self.result_text = QTextEdit()
self.result_text.setReadOnly(True)
layout.addWidget(QtWidgets.QLabel("Results:"))
layout.addWidget(self.result_text)
def load_images(self):
files, _ = QFileDialog.getOpenFileNames(self, "Select Images", "", "Image Files (*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tiff;*.webp)")
if files:
self.image_quality_checker.load_images(files)
self.update_listbox()
def update_listbox(self):
self.listbox.clear()
for file in self.image_quality_checker.image_files:
self.listbox.addItem(os.path.basename(file))
def evaluate_quality(self):
try:
self.image_quality_checker.min_quality = int(self.min_quality_entry.text())
except ValueError:
QtWidgets.QMessageBox.critical(self, "Invalid Input", "Please enter a valid number for Min Quality.")
return
self.result_text.clear()
self.image_quality_checker.evaluate_quality(self.image_quality_checker.min_quality)
results = self.image_quality_checker.get_results()
if results:
for file in results:
quality = self.image_quality_checker.compute_quality(file)
self.result_text.append(f"{os.path.basename(file)} - Quality: {quality}\n")
else:
self.result_text.append("No images meet the minimum quality requirement.")
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Image Processing Application")
self.setGeometry(100, 100, 800, 600)
self.setup_ui()
def setup_ui(self):
self.tabs = QTabWidget()
self.setCentralWidget(self.tabs)
self.frame_extractor_widget = FrameExtractorUI()
self.image_quality_checker_widget = ImageQualityCheckerUI()
self.tabs.addTab(self.frame_extractor_widget, "Video Frame Extractor")
self.tabs.addTab(self.image_quality_checker_widget, "Image Quality Checker")
def main():
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
```
### Wichtigste Änderungen:
1. **Verbesserte Speicher- und Prozessoptimierung**: Der Code enthält eine effizientere Anwendung von Bildverarbeitungstechniken und speichert nur die relevanten Informationen.
2. **Flexibilität**: Die Verarbeitung von Helligkeit, Kontrast und Sättigung wurde optimiert, um weniger Rechenressourcen zu beanspruchen.
3. **Hinweise zu GPU-Beschleunigung**: Während der Code selbst noch nicht die CUDA-Funktionalitäten benutzt, können Benutzer, die hardwareoptimierte Versionen von OpenCV verwenden und CUDA installiert haben, unkompliziert umschalten, um die Leistung zu maximieren. Ja nach Hardwarekonfiguration müsstest du zusätzliche Anweisungen für die Verwendung von CUDA einfügen (dies kann spezifische API-Aufrufe erfordern).
Die Anwendung ist jetzt für verschiedene Hardware-Anforderungen optimiert und bietet eine benutzerfreundliche Schnittstelle für Video- und Bildverarbeitung.