Write a python programm that resizes images. The program should go through D:\Bilder\Python\1. Input and its subfolders and save all processed images in D:\Bilder\Python\2. Output keeping the same folder structure. Use the following implementations: 

To achieve the highest quality possible for downscaling and upscaling images in Python using Pillow and NumPy, we need to carefully consider colorspace corrections and the choice of resampling filters.

Below, I'll provide implementations for both downscaling and upscaling, incorporating the best practices from the provided ImageMagick documentation.
Overview

    Downscaling (Reducing Image Size):
        Convert the image from sRGB to linear RGB (undo gamma compression).
        Perform the resize operation using a high-quality resampling filter (Lanczos).
        Convert the image back from linear RGB to sRGB (apply gamma compression).

    Upscaling (Increasing Image Size):
        Resize the image directly in the sRGB colorspace using a high-quality resampling filter (Lanczos).
        Optional: Apply slight sharpening to enhance details.

Implementation Details
Required Libraries

from PIL import Image
import numpy as np

sRGB and Linear RGB Conversion Functions

We'll define functions to convert between sRGB and linear RGB. The sRGB color space uses a non-linear transformation (gamma correction), which we need to consider for accurate image processing.
sRGB to Linear RGB Conversion

def srgb_to_linear(srgb):
    srgb = srgb / 255.0  # Normalize to [0, 1]
    linear = np.where(
        srgb <= 0.04045,
        srgb / 12.92,
        ((srgb + 0.055) / 1.055) ** 2.4
    )
    return linear

Linear RGB to sRGB Conversion

def linear_to_srgb(linear):
    srgb = np.where(
        linear <= 0.0031308,
        linear * 12.92,
        1.055 * (linear ** (1 / 2.4)) - 0.055
    )
    srgb = np.clip(srgb * 255.0, 0, 255).astype('uint8')
    return srgb

Downscaling Function

def high_quality_downscale(input_image_path, output_image_path, new_size):
    # Open the original image
    img = Image.open(input_image_path)
    
    # Convert image to NumPy array
    img_array = np.array(img).astype('float32')
    
    # Check if the image has an alpha channel
    if img_array.shape[2] == 4:
        rgb = img_array[..., :3]
        alpha = img_array[..., 3]
    else:
        rgb = img_array
        alpha = None
    
    # Convert from sRGB to linear RGB
    linear_rgb = srgb_to_linear(rgb)
    
    # Convert linear RGB back to PIL image
    linear_img = Image.fromarray((linear_rgb * 255).astype('float32').round().astype('uint8'))
    
    # Perform the resize operation using Lanczos filter
    linear_resized = linear_img.resize(new_size, resample=Image.LANCZOS)
    
    # Convert back to NumPy array
    linear_resized_array = np.array(linear_resized).astype('float32') / 255.0
    
    # Convert from linear RGB back to sRGB
    srgb_resized = linear_to_srgb(linear_resized_array)
    
    # If there was an alpha channel, resize it separately and reattach
    if alpha is not None:
        alpha_img = Image.fromarray(alpha.astype('uint8'))
        alpha_resized = alpha_img.resize(new_size, resample=Image.LANCZOS)
        alpha_resized_array = np.array(alpha_resized)
        # Combine RGB and alpha channels
        srgb_resized = np.dstack((srgb_resized, alpha_resized_array))
    
    # Convert NumPy array back to PIL Image and save
    result_img = Image.fromarray(srgb_resized)
    result_img.save(output_image_path)

Usage Example

input_image_path = 'input_image.png'
output_image_path = 'downscaled_image.png'
new_size = (800, 600)  # Desired size

high_quality_downscale(input_image_path, output_image_path, new_size)

Upscaling Function

For upscaling, the recommendation is to process the image in the sRGB colorspace directly without linearization.

def high_quality_upscale(input_image_path, output_image_path, new_size):
    # Open the original image
    img = Image.open(input_image_path)
    
    # Perform the resize operation using Lanczos filter
    upscaled_img = img.resize(new_size, resample=Image.LANCZOS)
    
    # Optional: Apply slight sharpening using an unsharp mask
    upscaled_img = upscaled_img.filter(Image.Filter.UnsharpMask(radius=2, percent=150, threshold=3))
    
    # Save the result
    upscaled_img.save(output_image_path)

Usage Example

input_image_path = 'input_image.png'
output_image_path = 'upscaled_image.png'
new_size = (1600, 1200)  # Desired size

high_quality_upscale(input_image_path, output_image_path, new_size)

Explanation
Downscaling

    Colorspace Conversion:
        Converting to linear RGB ensures that the interpolation performed during resizing is mathematically correct for luminance data. This avoids gamma-related artifacts such as darkening or loss of detail in shadows and highlights.
    Resampling Filter:
        The Lanczos filter is a high-quality resampling filter that provides better results than simpler filters like nearest-neighbor or bilinear, especially when reducing image size.
    Alpha Channel Handling:
        If the image has transparency, we resize the alpha channel separately to maintain the integrity of transparent areas.

Upscaling

    Colorspace:
        We keep the image in the sRGB colorspace because upscaling in linear RGB can introduce undesirable artifacts and reduce image quality.
    Resampling Filter:
        The Lanczos filter helps in producing smoother gradients and sharper edges when enlarging images.
    Optional Sharpening:
        Upscaling can sometimes make images appear slightly blurry. Applying an unsharp mask can enhance edge contrast and improve perceived sharpness.

Additional Considerations

    Color Profiles:
        Ensure that color profiles are preserved when opening and saving images. Pillow handles basic profiles, but for complex profiles, additional handling might be necessary.
    File Formats:
        When working with formats like JPEG, be aware of compression artifacts. Consider using formats like PNG when quality is paramount.
    Dynamic Range:
        If working with high dynamic range images (e.g., 16-bit per channel), you may need to adjust the code to handle different data types.

Conclusion

By incorporating colorspace corrections and using high-quality resampling filters, we can significantly improve the quality of downscaled and upscaled images. The provided implementations follow the best practices outlined in the ImageMagick documentation and adapt them to Python using Pillow and NumPy.
References

    ImageMagick Resize Documentation:
        Provides in-depth explanations of various resampling filters and their effects on image quality.
    Gamma Error in Picture Scaling (by Eric Brasseur):
        Discusses the importance of gamma correction when resizing images.
    Pillow Documentation:
        Official documentation for the Python Imaging Library (PIL) fork, which provides extensive image processing capabilities.