diff --git a/satpy/tests/behave/create_reference.py b/satpy/tests/behave/create_reference.py new file mode 100755 index 0000000000..de2929ee13 --- /dev/null +++ b/satpy/tests/behave/create_reference.py @@ -0,0 +1,30 @@ +from dask.diagnostics import ProgressBar +from satpy import Scene +from glob import glob +import os +import warnings +import dask + +ext_data_path = "/home/bildabgleich/pytroll-image-comparison-tests/data" + +os.environ['OMP_NUM_THREADS'] = os.environ['MKL_NUM_THREADS'] = '2' +os.environ['PYTROLL_CHUNK_SIZE'] = '1024' +warnings.simplefilter('ignore') +dask.config.set(scheduler='threads', num_workers=4) + +# Get the list of satellite files to open +satellite = "GOES17" +filenames = glob(f'{ext_data_path}/satellite_data/{satellite}/*.nc') + +scn = Scene(reader='abi_l1b', filenames=filenames) + +# what composites Satpy knows how to make and that it has the inputs for? +print(scn.available_composite_names()) + +composite = 'ash' +scn.load([composite]) +with ProgressBar(): + scn.save_datasets(writer='simple_image', filename=f'./features/data/reference/reference_image_{satellite}_{composite}.png') + + + diff --git a/satpy/tests/behave/features/data/reference/reference_image_GOES16_airmass.png b/satpy/tests/behave/features/data/reference/reference_image_GOES16_airmass.png new file mode 100644 index 0000000000..4d88c1f152 Binary files /dev/null and b/satpy/tests/behave/features/data/reference/reference_image_GOES16_airmass.png differ diff --git a/satpy/tests/behave/features/data/reference/reference_image_GOES16_ash.png b/satpy/tests/behave/features/data/reference/reference_image_GOES16_ash.png new file mode 100644 index 0000000000..32cb4619dc Binary files /dev/null and b/satpy/tests/behave/features/data/reference/reference_image_GOES16_ash.png differ diff --git a/satpy/tests/behave/features/data/reference/reference_image_GOES17_airmass.png b/satpy/tests/behave/features/data/reference/reference_image_GOES17_airmass.png new file mode 100755 index 0000000000..f5d90da452 Binary files /dev/null and b/satpy/tests/behave/features/data/reference/reference_image_GOES17_airmass.png differ diff --git a/satpy/tests/behave/features/data/reference/reference_image_GOES17_ash.png b/satpy/tests/behave/features/data/reference/reference_image_GOES17_ash.png new file mode 100644 index 0000000000..7c365460c3 Binary files /dev/null and b/satpy/tests/behave/features/data/reference/reference_image_GOES17_ash.png differ diff --git a/satpy/tests/behave/features/data/reference_different/reference_image_GOES16_airmass.png b/satpy/tests/behave/features/data/reference_different/reference_image_GOES16_airmass.png new file mode 100755 index 0000000000..6608295320 Binary files /dev/null and b/satpy/tests/behave/features/data/reference_different/reference_image_GOES16_airmass.png differ diff --git a/satpy/tests/behave/features/data/reference_different/reference_image_GOES16_ash.png b/satpy/tests/behave/features/data/reference_different/reference_image_GOES16_ash.png new file mode 100644 index 0000000000..bf900cbe09 Binary files /dev/null and b/satpy/tests/behave/features/data/reference_different/reference_image_GOES16_ash.png differ diff --git a/satpy/tests/behave/features/data/reference_different/reference_image_GOES17_airmass.png b/satpy/tests/behave/features/data/reference_different/reference_image_GOES17_airmass.png new file mode 100755 index 0000000000..7503c91754 Binary files /dev/null and b/satpy/tests/behave/features/data/reference_different/reference_image_GOES17_airmass.png differ diff --git a/satpy/tests/behave/features/data/reference_different/reference_image_GOES17_ash.png b/satpy/tests/behave/features/data/reference_different/reference_image_GOES17_ash.png new file mode 100644 index 0000000000..7f1645f6ed Binary files /dev/null and b/satpy/tests/behave/features/data/reference_different/reference_image_GOES17_ash.png differ diff --git a/satpy/tests/behave/features/image_comparison.feature b/satpy/tests/behave/features/image_comparison.feature new file mode 100755 index 0000000000..23bd550e6d --- /dev/null +++ b/satpy/tests/behave/features/image_comparison.feature @@ -0,0 +1,13 @@ +Feature: Image Comparison + + Scenario Outline: Compare generated image with reference image + Given I have a reference image file from + When I generate a new image file from + Then the generated image should be the same as the reference image + + Examples: + |satellite |composite | + |GOES17 |airmass | + |GOES16 |airmass | + |GOES16 |ash | + |GOES17 |ash | \ No newline at end of file diff --git a/satpy/tests/behave/features/steps/image_comparison.py b/satpy/tests/behave/features/steps/image_comparison.py new file mode 100755 index 0000000000..04dcedc736 --- /dev/null +++ b/satpy/tests/behave/features/steps/image_comparison.py @@ -0,0 +1,102 @@ +import os +import warnings +from glob import glob +from PIL import Image +import cv2 +import dask +import numpy as np +from behave import given, when, then +from satpy import Scene +from datetime import datetime +import pytz + +ext_data_path = "/app/ext_data" +#ext_data_path = "/home/bildabgleich/pytroll-image-comparison-tests/data" +threshold = 2000 + +# Define a before_all hook to create the timestamp and test results directory +def before_all(context): + berlin_time = datetime.now(pytz.timezone('Europe/Berlin')) + context.timestamp = berlin_time.strftime("%Y-%m-%d-%H-%M-%S") + context.test_results_dir = f"{ext_data_path}/test_results/image_comparison/{context.timestamp}" + os.makedirs(os.path.join(context.test_results_dir, 'generated'), exist_ok=True) + os.makedirs(os.path.join(context.test_results_dir, 'difference'), exist_ok=True) + + # Write the timestamp to test_results.txt + results_file = os.path.join(context.test_results_dir, 'test_results.txt') + with open(results_file, 'a') as f: + f.write(f"Test executed at {context.timestamp}.\n\n") + +# Register the before_all hook +def setup_hooks(): + from behave import use_fixture + from behave.runner import Context + + use_fixture(before_all, Context) + +setup_hooks() +@given('I have a {composite} reference image file from {satellite}') +def step_given_reference_image(context, composite, satellite): + reference_image = f"reference_image_{satellite}_{composite}.png" + context.reference_image = cv2.imread(f"./features/data/reference/{reference_image}") + context.reference_different_image = cv2.imread(f"./features/data/reference_different/{reference_image}") + context.satellite = satellite + context.composite = composite + + +@when('I generate a new {composite} image file from {satellite}') +def step_when_generate_image(context, composite, satellite): + os.environ['OMP_NUM_THREADS'] = os.environ['MKL_NUM_THREADS'] = '2' + os.environ['PYTROLL_CHUNK_SIZE'] = '1024' + warnings.simplefilter('ignore') + dask.config.set(scheduler='threads', num_workers=4) + + # Get the list of satellite files to open + filenames = glob(f'{ext_data_path}/satellite_data/{satellite}/*.nc') + + scn = Scene(reader='abi_l1b', filenames=filenames) + + scn.load([composite]) + + # Save the generated image in the generated folder + generated_image_path = os.path.join(context.test_results_dir, 'generated', + f'generated_{context.satellite}_{context.composite}.png') + scn.save_datasets(writer='simple_image', filename=generated_image_path) + + # Save the generated image in the context + context.generated_image = cv2.imread(generated_image_path) + + +@then('the generated image should be the same as the reference image') +def step_then_compare_images(context): + # Load the images + imageA = cv2.cvtColor(context.reference_image, cv2.COLOR_BGR2GRAY) # reference_different_image for testing only + imageB = cv2.cvtColor(context.generated_image, cv2.COLOR_BGR2GRAY) + # Ensure both images have the same dimensions + if imageA.shape != imageB.shape: + raise ValueError("Both images must have the same dimensions") + array1 = np.array(imageA) + array2 = np.array(imageB) + # Perform pixel-wise comparison + result_matrix = (array1 != array2).astype(np.uint8) * 255 + + # Save the resulting numpy array as an image in the difference folder + diff_image_path = os.path.join(context.test_results_dir, 'difference', + f'diff_{context.satellite}_{context.composite}.png') + cv2.imwrite(diff_image_path, result_matrix) + + # Count non-zero pixels in the result matrix + non_zero_count = np.count_nonzero(result_matrix) + + # Write the results to a file in the test results directory + results_file = os.path.join(context.test_results_dir, 'test_results.txt') + with open(results_file, 'a') as f: + f.write(f"Test for {context.satellite} - {context.composite}\n") + f.write(f"Non-zero pixel differences: {non_zero_count}\n") + if non_zero_count < threshold: + f.write(f"Result: Passed - {non_zero_count} pixel differences.\n\n") + else: + f.write(f"Result: Failed - {non_zero_count} pixel differences exceed the threshold of {threshold}.\n\n") + + # Assert that the number of differences is below the threshold + assert non_zero_count < threshold, f"Images are not similar enough. {non_zero_count} pixel differences exceed the threshold of {threshold}." diff --git a/satpy/tests/behave/modify_image.py b/satpy/tests/behave/modify_image.py new file mode 100755 index 0000000000..a4252db928 --- /dev/null +++ b/satpy/tests/behave/modify_image.py @@ -0,0 +1,33 @@ +from PIL import Image, ImageDraw, ImageFont +import os + +def add_text_to_image(input_path, output_path, text, position=(800, 2200), font_size=700, font_color=(255, 255, 255)): + # Open the image + image = Image.open(input_path) + + # Create a drawing object + draw = ImageDraw.Draw(image) + + # Load a font + font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" + if os.path.exists(font_path): + font = ImageFont.truetype(font_path, font_size) + else: + print("DejaVuSans not found, using default font (fixed size)") + font = ImageFont.load_default() + + draw.text(position, text, font=font, fill=font_color) + + # Save the modified image + image.save(output_path) + + +# Example usage +satellite = "GOES16" +composite = "ash" +reference_image = f"reference_image_{satellite}_{composite}.png" +input_image_path = f"./features/data/reference/{reference_image}" +output_image_path = f"./features/data/reference_different/{reference_image}" +text_to_add = 'Hello, World!' + +add_text_to_image(input_image_path, output_image_path, text_to_add)