Willkommen zu meinem neuesten Projekt – einem vielseitigen Bildanalysetool, entwickelt mit Python und PyQt6! Dieses Tool nutzt fortschrittliche Bildverarbeitungstechniken, um Anomalien, Manipulationen oder interessante Muster in Bildern zu erkennen. Es ist ideal für Hobbyfotografen, Forensiker, Entwickler oder einfach alle, die neugierig auf die Geheimnisse hinter Bildern sind. In diesem Beitrag stelle ich das Tool vor, erkläre seine Funktionen, liefere den vollständigen Code, zeige Installationsanweisungen und gebe konkrete Einsatzbeispiele.
Was kann das Tool?
Das Tool kombiniert leistungsstarke Bibliotheken wie OpenCV, Scikit-Image und Matplotlib, um Bilder aus verschiedenen Perspektiven zu analysieren:
- Kantendetektion: Mit Algorithmen wie Canny, Sobel und Laplacian.
- Texturanalyse: Local Binary Patterns (LBP) und Gabor-Filter für Strukturen.
- Frequenzanalyse: Fourier-Transformation für globale Muster und Wavelet-Transformation für lokale Details.
- Farbverteilung: Histogramme im RGB- und HSV-Farbraum zur Untersuchung von Farben.
- Manipulationen: Error Level Analysis (ELA) zur Erkennung von Kompressionsunterschieden.
- Bewegung: Optischer Fluss für Bildpaare oder Videos.
Die Ergebnisse werden in einer übersichtlichen Matplotlib-Visualisierung dargestellt, wobei Anomalien mit grünen Rechtecken hervorgehoben werden. Dank der modernen PyQt6-Oberfläche ist die Bedienung kinderleicht: Bild laden, Analysemethoden per Checkbox auswählen und mit einem Klick starten.
Warum dieses Tool?
In einer Zeit, in der Bildmanipulationen durch Tools wie Photoshop oder KI-basierte Generatoren wie DALL-E alltäglich sind, wird es immer wichtiger, Fälschungen zu erkennen. Methoden wie ELA (siehe Hacker Factor Tutorial) decken subtile Unterschiede in der JPEG-Kompression auf, die auf Bearbeitungen hinweisen. Mein Tool geht darüber hinaus und kombiniert diese Technik mit anderen, um ein umfassendes Bild zu liefern – nicht nur für Forensik, sondern auch für kreative oder wissenschaftliche Zwecke.
Der Code
Hier ist der vollständige Code, bereit zum Kopieren und Ausprobieren:
python
import sys
import cv2
import numpy as np
import matplotlib.pyplot as plt
from skimage.feature import local_binary_pattern, hog
from skimage.filters import gabor
from scipy.fft import fft2, fftshift
import pywt
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QCheckBox, QFileDialog, QScrollArea)
from PyQt6.QtGui import QPixmap, QImage
from PyQt6.QtCore import Qt
import os
# --- Analysemethoden ---
def preprocess_image(image_path):
image = cv2.imread(image_path)
if image is None:
raise ValueError("Bild konnte nicht geladen werden.")
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred_image = cv2.GaussianBlur(gray_image, (5, 5), 0)
return image, gray_image, blurred_image
def detect_edges(blurred_image):
adaptive_thresh = cv2.adaptiveThreshold(blurred_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
edges_canny = cv2.Canny(adaptive_thresh, 100, 200)
edges_sobel = np.sqrt(cv2.Sobel(adaptive_thresh, cv2.CV_64F, 1, 0, ksize=3)**2 +
cv2.Sobel(adaptive_thresh, cv2.CV_64F, 0, 1, ksize=3)**2)
edges_laplacian = cv2.Laplacian(adaptive_thresh, cv2.CV_64F)
return edges_canny, edges_sobel, edges_laplacian
def analyze_texture(gray_image):
return local_binary_pattern(gray_image, P=8, R=1, method='uniform')
def analyze_gabor_features(gray_image):
gabor_response, _ = gabor(gray_image, frequency=0.6)
return gabor_response
def segment_image(gray_image):
_, segmented = cv2.threshold(gray_image, 127, 255, cv2.THRESH_BINARY)
return segmented
def analyze_histogram(gray_image):
return cv2.calcHist([gray_image], [0], None, [256], [0, 256])
def analyze_color_channels(image):
return [cv2.calcHist([ch], [0], None, [256], [0, 256]) for ch in cv2.split(image)]
def analyze_fourier_transform(gray_image):
f = fft2(gray_image)
fshift = fftshift(f)
return np.log(1 + np.abs(fshift))
def extract_hog_features(gray_image):
_, hog_image = hog(gray_image, visualize=True, block_norm='L2-Hys')
return hog_image
def analyze_pixel_values(gray_image):
diff_x = np.pad(np.abs(np.diff(gray_image, axis=0)), ((0, 1), (0, 0)), mode='constant')
diff_y = np.pad(np.abs(np.diff(gray_image, axis=1)), ((0, 0), (0, 1)), mode='constant')
return diff_x + diff_y
def analyze_wavelet(gray_image):
coeffs = pywt.dwt2(gray_image, 'db1')
cA, (cH, cV, cD) = coeffs
return np.abs(cD)
def analyze_hsv_anomalies(image):
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
hist_h = cv2.calcHist([h], [0], None, [180], [0, 180])
hist_s = cv2.calcHist([s], [0], None, [256], [0, 256])
return hsv_image, hist_h, hist_s
def analyze_optical_flow(prev_image, gray_image):
if prev_image is None:
return np.zeros_like(gray_image)
flow = cv2.calcOpticalFlowFarneback(prev_image, gray_image, None, 0.5, 3, 15, 3, 5, 1.2, 0)
return np.sqrt(flow[..., 0]**2 + flow[..., 1]**2)
def analyze_ela(image_path, quality=90):
temp_path = "temp_ela.jpg"
image = cv2.imread(image_path)
cv2.imwrite(temp_path, image, [int(cv2.IMWRITE_JPEG_QUALITY), quality])
recompressed = cv2.imread(temp_path)
ela = cv2.absdiff(image, recompressed)
ela_gray = cv2.cvtColor(ela, cv2.COLOR_BGR2GRAY)
os.remove(temp_path)
return ela_gray
def find_anomalies(edges_canny, segmented, texture, pixel_anomalies, ela_gray):
anomalies = np.zeros_like(edges_canny)
anomalies[(edges_canny > 0) | (segmented > 0) | (pixel_anomalies > 50) | (ela_gray > 20)] = 255
return anomalies
def visualize_results(image, results, anomalies):
plt.figure(figsize=(20, 15))
plot_idx = 1
plt.subplot(4, 5, plot_idx)
plt.title("Original")
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.axis("off")
plot_idx += 1
for key, value in results.items():
if key == "edges":
for i, edge in enumerate(value):
plt.subplot(4, 5, plot_idx)
plt.title(f"Edges ({['Canny', 'Sobel', 'Laplacian'][i]})")
plt.imshow(edge, cmap="gray")
plt.axis("off")
plot_idx += 1
elif key in ["histogram", "color_histograms", "hist_h", "hist_s"]:
plt.subplot(4, 5, plot_idx)
plt.title(f"{key}")
if key == "color_histograms":
for i, hist in enumerate(value):
plt.plot(hist, color=['r', 'g', 'b'][i])
else:
plt.plot(value)
plot_idx += 1
elif key == "hsv":
plt.subplot(4, 5, plot_idx)
plt.title("HSV")
plt.imshow(value)
plt.axis("off")
plot_idx += 1
else:
plt.subplot(4, 5, plot_idx)
plt.title(key.capitalize())
plt.imshow(value, cmap="gray" if key != "pixel_anomalies" else "hot")
plt.axis("off")
plot_idx += 1
plt.subplot(4, 5, plot_idx)
plt.title("Anomalies")
anomaly_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
contours, _ = cv2.findContours(anomalies, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
if cv2.contourArea(contour) > 500:
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(anomaly_image, (x, y), (x + w, y + h), (0, 255, 0), 2)
plt.imshow(anomaly_image)
plt.axis("off")
plt.tight_layout()
plt.savefig("analysis_results.png", dpi=300)
plt.show()
# --- PyQt6 GUI ---
class ImageAnalysisWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Bildanalyse-Tool")
self.setGeometry(100, 100, 800, 600)
self.image_path = None
self.prev_gray = None
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QHBoxLayout(main_widget)
left_layout = QVBoxLayout()
self.image_label = QLabel("Kein Bild geladen")
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_label.setFixedSize(400, 400)
left_layout.addWidget(self.image_label)
self.load_button = QPushButton("Bild laden")
self.load_button.clicked.connect(self.load_image)
left_layout.addWidget(self.load_button)
self.analyze_button = QPushButton("Analysieren")
self.analyze_button.clicked.connect(self.analyze_image)
left_layout.addWidget(self.analyze_button)
self.status_label = QLabel("Status: Bereit")
left_layout.addWidget(self.status_label)
left_layout.addStretch()
right_widget = QWidget()
right_layout = QVBoxLayout(right_widget)
right_layout.addWidget(QLabel("Analysemethoden"))
self.checkboxes = {
"Edges": QCheckBox("Edges", checked=True),
"Texture": QCheckBox("Texture", checked=True),
"Gabor": QCheckBox("Gabor", checked=True),
"Segmentation": QCheckBox("Segmentation", checked=True),
"Histogram": QCheckBox("Histogram", checked=True),
"Fourier": QCheckBox("Fourier", checked=True),
"HOG": QCheckBox("HOG", checked=True),
"Pixel Anomalies": QCheckBox("Pixel Anomalies", checked=True),
"Wavelet": QCheckBox("Wavelet", checked=True),
"HSV": QCheckBox("HSV", checked=True),
"Optical Flow": QCheckBox("Optical Flow", checked=False),
"ELA": QCheckBox("ELA", checked=True)
}
for checkbox in self.checkboxes.values():
right_layout.addWidget(checkbox)
right_layout.addStretch()
scroll = QScrollArea()
scroll.setWidget(right_widget)
scroll.setWidgetResizable(True)
main_layout.addLayout(left_layout)
main_layout.addWidget(scroll)
def load_image(self):
file_name, _ = QFileDialog.getOpenFileName(self, "Bild laden", "", "Image Files (*.png *.jpg *.jpeg *.jfif)")
if file_name:
self.image_path = file_name
pixmap = QPixmap(file_name).scaled(400, 400, Qt.AspectRatioMode.KeepAspectRatio)
self.image_label.setPixmap(pixmap)
self.status_label.setText(f"Status: Geladen: {os.path.basename(file_name)}")
def analyze_image(self):
if not self.image_path:
self.status_label.setText("Status: Bitte zuerst ein Bild laden!")
return
self.status_label.setText("Status: Analysiere...")
image, gray_image, blurred_image = preprocess_image(self.image_path)
results = {}
if self.checkboxes["Edges"].isChecked():
results["edges"] = detect_edges(blurred_image)
if self.checkboxes["Texture"].isChecked():
results["texture"] = analyze_texture(gray_image)
if self.checkboxes["Gabor"].isChecked():
results["gabor"] = analyze_gabor_features(gray_image)
if self.checkboxes["Segmentation"].isChecked():
results["segmented"] = segment_image(gray_image)
if self.checkboxes["Histogram"].isChecked():
results["histogram"] = analyze_histogram(gray_image)
results["color_histograms"] = analyze_color_channels(image)
if self.checkboxes["Fourier"].isChecked():
results["fourier"] = analyze_fourier_transform(gray_image)
if self.checkboxes["HOG"].isChecked():
results["hog"] = extract_hog_features(gray_image)
if self.checkboxes["Pixel Anomalies"].isChecked():
results["pixel_anomalies"] = analyze_pixel_values(gray_image)
if self.checkboxes["Wavelet"].isChecked():
results["wavelet"] = analyze_wavelet(gray_image)
if self.checkboxes["HSV"].isChecked():
results["hsv"], results["hist_h"], results["hist_s"] = analyze_hsv_anomalies(image)
if self.checkboxes["Optical Flow"].isChecked():
results["optical_flow"] = analyze_optical_flow(self.prev_gray, gray_image)
self.prev_gray = gray_image.copy()
if self.checkboxes["ELA"].isChecked():
results["ela"] = analyze_ela(self.image_path)
anomalies = find_anomalies(
results.get("edges", [np.zeros_like(gray_image)])[0],
results.get("segmented", np.zeros_like(gray_image)),
results.get("texture", np.zeros_like(gray_image)),
results.get("pixel_anomalies", np.zeros_like(gray_image)),
results.get("ela", np.zeros_like(gray_image))
)
visualize_results(image, results, anomalies)
self.status_label.setText("Status: Analyse abgeschlossen!")
# --- Start der Anwendung ---
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ImageAnalysisWindow()
window.show()
sys.exit(app.exec())
Installation
Um das Tool zu nutzen, benötigst du Python (≥ 3.8) und die folgenden Bibliotheken. Installiere sie mit diesem Befehl:
bash
pip install PyQt6 opencv-python numpy matplotlib scikit-image scipy pywavelets
- PyQt6: Moderne GUI-Bibliothek (Dokumentation).
- OpenCV: Kern der Bildverarbeitung (OpenCV.org).
- NumPy: Numerische Operationen (NumPy.org).
- Matplotlib: Visualisierung der Ergebnisse (Matplotlib.org).
- Scikit-Image: Erweiterte Bildverarbeitungsalgorithmen (Scikit-Image.org).
- SciPy: Wissenschaftliche Berechnungen wie FFT (SciPy.org).
- PyWavelets: Wavelet-Transformation für detaillierte Analysen (PyWavelets Dokumentation).
Falls du eine virtuelle Umgebung nutzen möchtest (empfohlen), erstelle sie so:
bash
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
pip install PyQt6 opencv-python numpy matplotlib scikit-image scipy pywavelets
Wie benutze ich es?
- Code speichern: Kopiere den Code in eine Datei, z. B. image_analyzer.py.
- Abhängigkeiten installieren: Führe den pip-Befehl oben aus.
- Tool starten: Starte das Skript mit python image_analyzer.py.
- Bild laden: Klicke auf „Bild laden“ und wähle ein Bild (JPEG, PNG, etc.).
- Methoden wählen: Aktiviere die gewünschten Analysen per Checkbox – standardmäßig sind fast alle aktiv.
- Analysieren: Klicke auf „Analysieren“. Die Ergebnisse erscheinen in einem Matplotlib-Fenster und werden als analysis_results.png gespeichert.
Einsatzbeispiele
1. Bildmanipulation aufdecken
Szenario: Du hast ein Foto, das angeblich unbearbeitet ist, aber du zweifelst daran.
Vorgehen: Lade das Bild, aktiviere „ELA“, „Wavelet“ und „Pixel Anomalies“. ELA zeigt Kompressionsunterschiede (z. B. bei retuschierten Bereichen), Wavelet deckt unnatürliche Texturen auf, und Pixel Anomalies markieren abrupte Übergänge. Grüne Rechtecke zeigen verdächtige Stellen – perfekt für Foto-Forensik!
2. Qualitätskontrolle in der Produktion
Szenario: Du prüfst Produktionsbilder auf Defekte wie Kratzer oder Verfärbungen.
Vorgehen: Wähle „Edges“, „Segmentation“ und „HSV“. Kantenalgorithmen finden physische Fehler, Segmentierung trennt Bereiche, und HSV zeigt Farbabweichungen. Dies könnte in der Industrie zur automatisierten Fehlererkennung erweitert werden.
3. Künstlerische Analyse
Szenario: Du möchtest die Struktur eines Gemäldes untersuchen.
Vorgehen: Nutze „Texture“, „Gabor“ und „Fourier“. LBP und Gabor zeigen lokale Muster (Pinselstriche), während Fourier globale Frequenzen enthüllt. Das Ergebnis könnte Kunsthistoriker oder Restauratoren interessieren.
4. Bewegungsanalyse (mit zwei Bildern)
Szenario: Du hast zwei aufeinanderfolgende Fotos und suchst nach Bewegung.
Vorgehen: Lade das erste Bild, analysiere es, dann das zweite mit „Optical Flow“ aktiviert. Die Bewegungsvektoren zeigen, wo sich etwas verändert hat – nützlich für Überwachung oder Animation.
Erweiterungsmöglichkeiten
- Maschinelles Lernen: Integriere ein Modell mit TensorFlow (TensorFlow.org) oder PyTorch (PyTorch.org), um Anomalien präziser zu klassifizieren.
- Video-Unterstützung: Erweitere das Tool mit OpenCVs VideoCapture für Echtzeit-Bewegungsanalysen.
- Web-Version: Nutze Flask (Flask.palletsprojects.com) oder Django (Djangoproject.com), um eine browserbasierte Version zu erstellen.
- Interaktive Schwellenwerte: Füge Slider hinzu, um Parameter wie Anomaliegröße dynamisch anzupassen.
Tipps für Fortgeschrittene
- Performance: Bei großen Bildern kannst du die Analyse mit NumPy’s Vektorisierung oder OpenCVs CUDA-Unterstützung beschleunigen (siehe OpenCV CUDA).
- Datenbank: Speichere Ergebnisse in einer SQLite-Datenbank für spätere Vergleiche.
- Customizing: Passe die Schwellenwerte (z. B. pixel_anomalies > 50) an deinen Anwendungsfall an.
Fazit
Dieses Bildanalysetool ist mehr als nur ein Spielzeug – es ist eine robuste Grundlage für vielfältige Anwendungen, von der Forensik über die Industrie bis hin zur Kunst. Die Kombination aus benutzerfreundlicher PyQt6-Oberfläche und leistungsstarken Analysemethoden macht es sowohl für Einsteiger als auch für Profis attraktiv. Es zeigt, wie zugänglich und spannend Bildverarbeitung mit Python sein kann.
Probiere es aus – lade ein Foto hoch, experimentiere mit den Methoden und entdecke, was sich hinter deinen Bildern verbirgt! Hast du Ideen für neue Funktionen oder Fragen zur Anpassung? Teile sie mir in den Kommentaren mit – ich freue mich auf Feedback und weitere Entwicklungen dieses Projekts.
Schreibe einen Kommentar