save results in a textfile: import os
import shutil
from collections import defaultdict
from PIL import Image
import numpy as np

# Mocking the configuration values initially
class Config:
    COLORDIFF_THRESHOLD = 5
    FRACTION_COLORED_PIXELS_THRESHOLD = 0.03

def is_color_image(
    img: np.ndarray,
    diff_threshold: int = Config.COLORDIFF_THRESHOLD,
    fraction_threshold: float = Config.FRACTION_COLORED_PIXELS_THRESHOLD,
) -> bool:
    """Determine if an image is color based on RGB channel differences."""
    # Check if the image has three channels (RGB)
    if img.ndim != 3 or img.shape[2] != 3:
        return False

    # Calculate the peak-to-peak (max - min) difference across the RGB channels for each pixel
    max_diff = np.ptp(img, axis=2)

    # Compute the fraction of pixels where the max difference exceeds the threshold
    fraction_colored = np.mean(max_diff > diff_threshold)

    # Determine if the image meets or exceeds the required fraction of colored pixels
    return fraction_colored >= fraction_threshold

def calculate_modal_value(histogram, start, end):
    range_histogram = histogram[start:end+1]
    if len(range_histogram) > 0:
        modal_value = np.argmax(range_histogram) + start
        return int(round(modal_value))  # Ensure the modal value is rounded to an integer
    return 0

def analyze_image(image_path):
    try:
        with Image.open(image_path) as img:
            img_array = np.array(img)
            
            # Check if the image is grayscale or color
            if is_color_image(img_array):
                # If the image is color, skip processing
                print(f"Skipped color image: {image_path}")
                return None
            
            # Convert image to grayscale
            gray_img = img.convert('L')
            img_array_gray = np.array(gray_img)

        histogram, _ = np.histogram(img_array_gray, bins=256, range=(0, 256))

        # Calculate modal value for the range 0-55
        modal_value_0_55 = calculate_modal_value(histogram, 0, 55)

        return image_path, modal_value_0_55
    except Exception as e:
        print(f"Error processing {image_path}: {str(e)}")
        return None

def process_directory(root_directory):
    modal_values_per_subfolder = defaultdict(list)

    for dirpath, _, filenames in os.walk(root_directory):
        for file in filenames:
            if file.lower().endswith(('png', 'jpg', 'jpeg', 'bmp', 'gif')):
                image_path = os.path.join(dirpath, file)
                result = analyze_image(image_path)
                if result:
                    subfolder = os.path.relpath(dirpath, root_directory)
                    modal_values_per_subfolder[subfolder].append(result)
    
    return modal_values_per_subfolder

def calculate_mean_modal_values(modal_values_per_subfolder):
    mean_modal_values = {}
    for subfolder, modal_values in modal_values_per_subfolder.items():
        if modal_values:
            modal_value_0_55_list = [mv[1] for mv in modal_values]
            mean_modal_value_0_55 = np.mean(modal_value_0_55_list)
            mean_modal_values[subfolder] = round(mean_modal_value_0_55)  # Ensure rounding to integer
    return mean_modal_values

def find_outlier_images(modal_values_per_subfolder, mean_modal_values):
    outlier_images = defaultdict(list)
    for subfolder, modal_values in modal_values_per_subfolder.items():
        mean_modal_value = mean_modal_values[subfolder]
        for image_path, modal_value in modal_values:
            if abs(modal_value - mean_modal_value) > 3:
                outlier_images[subfolder].append((image_path, modal_value))
    return outlier_images

def copy_outlier_images(outlier_images, input_root, output_root):
    for subfolder, images in outlier_images.items():
        if images:
            output_subfolder = os.path.join(output_root, subfolder)
            os.makedirs(output_subfolder, exist_ok=True)
            
            for image_path, _ in images:
                rel_path = os.path.relpath(image_path, input_root)
                dest_path = os.path.join(output_root, rel_path)
                
                shutil.copy(image_path, dest_path)

def display_mean_modal_values_and_outliers(mean_modal_values, outlier_images):
    print("Mean Modal Values:")
    for subfolder, mean in mean_modal_values.items():
        print(f"Subfolder: {subfolder}")
        print(f"  Mean Modal Value 0-55: {mean}")

    print("\nOutlier Images:")
    for subfolder, images in outlier_images.items():
        if images:
            print(f"Subfolder: {subfolder}")
            for image_path, modal_value in images:
                print(f"  Image: {image_path}, Modal Value 0-55: {modal_value}")

def main():
    input_root_dir = r'D:\Bilder\Python\1. Input'
    output_root_dir = r'D:\Bilder\Python\2. Output'
    modal_values_per_subfolder = process_directory(input_root_dir)
    mean_modal_values = calculate_mean_modal_values(modal_values_per_subfolder)
    outlier_images = find_outlier_images(modal_values_per_subfolder, mean_modal_values)
    
    # Display results
    display_mean_modal_values_and_outliers(mean_modal_values, outlier_images)
    
    # Copy outlier images
    copy_outlier_images(outlier_images, input_root_dir, output_root_dir)

if __name__ == "__main__":
    main()