From 352c50edacfde161c0af2a5a21de08d21e1471cc Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Thu, 2 May 2024 09:21:49 +0100 Subject: [PATCH 01/31] removing the RC from the version in prep for v1 --- src/open_vp_cal/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/open_vp_cal/__init__.py b/src/open_vp_cal/__init__.py index 0e41bd4..09a02e2 100644 --- a/src/open_vp_cal/__init__.py +++ b/src/open_vp_cal/__init__.py @@ -15,7 +15,7 @@ Init Module defines a few module level variables """ -__version__ = "1.0.0-rc.12" +__version__ = "1.0.0" __authors__ = [ "Adam Davis", "Adrian Pueyo", "Carol Payne", "Francesco Luigi Giardiello", "Daniel Heckenberg" ] From 8758ac356abab67f72b61e8bf5fd1fe1b6a229a8 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Thu, 2 May 2024 11:53:38 +0100 Subject: [PATCH 02/31] fixing issue where input plate gamut had been removed by accident --- requirements.txt | 2 +- src/open_vp_cal/widgets/project_settings_widget.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 20bc4da..27b6fc5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ numpy==1.26.2 opencolorio==2.3.1 opencv-python==4.8.1.78 packaging==23.2 -Pillow==10.3.0 +pillow==10.3.0 platformdirs==4.1.0 pluggy==1.3.0 pycodestyle==2.11.1 diff --git a/src/open_vp_cal/widgets/project_settings_widget.py b/src/open_vp_cal/widgets/project_settings_widget.py index 66f6737..1d23bf9 100644 --- a/src/open_vp_cal/widgets/project_settings_widget.py +++ b/src/open_vp_cal/widgets/project_settings_widget.py @@ -84,7 +84,7 @@ def refresh_default_data(self): """ Refreshes the default data dictionary with the current default values from the LedWallSettings class """ - target_gamut_options = constants.ColourSpace.CS_ALL + target_gamut_options = constants.ColourSpace.CS_ALL.copy() target_gamut_options.pop(target_gamut_options.index(constants.ColourSpace.CS_ACES)) target_gamut_options.extend(self.project_custom_primaries.keys()) default_led_wall = LedWallSettings(self, constants.DEFAULT) From c733ce15a2ff88c2dc117a44f7d385fc9538d5ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 22:00:36 +0000 Subject: [PATCH 03/31] Bump jinja2 from 3.1.3 to 3.1.4 Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4) --- updated-dependencies: - dependency-name: jinja2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 20bc4da..df811ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ imagesize==1.4.1 importlib-resources==6.1.1 iniconfig==2.0.0 isort==5.13.2 -Jinja2==3.1.3 +Jinja2==3.1.4 kiwisolver==1.4.5 lazy-object-proxy==1.9.0 macholib==1.16.3 From f2b5e1fd67616c83acd77287d5353d6e2ec6a824 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Wed, 15 May 2024 19:34:01 -0700 Subject: [PATCH 04/31] Renaming External White to White Point Offset --- src/open_vp_cal/application_base.py | 6 +- src/open_vp_cal/core/constants.py | 8 +-- src/open_vp_cal/framework/processing.py | 16 ++--- src/open_vp_cal/led_wall_settings.py | 41 ++++++------ .../widgets/project_settings_widget.py | 62 +++++++++---------- .../widgets/swatch_analysis_widget.py | 2 +- .../project_settings.json | 4 +- .../project_settings.json | 8 +-- .../test_led_wall_settings.py | 22 +++---- .../test_open_vp_cal/test_project_settings.py | 4 +- tests/test_open_vp_cal/test_projects.py | 8 +-- tests/test_open_vp_cal/test_settings.json | 4 +- 12 files changed, 92 insertions(+), 93 deletions(-) diff --git a/src/open_vp_cal/application_base.py b/src/open_vp_cal/application_base.py index a4f7804..fcb3f71 100644 --- a/src/open_vp_cal/application_base.py +++ b/src/open_vp_cal/application_base.py @@ -139,12 +139,12 @@ def run_pre_checks(self, led_walls: List[LedWallSettings]) -> bool: self.error_message(message) return False - if led_wall.use_external_white_point: - if not led_wall.external_white_point_file: + if led_wall.use_white_point_offset: + if not led_wall.white_point_offset_source: self.error_message(f"External White Point Enabled But File Not Set {led_wall.name}") return False - if not os.path.exists(led_wall.external_white_point_file): + if not os.path.exists(led_wall.white_point_offset_source): self.error_message(f"External White Point File Set Does Not Exist {led_wall.name}") return False diff --git a/src/open_vp_cal/core/constants.py b/src/open_vp_cal/core/constants.py index c58d542..de205f0 100644 --- a/src/open_vp_cal/core/constants.py +++ b/src/open_vp_cal/core/constants.py @@ -141,15 +141,15 @@ class LedWallSettingsKeys: TARGET_TO_SCREEN_CAT = "target_to_screen_cat" MATCH_REFERENCE_WALL = "match_reference_wall" REFERENCE_WALL = "reference_wall" - EXTERNAL_WHITE_POINT_FILE = "external_white_point_file" - USE_EXTERNAL_WHITE_POINT = "use_external_white_point" + WHITE_POINT_OFFSET_SOURCE = "white_point_offset_source" + USE_WHITE_POINT_OFFSET = "use_white_point_offset" IS_VERIFICATION_WALL = "is_verification_wall" VERIFICATION_WALL = "verification_wall" ALL = [NAME, ENABLE_EOTF_CORRECTION, ENABLE_GAMUT_COMPRESSION, AUTO_WB_SOURCE, INPUT_SEQUENCE_FOLDER, NUM_GREY_PATCHES, PRIMARIES_SATURATION, CALCULATION_ORDER, INPUT_PLATE_GAMUT, NATIVE_CAMERA_GAMUT, REFERENCE_TO_TARGET_CAT, ROI, SHADOW_ROLLOFF, TARGET_MAX_LUM_NITS, TARGET_GAMUT, TARGET_EOTF, - TARGET_TO_SCREEN_CAT, MATCH_REFERENCE_WALL, REFERENCE_WALL, USE_EXTERNAL_WHITE_POINT, - EXTERNAL_WHITE_POINT_FILE, VERIFICATION_WALL, IS_VERIFICATION_WALL, AVOID_CLIPPING] + TARGET_TO_SCREEN_CAT, MATCH_REFERENCE_WALL, REFERENCE_WALL, USE_WHITE_POINT_OFFSET, + WHITE_POINT_OFFSET_SOURCE, VERIFICATION_WALL, IS_VERIFICATION_WALL, AVOID_CLIPPING] class PATCHES: diff --git a/src/open_vp_cal/framework/processing.py b/src/open_vp_cal/framework/processing.py index 75ddf0c..ee4193c 100644 --- a/src/open_vp_cal/framework/processing.py +++ b/src/open_vp_cal/framework/processing.py @@ -162,8 +162,8 @@ def analyse(self): target_cs, target_to_screen_cat, native_camera_cs = self._analysis_prep() reference_wall_external_white_balance_matrix = None - if self.led_wall.match_reference_wall and self.led_wall.use_external_white_point: - raise ValueError("Cannot use external white point and a reference wall") + if self.led_wall.match_reference_wall and self.led_wall.use_white_point_offset: + raise ValueError("Cannot use white point offset and a reference wall") if self.led_wall.match_reference_wall: if self.led_wall.reference_wall_as_wall: @@ -174,9 +174,9 @@ def analyse(self): Results.WHITE_BALANCE_MATRIX] decoupled_lens_white_samples = None - if self.led_wall.use_external_white_point: + if self.led_wall.use_white_point_offset: decoupled_lens_white_samples = imaging_utils.get_decoupled_white_samples_from_file( - self.led_wall.external_white_point_file) + self.led_wall.white_point_offset_source) default_wall = LedWallSettings("default") @@ -227,8 +227,8 @@ def calibrate(self) -> ProcessingResults: target_cs, target_to_screen_cat, native_camera_cs = self._analysis_prep() reference_wall_external_white_balance_matrix = None - if self.led_wall.match_reference_wall and self.led_wall.use_external_white_point: - raise ValueError("Cannot use external white point and a reference wall") + if self.led_wall.match_reference_wall and self.led_wall.use_white_point_offset: + raise ValueError("Cannot use white point offset and a reference wall") if self.led_wall.match_reference_wall: if self.led_wall.reference_wall_as_wall: @@ -239,9 +239,9 @@ def calibrate(self) -> ProcessingResults: Results.WHITE_BALANCE_MATRIX] decoupled_lens_white_samples = None - if self.led_wall.use_external_white_point: + if self.led_wall.use_white_point_offset: decoupled_lens_white_samples = imaging_utils.get_decoupled_white_samples_from_file( - self.led_wall.external_white_point_file) + self.led_wall.white_point_offset_source) calibration_results = calibrate.run( measured_samples=self.led_wall.processing_results.samples, diff --git a/src/open_vp_cal/led_wall_settings.py b/src/open_vp_cal/led_wall_settings.py index 4a645e7..5e7fee5 100644 --- a/src/open_vp_cal/led_wall_settings.py +++ b/src/open_vp_cal/led_wall_settings.py @@ -56,8 +56,8 @@ def __init__(self, project_settings: "ProjectSettings", name="Wall1"): constants.LedWallSettingsKeys.TARGET_TO_SCREEN_CAT: constants.CAT.CAT_NONE, constants.LedWallSettingsKeys.MATCH_REFERENCE_WALL: False, constants.LedWallSettingsKeys.REFERENCE_WALL: "", - constants.LedWallSettingsKeys.USE_EXTERNAL_WHITE_POINT: False, - constants.LedWallSettingsKeys.EXTERNAL_WHITE_POINT_FILE: "", + constants.LedWallSettingsKeys.USE_WHITE_POINT_OFFSET: False, + constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE: "", constants.LedWallSettingsKeys.IS_VERIFICATION_WALL: False, constants.LedWallSettingsKeys.VERIFICATION_WALL: "", constants.LedWallSettingsKeys.AVOID_CLIPPING: True @@ -583,41 +583,40 @@ def reference_wall(self, value: Union["LedWallSettings", str]): self._set_property(constants.LedWallSettingsKeys.REFERENCE_WALL, led_wall.name) @property - def use_external_white_point(self) -> bool: - """ Whether we are using an external white point for the LED wall or not + def use_white_point_offset(self) -> bool: + """ Whether we are using a white point offset for the LED wall or not Returns: - bool: Gets whether we want to use an external white point or not + bool: Gets whether we want to use a white point offset or not """ - return self._led_settings[constants.LedWallSettingsKeys.USE_EXTERNAL_WHITE_POINT] + return self._led_settings[constants.LedWallSettingsKeys.USE_WHITE_POINT_OFFSET] - @use_external_white_point.setter - def use_external_white_point(self, value: bool): - """ Set whether we are using an external white point or not + @use_white_point_offset.setter + def use_white_point_offset(self, value: bool): + """ Set whether we are using a white point offset or not Args: value (bool): Whether to use the external white point or not """ - self._set_property(constants.LedWallSettingsKeys.USE_EXTERNAL_WHITE_POINT, value) + self._set_property(constants.LedWallSettingsKeys.USE_WHITE_POINT_OFFSET, value) @property - def external_white_point_file(self) -> str: - """ The file which contains an image sample from which we want to calculate the external white point from + def white_point_offset_source(self) -> str: + """ The source which contains an image sample from which we want to calculate the white point offset from Returns: - str: The filepath which contains the image we want to sample to calculate the external white point from + str: The filepath which contains the image we want to sample to calculate the white point offset from """ - return self._led_settings[constants.LedWallSettingsKeys.EXTERNAL_WHITE_POINT_FILE] + return self._led_settings[constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE] - @external_white_point_file.setter - def external_white_point_file(self, value: str): - """ Set the file which contains an image sample from which we want to calculate the external white point from + @white_point_offset_source.setter + def white_point_offset_source(self, value: str): + """ Set the source which contains an image sample from which we want to calculate the white point offset from Args: - value (str): The filepath which contains the image we want to sample to calculate the external - white point from + value (str): The filepath which contains the image we want to sample to calculate the white point offset from """ - self._set_property(constants.LedWallSettingsKeys.EXTERNAL_WHITE_POINT_FILE, value) + self._set_property(constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE, value) @property def verification_wall(self) -> str: @@ -694,7 +693,7 @@ def has_valid_white_balance_options(self) -> bool: Returns: True or False depending on whether the white balance options are valid or not """ - values = [self.auto_wb_source, self.match_reference_wall, self.use_external_white_point].count(True) + values = [self.auto_wb_source, self.match_reference_wall, self.use_white_point_offset].count(True) if values > 1: return False return True diff --git a/src/open_vp_cal/widgets/project_settings_widget.py b/src/open_vp_cal/widgets/project_settings_widget.py index 1d23bf9..ac2ef63 100644 --- a/src/open_vp_cal/widgets/project_settings_widget.py +++ b/src/open_vp_cal/widgets/project_settings_widget.py @@ -692,9 +692,9 @@ def __init__(self, parent=None): self.match_reference_wall = QCheckBox() self.reference_wall = QComboBox() self.auto_wb_source = QCheckBox() - self.external_white_point_file_button = None - self.use_external_white_point = QCheckBox() - self.external_white_point_file = QLineEdit() + self.white_point_offset_source_button = None + self.use_white_point_offset = QCheckBox() + self.white_point_offset_source = QLineEdit() self.init_ui() def init_ui(self): @@ -722,17 +722,17 @@ def init_ui(self): reference_settings_group.setLayout(reference_settings_layout) main_layout.addWidget(reference_settings_group) - self.external_white_point_file_button = QPushButton("Browse") - external_white_point_file_layout = QHBoxLayout() - external_white_point_file_layout.addWidget(self.external_white_point_file) - external_white_point_file_layout.addWidget(self.external_white_point_file_button) + self.white_point_offset_source_button = QPushButton("Browse") + white_point_offset_source_layout = QHBoxLayout() + white_point_offset_source_layout.addWidget(self.white_point_offset_source) + white_point_offset_source_layout.addWidget(self.white_point_offset_source_button) - external_white_point_group = QGroupBox("External White Point") - external_white_point_layout = QFormLayout() - external_white_point_layout.addRow(QLabel("Use External White Point:"), self.use_external_white_point) - external_white_point_layout.addRow("External White Point File:", external_white_point_file_layout) - external_white_point_group.setLayout(external_white_point_layout) - main_layout.addWidget(external_white_point_group) + white_point_offset_group = QGroupBox("White Point Offset") + white_point_layout = QFormLayout() + white_point_layout.addRow(QLabel("Use White Point Offset:"), self.use_white_point_offset) + white_point_layout.addRow("White Point Offset Source:", white_point_offset_source_layout) + white_point_offset_group.setLayout(white_point_layout) + main_layout.addWidget(white_point_offset_group) # Add the main widget to the scroll area scroll.setWidget(main_widget) @@ -940,9 +940,9 @@ def __init__(self, self.model = model self.reference_wall_widget = self.plate_settings_view.reference_wall self.match_reference_wall_widget = self.plate_settings_view.match_reference_wall - self.use_external_white_point_widget = self.plate_settings_view.use_external_white_point - self.external_white_point_file_widget = self.plate_settings_view.external_white_point_file - self.external_white_point_file_button_widget = self.plate_settings_view.external_white_point_file_button + self.use_white_point_offset_widget = self.plate_settings_view.use_white_point_offset + self.white_point_offset_source_widget = self.plate_settings_view.white_point_offset_source + self.white_point_offset_source_button_widget = self.plate_settings_view.white_point_offset_source_button self.reference_wall_widget.setEnabled( self.match_reference_wall_widget.checkState() == Qt.Checked @@ -1012,14 +1012,14 @@ def __init__(self, self.plate_settings_view.reference_wall.currentIndexChanged.connect( lambda: self.model.set_data( constants.LedWallSettingsKeys.REFERENCE_WALL, self.plate_settings_view.reference_wall.currentText())) - self.plate_settings_view.use_external_white_point.stateChanged.connect( + self.plate_settings_view.use_white_point_offset.stateChanged.connect( lambda: self.model.set_data( - constants.LedWallSettingsKeys.USE_EXTERNAL_WHITE_POINT, - self.plate_settings_view.use_external_white_point.isChecked())) - self.plate_settings_view.external_white_point_file.textChanged.connect( + constants.LedWallSettingsKeys.USE_WHITE_POINT_OFFSET, + self.plate_settings_view.use_white_point_offset.isChecked())) + self.plate_settings_view.white_point_offset_source.textChanged.connect( lambda: self.model.set_data( - constants.LedWallSettingsKeys.EXTERNAL_WHITE_POINT_FILE, - self.plate_settings_view.external_white_point_file.text())) + constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE, + self.plate_settings_view.white_point_offset_source.text())) self.led_analysis_settings_view.target_to_screen_cat.currentIndexChanged.connect( lambda: self.model.set_data(constants.LedWallSettingsKeys.TARGET_TO_SCREEN_CAT, self.led_analysis_settings_view.target_to_screen_cat.currentText())) @@ -1048,8 +1048,8 @@ def __init__(self, self.led_settings_view.gamut_dialog_button.clicked.connect(self.open_custom_gamut_dialog) self.project_settings_view.output_folder_button.clicked.connect(self.open_folder_select_dialog) self.project_settings_view.custom_logo_button.clicked.connect(self.open_logo_select_dialog) - self.plate_settings_view.external_white_point_file_button.clicked.connect( - self.open_external_white_point_file_dialog) + self.plate_settings_view.white_point_offset_source_button.clicked.connect( + self.open_white_point_source_dialog) self.match_reference_wall_widget.stateChanged.connect(self.on_match_reference_wall_changed) self.model.led_wall_removed.connect(self.on_led_wall_removed) @@ -1130,10 +1130,10 @@ def highlight_invalid_settings(self, led_wall_name: str) -> None: self.plate_settings_view.auto_wb_source.setStyleSheet(style_sheet if led_wall.auto_wb_source else "") self.match_reference_wall_widget.setStyleSheet(style_sheet if led_wall.match_reference_wall else "") - self.use_external_white_point_widget.setStyleSheet(style_sheet if led_wall.use_external_white_point else "") - self.external_white_point_file_widget.setStyleSheet(style_sheet if led_wall.use_external_white_point else "") - self.external_white_point_file_button_widget.setStyleSheet( - style_sheet if led_wall.use_external_white_point else "") + self.use_white_point_offset_widget.setStyleSheet(style_sheet if led_wall.use_white_point_offset else "") + self.white_point_offset_source_widget.setStyleSheet(style_sheet if led_wall.use_white_point_offset else "") + self.white_point_offset_source_button_widget.setStyleSheet( + style_sheet if led_wall.use_white_point_offset else "") if led_wall.match_reference_wall and not led_wall.reference_wall: self.reference_wall_widget.setStyleSheet(error_style_sheet) @@ -1151,7 +1151,7 @@ def open_logo_select_dialog(self) -> None: return self.model.set_data(constants.ProjectSettingsKeys.CUSTOM_LOGO_PATH, filename) - def open_external_white_point_file_dialog(self)-> None: + def open_white_point_source_dialog(self)-> None: """ Opens a file dialogue to select an image to use as a reference to an external white point analysis, and sets the path in the model """ @@ -1161,8 +1161,8 @@ def open_external_white_point_file_dialog(self)-> None: ) if not filename: return - self.model.set_data(constants.LedWallSettingsKeys.USE_EXTERNAL_WHITE_POINT, True) - self.model.set_data(constants.LedWallSettingsKeys.EXTERNAL_WHITE_POINT_FILE, filename) + self.model.set_data(constants.LedWallSettingsKeys.USE_WHITE_POINT_OFFSET, True) + self.model.set_data(constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE, filename) def open_folder_select_dialog(self) -> None: """ diff --git a/src/open_vp_cal/widgets/swatch_analysis_widget.py b/src/open_vp_cal/widgets/swatch_analysis_widget.py index 950f90f..cd507ca 100644 --- a/src/open_vp_cal/widgets/swatch_analysis_widget.py +++ b/src/open_vp_cal/widgets/swatch_analysis_widget.py @@ -227,7 +227,7 @@ def update_exposure(self) -> None: preview_calibration = self.preview_calibration_checkbox.isChecked() for led_wall in self.led_walls: if (not led_wall.auto_wb_source and not led_wall.match_reference_wall - and not led_wall.use_external_white_point): + and not led_wall.use_white_point_offset): apply_white_balance_checked = False else: apply_white_balance_checked = self.apply_white_balance_checkbox.isChecked() diff --git a/tests/test_open_vp_cal/resources/Sample_Project5_Decoupled_Lens/project_settings.json b/tests/test_open_vp_cal/resources/Sample_Project5_Decoupled_Lens/project_settings.json index 50daff3..129eb01 100644 --- a/tests/test_open_vp_cal/resources/Sample_Project5_Decoupled_Lens/project_settings.json +++ b/tests/test_open_vp_cal/resources/Sample_Project5_Decoupled_Lens/project_settings.json @@ -37,8 +37,8 @@ "target_to_screen_cat": "None", "match_reference_wall": false, "reference_wall": "", - "use_external_white_point": true, - "external_white_point_file": "/Users/adamdavis/dev/workspace/git/netflix-skunkworks/OpenVPCal/tests/resources/Plates/A104_C003_11080B_001.R3D/A104_C003_11080B_001.01397369.exr", + "use_white_point_offset": true, + "white_point_offset_source": "/Users/adamdavis/dev/workspace/git/netflix-skunkworks/OpenVPCal/tests/resources/Plates/A104_C003_11080B_001.R3D/A104_C003_11080B_001.01397369.exr", "is_verification_wall": false, "verification_wall": "", "avoid_clipping": false diff --git a/tests/test_open_vp_cal/resources/Sample_Project6_Reference_Wall_With_Decoupled_Lens/project_settings.json b/tests/test_open_vp_cal/resources/Sample_Project6_Reference_Wall_With_Decoupled_Lens/project_settings.json index 0e391bd..f6a990a 100644 --- a/tests/test_open_vp_cal/resources/Sample_Project6_Reference_Wall_With_Decoupled_Lens/project_settings.json +++ b/tests/test_open_vp_cal/resources/Sample_Project6_Reference_Wall_With_Decoupled_Lens/project_settings.json @@ -37,8 +37,8 @@ "target_to_screen_cat": "None", "match_reference_wall": true, "reference_wall": "ROE_DECOWP", - "use_external_white_point": false, - "external_white_point_file": "", + "use_white_point_offset": false, + "white_point_offset_source": "", "is_verification_wall": false, "verification_wall": "", "avoid_clipping": false @@ -68,8 +68,8 @@ "target_to_screen_cat": "None", "match_reference_wall": false, "reference_wall": "", - "use_external_white_point": true, - "external_white_point_file": "/Users/adamdavis/dev/workspace/git/netflix-skunkworks/OpenVPCal/tests/resources/Plates/A104_C003_11080B_001.R3D/A104_C003_11080B_001.01397369.exr", + "use_white_point_offset": true, + "white_point_offset_source": "/Users/adamdavis/dev/workspace/git/netflix-skunkworks/OpenVPCal/tests/resources/Plates/A104_C003_11080B_001.R3D/A104_C003_11080B_001.01397369.exr", "is_verification_wall": false, "verification_wall": "", "avoid_clipping": false diff --git a/tests/test_open_vp_cal/test_led_wall_settings.py b/tests/test_open_vp_cal/test_led_wall_settings.py index 8eb8303..1e34eb6 100644 --- a/tests/test_open_vp_cal/test_led_wall_settings.py +++ b/tests/test_open_vp_cal/test_led_wall_settings.py @@ -51,8 +51,8 @@ def setUp(self): constants.LedWallSettingsKeys.NATIVE_CAMERA_GAMUT: constants.CameraColourSpace.RED_WIDE_GAMUT, constants.LedWallSettingsKeys.MATCH_REFERENCE_WALL: False, constants.LedWallSettingsKeys.REFERENCE_WALL: "", - constants.LedWallSettingsKeys.USE_EXTERNAL_WHITE_POINT: False, - constants.LedWallSettingsKeys.EXTERNAL_WHITE_POINT_FILE: "", + constants.LedWallSettingsKeys.USE_WHITE_POINT_OFFSET: False, + constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE: "", constants.LedWallSettingsKeys.IS_VERIFICATION_WALL: False, constants.LedWallSettingsKeys.VERIFICATION_WALL: "", constants.LedWallSettingsKeys.AVOID_CLIPPING: True @@ -157,12 +157,12 @@ def test_match_reference_wall(self): self.assertEqual(self.wall.match_reference_wall, True) def test_use_external_white_point(self): - self.wall.use_external_white_point = True - self.assertEqual(self.wall.use_external_white_point, True) + self.wall.use_white_point_offset = True + self.assertEqual(self.wall.use_white_point_offset, True) def test_external_white_point_file(self): - self.wall.external_white_point_file = "new_file.exr" - self.assertEqual(self.wall.external_white_point_file, "new_file.exr") + self.wall.white_point_offset_source = "new_file.exr" + self.assertEqual(self.wall.white_point_offset_source, "new_file.exr") def test_reference_wall(self): @@ -223,8 +223,8 @@ def test_verification_wall(self): "target_max_lum_nits", "target_to_screen_cat", "match_reference_wall", - "use_external_white_point", - "external_white_point_file"] + "use_white_point_offset", + "white_point_offset_source"] # We test all the other linked params with false data for count, linked_prop in enumerate(other_linked_properties): @@ -263,11 +263,11 @@ def test_remove_reference_wall(self): self.assertEqual(new_wall.match_reference_wall, False) def test_reset_defaults(self): - self.wall.use_external_white_point = True - self.assertEqual(self.wall.use_external_white_point, True) + self.wall.use_white_point_offset = True + self.assertEqual(self.wall.use_white_point_offset, True) self.wall.reset_defaults() - self.assertEqual(self.wall.use_external_white_point, False) + self.assertEqual(self.wall.use_white_point_offset, False) self.assertEqual(self.wall._default_led_settings, self.wall._led_settings) diff --git a/tests/test_open_vp_cal/test_project_settings.py b/tests/test_open_vp_cal/test_project_settings.py index 9cce643..988eae4 100644 --- a/tests/test_open_vp_cal/test_project_settings.py +++ b/tests/test_open_vp_cal/test_project_settings.py @@ -87,8 +87,8 @@ def setUp(self): constants.LedWallSettingsKeys.TARGET_TO_SCREEN_CAT: constants.CAT.CAT_CAT02, constants.LedWallSettingsKeys.MATCH_REFERENCE_WALL: False, constants.LedWallSettingsKeys.REFERENCE_WALL: "", - constants.LedWallSettingsKeys.USE_EXTERNAL_WHITE_POINT: False, - constants.LedWallSettingsKeys.EXTERNAL_WHITE_POINT_FILE: "", + constants.LedWallSettingsKeys.USE_WHITE_POINT_OFFSET: False, + constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE: "", constants.LedWallSettingsKeys.VERIFICATION_WALL: "", constants.LedWallSettingsKeys.IS_VERIFICATION_WALL: False, constants.LedWallSettingsKeys.AVOID_CLIPPING: True diff --git a/tests/test_open_vp_cal/test_projects.py b/tests/test_open_vp_cal/test_projects.py index 2d0102f..c3978e0 100644 --- a/tests/test_open_vp_cal/test_projects.py +++ b/tests/test_open_vp_cal/test_projects.py @@ -225,8 +225,8 @@ class TestSample_Project5_Decoupled_Lens(BaseTestProjectPlateReuse): def setUp(self): super().setUp() for led_wall in self.project_settings.led_walls: - if led_wall.external_white_point_file: - led_wall.external_white_point_file = os.path.join( + if led_wall.white_point_offset_source: + led_wall.white_point_offset_source = os.path.join( self.get_sample_plates(), "A104_C003_11080B_001.R3D", "A104_C003_11080B_001.01397369.exr") def test_project5(self): @@ -253,8 +253,8 @@ class TestSample_Project6_Reference_Wall_With_Decoupled_Lens(BaseTestProjectPlat def setUp(self): super().setUp() for led_wall in self.project_settings.led_walls: - if led_wall.external_white_point_file: - led_wall.external_white_point_file = os.path.join( + if led_wall.white_point_offset_source: + led_wall.white_point_offset_source = os.path.join( self.get_sample_plates(), "A104_C003_11080B_001.R3D", "A104_C003_11080B_001.01397369.exr") def test_project6(self): diff --git a/tests/test_open_vp_cal/test_settings.json b/tests/test_open_vp_cal/test_settings.json index 8a43190..e5f359b 100644 --- a/tests/test_open_vp_cal/test_settings.json +++ b/tests/test_open_vp_cal/test_settings.json @@ -57,8 +57,8 @@ "target_to_screen_cat": "CAT02", "match_reference_wall": false, "reference_wall": "", - "use_external_white_point": false, - "external_white_point_file": "", + "use_white_point_offset": false, + "white_point_offset_source": "", "is_verification_wall": false, "verification_wall": "", "avoid_clipping": true From 51787d2b3070177c1a4d5aef71475ef84fb8f624 Mon Sep 17 00:00:00 2001 From: giardiello Date: Thu, 16 May 2024 11:53:02 +0100 Subject: [PATCH 05/31] Update README.md Change terminology from external white point to white point offset --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fe129a1..496c4b0 100644 --- a/README.md +++ b/README.md @@ -565,19 +565,19 @@ Right clicking offers a context menu which resets the ROI should you make a mist - USE EXTERNAL WHITE POINT + USE WHITE POINT OFFSET - USE EXTERNAL WHITE POINT + USE WHITE POINT OFFSET - If checked, the plate original white point will be converted to the external white point prior to perform the analysis and the calibration + If checked, the plate original white point will be shofted towards the measured white point of the indicated file, prior to perform the analysis and the calibration - EXTERNAL WHITE POINT FILE + WHITE POINT OFFSET SOURCE FILE - A path to a file with the external white point + A path to a file with the white point offset source @@ -962,7 +962,7 @@ The widget offers a series of tools: Apply White Balance Preview - Applies a preview of the Auto-WB and or External White Balance, if present + Applies a preview of the Auto-WB and or White Balance Offset, if present @@ -1372,17 +1372,17 @@ In this use case, we ask to perform an auto white-balance in the camera on the f ##### MATCH WALL CALIBRATION: Shiting the calibration to make multiple walls match -When your stage has multiple LED wall types (brands/models) and you want to match them in camera, this workflow allows you to select a “reference wall” that is used as main target for the calibration. By enabling “Match Reference Wall” and selecting from the dropdown menu one of the other walls from the Stage View Bin, OpenVpCal will make sure that the wall you are calibrating will match the Reference one when keeping the camera at the same white balance. The Reference wall can follow any of the other workflows (Simple, Auto-WB and External White), and these shifts will be handed along to the matching wall(s). +When your stage has multiple LED wall types (brands/models) and you want to match them in camera, this workflow allows you to select a “reference wall” that is used as main target for the calibration. By enabling “Match Reference Wall” and selecting from the dropdown menu one of the other walls from the Stage View Bin, OpenVpCal will make sure that the wall you are calibrating will match the Reference one when keeping the camera at the same white balance. The Reference wall can follow any of the other workflows (Simple, Auto-WB and White Point offset), and these shifts will be handed along to the matching wall(s). image_tooltip -##### EXTERNAL WHITE BALANCE CALIBRATION: Decoupling lenses or shifting to external white points +##### WHITE POINT OFFSET CALIBRATION: Decoupling lenses or shifting to external white points Like the auto-wb option, this option allows to correct the plate prior to be passed to the analysis. -However, in this case the calibration plate is white balanced towards an external white point, instead of the target white point. There are different use cases for this workflow, but it has mostly been designed to decouple the effect of the lens used to shoot the calibration plate. When enabeling this option, OpenVpCal requires the user to select a frame from which it will calculate the white balance matrix to apply to the calibration patches. When aiming to decouple the effect of the lens, we perform the workflow in these simple steps: +However, in this case the calibration plate is white balanced towards a different white point, instead of the target white point. There are different use cases for this workflow, but it has mostly been designed to decouple the effect of the lens or filters used to shoot the calibration plate. When enabeling this option, OpenVpCal requires the user to select a frame from which it will calculate the white balance matrix to apply to the calibration patches. When aiming to decouple the effect of the lens, we perform the workflow in these simple steps: @@ -1390,12 +1390,12 @@ However, in this case the calibration plate is white balanced towards an externa 2. Remove the lens from the camera, don’t change anything else; 3. The incoming light from the LED wall to the sensor will be bright without a lens, so change the shutter speed and/or fps of the camera to make sure that the blurred image doesn’t clip. Ideally it should be exposed correctly (using the false color of the camera, should get to the 18% level). 4. Shoot just a second (a single frame is necessary) of the first patch of the calibration sequence only. You don’t need to run the entire sequence. -5. Use one frame of the clip shot without a lens as External White Point File +5. Use one frame of the clip shot without a lens as White Point Offset Source File @@ -1422,7 +1422,7 @@ However, in this case the calibration plate is white balanced towards an externa image_tooltip -Once applied, the gray patches on the right hand of the viewer will show the before and after the external white point shift calibration. +Once applied, the gray patches on the right hand of the viewer will show the before and after the white point offset calibration. #### EXPORT CALIBRATION From ff9b5035bc9eb51611169ad3a58dcf7158146517 Mon Sep 17 00:00:00 2001 From: davisadam10 Date: Thu, 16 May 2024 17:15:45 -0700 Subject: [PATCH 06/31] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 496c4b0..2ffae5a 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,11 @@ For more enquires please contact The full user guide for OpenVPCal can be found below or here as a pdf [UserGuide](https://github.com/Netflix-Skunkworks/OpenVPCal/blob/main/User_Guide_OpenVPCal.pdf). +## Quick Start - Tutorial Video +A quick start tutorial video can be found below + +[![Tutorial Video - Quick Start](https://img.youtube.com/vi/ORrTdUGl0JI/0.jpg)](https://www.youtube.com/watch?v=ORrTdUGl0JI) + # Table of Contents - [OpenVPCal](#openvpcal) From 86764ee1e76ebb7280839b6c038a6d752a655f0e Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Thu, 16 May 2024 17:22:05 -0700 Subject: [PATCH 07/31] Loosen the restriction to python3.11 and not the minor version --- compile.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compile.py b/compile.py index 04e66e0..3e51f93 100644 --- a/compile.py +++ b/compile.py @@ -210,12 +210,13 @@ def check_python_is_64_bit() -> bool: def check_python_version() -> bool: - """ Checks the version of python we have installed is 3.11.6 + """ Checks the version of python we have installed is 3.11 - Returns: True if python is 3.11.6, False if not + Returns: True if python is 3.11, False if not """ - return '3.11.6' == platform.python_version() + major_minor_version = '.'.join(platform.python_version().split('.')[:2]) + return '3.11' == major_minor_version def is_git_installed() -> bool: @@ -555,7 +556,7 @@ def check_dependencies() -> None: if not check_python_is_64_bit(): raise RuntimeError("Python must be 64 bit") if not check_python_version(): - raise RuntimeError("Python must be 3.11.6") + raise RuntimeError("Python must be 3.11") if not is_git_installed(): raise RuntimeError("Git must be installed") if not is_pkgconfig_installed(): From 26bc1702e273f4a060a47149be830b1142f39bea Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Thu, 16 May 2024 17:31:22 -0700 Subject: [PATCH 08/31] Releasing the first version of the project Co-authored-by: Adam Davis Co-authored-by: Francesco Luigi Giardiello Co-authored-by: Adrian Pueyo Co-authored-by: Daniel Heckenberg Co-authored-by: Carol Payne --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 2ffae5a..3d30a7c 100644 --- a/README.md +++ b/README.md @@ -1691,4 +1691,3 @@ Thank you all for your support and collaboration. - From f08b42b90064f11836a4779d243079ae89fb785d Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Thu, 16 May 2024 17:31:22 -0700 Subject: [PATCH 09/31] Releasing the first version of the project Co-authored-by: Adam Davis Co-authored-by: Francesco Luigi Giardiello Co-authored-by: Adrian Pueyo Co-authored-by: Daniel Heckenberg Co-authored-by: Carol Payne --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 2ffae5a..3d30a7c 100644 --- a/README.md +++ b/README.md @@ -1691,4 +1691,3 @@ Thank you all for your support and collaboration. - From 5ec3bf5e70226f7c4029dfc8b4cbc842cdc48560 Mon Sep 17 00:00:00 2001 From: giardiello Date: Fri, 17 May 2024 19:36:33 +0100 Subject: [PATCH 10/31] Update README.md Remove pdf --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 3d30a7c..6262163 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,7 @@ For more enquires please contact [openvpcal@netflix.com](mailto:openvpcal@netflix.com) -The full user guide for OpenVPCal can be found below or here as a pdf [UserGuide](https://github.com/Netflix-Skunkworks/OpenVPCal/blob/main/User_Guide_OpenVPCal.pdf). - +The full user guide for OpenVPCal can be found below. ## Quick Start - Tutorial Video A quick start tutorial video can be found below From 8576b01269121e413f1b398eb223fd42b6d06bdc Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Fri, 17 May 2024 11:41:18 -0700 Subject: [PATCH 11/31] Delete pdf userguide --- User_Guide_OpenVPCal.pdf | Bin 132 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 User_Guide_OpenVPCal.pdf diff --git a/User_Guide_OpenVPCal.pdf b/User_Guide_OpenVPCal.pdf deleted file mode 100644 index 99a3c868400ec98223a10b466cf83246e7266a3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132 zcmWN?K@!3s3;@7;U%>|~NeC_UHxNLWQRxWw;OliSd*%0N{iW)h=Qy;!w|RS%vHq{0 zxJrNOac0pMF1;l+Y9PBEhFq~CDem6x;Edl0feK~FiIRt43^RGjY`_So1TGLn!DwB9 O5|`1wS+Pe%_R9|(2PcRC From f094e99f875f50d15140e0426ee97d79cb2c570c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 May 2024 08:27:09 +0000 Subject: [PATCH 12/31] --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 39d6318..6e6bfb3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -48,7 +48,7 @@ PySide6-Essentials==6.5.3 pytest==7.4.3 pytest-xdist==3.5.0 python-dateutil==2.8.2 -requests==2.31.0 +requests==2.32.0 scipy==1.11.4 shiboken6==6.5.3 six==1.16.0 From 289939faeb861fb8709e11baac1c074df0d8451f Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 28 May 2024 11:58:10 +0100 Subject: [PATCH 13/31] fix: Ensure that when we calculate the start and end frame for the patches we account for any patches whcih occur after the eotf ramp patches --- src/open_vp_cal/framework/sample_patch.py | 54 ++++++++++++++----- .../Plates/Blue_Wall_Issue/raptor.480.exr | 3 ++ .../raptorverify.480.exr | 3 ++ 3 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue/raptor.480.exr create mode 100644 tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue_Verify/raptorverify.480.exr diff --git a/src/open_vp_cal/framework/sample_patch.py b/src/open_vp_cal/framework/sample_patch.py index df80147..aa0ab2a 100644 --- a/src/open_vp_cal/framework/sample_patch.py +++ b/src/open_vp_cal/framework/sample_patch.py @@ -17,7 +17,8 @@ """ import threading import numpy as np -from colour_checker_detection.detection.segmentation import detect_colour_checkers_segmentation +from colour_checker_detection.detection.segmentation import \ + detect_colour_checkers_segmentation from open_vp_cal.imaging import imaging_utils from open_vp_cal.core.structures import SamplePatchResults @@ -41,6 +42,23 @@ def __init__(self, led_wall_settings: LedWallSettings, self.trim_frames = 1 # The num frames we trim from the start and end of the patch, so we avoid multiplexing self.required_sample_frames = 3 + def get_num_patches_relative_to_red(self, red_patch_index) -> int: + """ Returns the number of patches relative to the red patch index, accounting + for if the patch occurs before or after the eotf ramp patches + + Args: + red_patch_index (int): The index of the red patch. + + Returns: + int: The number of patches relative to the red patch index. + """ + eotf_ramp_index = constants.PATCHES.get_patch_index( + constants.PATCHES.EOTF_RAMPS) + patch_index = constants.PATCHES.get_patch_index(self.patch) + if patch_index > eotf_ramp_index: + return patch_index - red_patch_index + self.led_wall.num_grey_patches + return patch_index - red_patch_index + def calculate_first_and_last_patch_frame(self) -> tuple[int, int]: """ Calculate the first and last frame of the patch. @@ -48,13 +66,17 @@ def calculate_first_and_last_patch_frame(self) -> tuple[int, int]: Returns: tuple[int, int]: The first and last frame of the patch. """ - patch_index = constants.PATCHES.get_patch_index(self.patch) - red_patch_index = constants.PATCHES.get_patch_index(constants.PATCHES.RED_PRIMARY_DESATURATED) - number_of_patches_relative_to_red = patch_index - red_patch_index + red_patch_index = constants.PATCHES.get_patch_index( + constants.PATCHES.RED_PRIMARY_DESATURATED) + + number_of_patches_relative_to_red = self.get_num_patches_relative_to_red( + red_patch_index + ) number_of_frames = number_of_patches_relative_to_red * self.separation_results.separation first_patch_frame = number_of_frames + self.separation_results.first_red_frame.frame_num last_patch_frame = first_patch_frame + (self.separation_results.separation - 1) - trim_frames = (last_patch_frame - first_patch_frame) // self.required_sample_frames + trim_frames = ( + last_patch_frame - first_patch_frame) // self.required_sample_frames if trim_frames > self.trim_frames: self.trim_frames = trim_frames return first_patch_frame, last_patch_frame @@ -97,7 +119,8 @@ def _sample_patch(self) -> None: None """ first_patch_frame, last_patch_frame = self.calculate_first_and_last_patch_frame() - self.analyse_patch_frames(0, self.sample_results, first_patch_frame, last_patch_frame) + self.analyse_patch_frames(0, self.sample_results, first_patch_frame, + last_patch_frame) def analyse_patch_frames(self, idx: int, results: list, first_patch_frame: int, last_patch_frame: int) -> None: @@ -115,14 +138,16 @@ def analyse_patch_frames(self, idx: int, results: list, # We trim a number of frames off either side of the patch to ensure we remove multiplexing sample_results = SamplePatchResults() samples = [] - for frame_num in range(first_patch_frame + self.trim_frames, (last_patch_frame - self.trim_frames) + 1): + for frame_num in range(first_patch_frame + self.trim_frames, + (last_patch_frame - self.trim_frames) + 1): frame = self.led_wall.sequence_loader.get_frame(frame_num) section = frame.extract_roi(self.led_wall.roi) mean_color = imaging_utils.sample_image(section) samples.append(mean_color) sample_results.frames.append(frame) - sample_results.samples = [sum(channel) / len(channel) for channel in zip(*samples)] + sample_results.samples = [sum(channel) / len(channel) for channel in + zip(*samples)] results[idx] = sample_results @@ -162,8 +187,10 @@ def _sample_patch(self) -> None: threads = [] results = [None] * grey_patches for patch_count in range(0, grey_patches): - patch_start_frame = first_patch_frame + (patch_count * self.separation_results.separation) - patch_last_frame = patch_start_frame + (self.separation_results.separation - 1) + patch_start_frame = first_patch_frame + ( + patch_count * self.separation_results.separation) + patch_last_frame = patch_start_frame + ( + self.separation_results.separation - 1) thread = threading.Thread(target=self.analyse_patch_frames, args=( patch_count, results, patch_start_frame, patch_last_frame) @@ -190,7 +217,8 @@ def __init__(self, led_wall_settings: LedWallSettings, led_wall_settings (LedWallSettings): The LED wall settings we want to sample separation_results (SeparationResults): The results of the separation. """ - super().__init__(led_wall_settings, separation_results, constants.PATCHES.MACBETH) + super().__init__(led_wall_settings, separation_results, + constants.PATCHES.MACBETH) self.sample_results = [None] def run(self) -> list: @@ -211,7 +239,8 @@ def _sample_patch(self) -> None: # We trim a number of frames off either side of the patch to ensure we remove multiplexing sample_results = SamplePatchResults() samples = [] - for frame_num in range(first_patch_frame + self.trim_frames, (last_patch_frame - self.trim_frames) + 1): + for frame_num in range(first_patch_frame + self.trim_frames, + (last_patch_frame - self.trim_frames) + 1): frame = self.led_wall.sequence_loader.get_frame(frame_num) section = frame.extract_roi(self.led_wall.roi) sample_results.frames.append(frame) @@ -221,7 +250,6 @@ def _sample_patch(self) -> None: swatch_colours, _, _ = ( colour_checker_swatches_data.values) - samples.append(swatch_colours) # Compute the mean for each tuple index across all tuples, if the detection fails and we get nans, then we diff --git a/tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue/raptor.480.exr b/tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue/raptor.480.exr new file mode 100644 index 0000000..5d86753 --- /dev/null +++ b/tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue/raptor.480.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac3bae0a802e91677f4975f714f32e50fff412e150108abbce17b460eccddcd6 +size 24902443 diff --git a/tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue_Verify/raptorverify.480.exr b/tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue_Verify/raptorverify.480.exr new file mode 100644 index 0000000..2b5526f --- /dev/null +++ b/tests/test_open_vp_cal/resources/Plates/Blue_Wall_Issue_Verify/raptorverify.480.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bef418a51ef39250bba98e9896395193a727f2167071ba5a535011d60a56162 +size 24902447 From 766b0dd526621483456f751b04d4faa3ecaac30e Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 28 May 2024 13:31:08 +0100 Subject: [PATCH 14/31] fix: Switch to use Eclidean distances to detect frame changes where colour detection fails alone, causing some issues with multiplexing --- .../framework/identify_separation.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/open_vp_cal/framework/identify_separation.py b/src/open_vp_cal/framework/identify_separation.py index 00e11c0..6230547 100644 --- a/src/open_vp_cal/framework/identify_separation.py +++ b/src/open_vp_cal/framework/identify_separation.py @@ -113,6 +113,11 @@ def check_green(mean_color: List[float]) -> bool: """ return imaging_utils.detect_green(mean_color) + def calculate_distance(self, rgb1, rgb2): + import numpy as np + """Calculate Euclidean distance between two RGB values.""" + return np.sqrt(np.sum((np.array(rgb2) - np.array(rgb1)) ** 2)) * 100 + def _find_first_red_and_green_frames(self) -> None: """ Iterates over all frames in the sequence loader, computes the mean colour of each frame, and finds the first @@ -120,22 +125,37 @@ def _find_first_red_and_green_frames(self) -> None: The results are stored in the separation_results attribute. """ + previous_mean_frame = None for frame in self.led_wall.sequence_loader: # Load the image from the frame image = frame.extract_roi(self.led_wall.roi) # Compute the average for all the values which are above the initial average mean_color, _ = imaging_utils.get_average_value_above_average(image) + distance = 0 + if previous_mean_frame: + distance = self.calculate_distance(mean_color, previous_mean_frame) + + + print (frame.frame_num, distance) + previous_mean_frame = mean_color - # Check if the image is red - if self.check_red(mean_color): + # Check if the image is red or we detect a significant change in the mean + if self.check_red(mean_color) or distance > 1: if self.separation_results.first_red_frame is None: + print ("Setting Red") + print(frame.frame_num, distance) self.separation_results.first_red_frame = frame + continue - # Check if the image is green - if self.check_green(mean_color): + # Check if the image is green or if we detect a significant change in the + # mean colour + if self.check_green(mean_color) or distance > 1: if self.separation_results.first_green_frame is None: + print("Setting Green") + print(frame.frame_num, distance) self.separation_results.first_green_frame = frame + continue # If we've found both the first red and green frames, we can stop if self.separation_results.first_red_frame is not None \ From 431e5a65fc8083687328c96e81a524e06dc6a395 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 28 May 2024 17:02:05 +0100 Subject: [PATCH 15/31] fix: We rely soley on the colour checks to begin with, we then store all our distance values and once we have detected 4 distinct peaks, (changes of frame) we use this to either add missing frames, or verify that they are infact close --- .../framework/identify_separation.py | 72 ++++++++++++----- src/open_vp_cal/imaging/imaging_utils.py | 78 ++++++++++++++----- 2 files changed, 113 insertions(+), 37 deletions(-) diff --git a/src/open_vp_cal/framework/identify_separation.py b/src/open_vp_cal/framework/identify_separation.py index 6230547..d978a33 100644 --- a/src/open_vp_cal/framework/identify_separation.py +++ b/src/open_vp_cal/framework/identify_separation.py @@ -18,6 +18,9 @@ """ from typing import List +import numpy as np +from scipy.signal import find_peaks + from open_vp_cal.imaging import imaging_utils from open_vp_cal.led_wall_settings import LedWallSettings @@ -113,11 +116,6 @@ def check_green(mean_color: List[float]) -> bool: """ return imaging_utils.detect_green(mean_color) - def calculate_distance(self, rgb1, rgb2): - import numpy as np - """Calculate Euclidean distance between two RGB values.""" - return np.sqrt(np.sum((np.array(rgb2) - np.array(rgb1)) ** 2)) * 100 - def _find_first_red_and_green_frames(self) -> None: """ Iterates over all frames in the sequence loader, computes the mean colour of each frame, and finds the first @@ -125,6 +123,9 @@ def _find_first_red_and_green_frames(self) -> None: The results are stored in the separation_results attribute. """ + frame_numbers = [] + distances = [] + previous_mean_frame = None for frame in self.led_wall.sequence_loader: # Load the image from the frame @@ -134,30 +135,65 @@ def _find_first_red_and_green_frames(self) -> None: mean_color, _ = imaging_utils.get_average_value_above_average(image) distance = 0 if previous_mean_frame: - distance = self.calculate_distance(mean_color, previous_mean_frame) - + distance = imaging_utils.calculate_distance( + mean_color, previous_mean_frame) - print (frame.frame_num, distance) + # Store the frame number and distance + frame_numbers.append(frame.frame_num) + distances.append(distance) previous_mean_frame = mean_color # Check if the image is red or we detect a significant change in the mean - if self.check_red(mean_color) or distance > 1: + if self.check_red(mean_color): if self.separation_results.first_red_frame is None: - print ("Setting Red") - print(frame.frame_num, distance) self.separation_results.first_red_frame = frame continue # Check if the image is green or if we detect a significant change in the # mean colour - if self.check_green(mean_color) or distance > 1: + if self.check_green(mean_color): if self.separation_results.first_green_frame is None: - print("Setting Green") - print(frame.frame_num, distance) self.separation_results.first_green_frame = frame continue - # If we've found both the first red and green frames, we can stop - if self.separation_results.first_red_frame is not None \ - and self.separation_results.first_green_frame is not None: - break + distances_array = np.array(distances) + peaks, _ = find_peaks(distances_array, height=1) + if len(peaks) >= 4: + + first_peak_frame_num = frame_numbers[peaks[0]] + first_peak_frame = self.led_wall.sequence_loader.get_frame( + first_peak_frame_num + ) + # If we didn't find a red frame, set the first red frame to the first + # peak + if self.separation_results.first_red_frame is None: + self.separation_results.first_red_frame = first_peak_frame + + # If the detected red frame is not within 3 frames of the first peak + # we detected the second red patch so we should use the first peak as + # the first red frame + if not imaging_utils.is_within_range( + self.separation_results.first_red_frame.frame_num, + first_peak_frame_num, 3): + self.separation_results.first_red_frame = first_peak_frame + + second_peak_frame_num = frame_numbers[peaks[1]] + second_peak_frame = self.led_wall.sequence_loader.get_frame( + second_peak_frame_num + ) + # If we didn't find a green frame, set the first green frame to the + # second peak + if self.separation_results.first_green_frame is None: + self.separation_results.first_green_frame = second_peak_frame + + # If the detected green frame is not within 3 frames of the second peak + # we detected the second green patch so we should use the first peak as + # the first red frame + if not imaging_utils.is_within_range( + self.separation_results.first_green_frame.frame_num, + second_peak_frame_num, 3): + self.separation_results.first_green_frame = second_peak_frame + + if (self.separation_results.first_red_frame is not None + and self.separation_results.first_green_frame is not None): + break diff --git a/src/open_vp_cal/imaging/imaging_utils.py b/src/open_vp_cal/imaging/imaging_utils.py index 0326797..8eb603b 100644 --- a/src/open_vp_cal/imaging/imaging_utils.py +++ b/src/open_vp_cal/imaging/imaging_utils.py @@ -43,7 +43,8 @@ class MockModule: """ def __getattr__(self, name): - raise MissingModuleError(f"'OpenImageIO' module is missing. Can't access '{name}'.") + raise MissingModuleError( + f"'OpenImageIO' module is missing. Can't access '{name}'.") Oiio = MockModule() @@ -52,7 +53,8 @@ def __getattr__(self, name): from colour.models import RGB_COLOURSPACE_ACES2065_1 import numpy as np -from open_vp_cal.core.constants import OIIO_COMPRESSION_ATTRIBUTE, OIIO_COMPRESSION_NONE, OIIO_BITS_PER_SAMPLE +from open_vp_cal.core.constants import OIIO_COMPRESSION_ATTRIBUTE, \ + OIIO_COMPRESSION_NONE, OIIO_BITS_PER_SAMPLE from open_vp_cal.core.resource_loader import ResourceLoader from open_vp_cal.core import utils @@ -93,7 +95,9 @@ def img_buf_from_numpy_array(np_array: np.array) -> Oiio.ImageBuf: Returns: The Oiio.ImgBuf """ - image_buf = Oiio.ImageBuf(Oiio.ImageSpec(np_array.shape[1], np_array.shape[0], np_array.shape[2], Oiio.FLOAT)) + image_buf = Oiio.ImageBuf( + Oiio.ImageSpec(np_array.shape[1], np_array.shape[0], np_array.shape[2], + Oiio.FLOAT)) # Set the pixels of the ImageBuf using the numpy array image_buf.set_pixels(Oiio.ROI(0, np_array.shape[1], 0, np_array.shape[0]), np_array) @@ -177,7 +181,8 @@ def get_oiio_bit_depth(value: Union[int, str]) -> Oiio.BASETYPE: "float": Oiio.FLOAT } if value not in bit_depth_map: - raise KeyError("Unsupported Bit Depth - Must Be " + ",".join([str(k) for k in bit_depth_map])) + raise KeyError("Unsupported Bit Depth - Must Be " + ",".join( + [str(k) for k in bit_depth_map])) return bit_depth_map[value] @@ -239,7 +244,8 @@ def stitch_images_horizontally(img_buffers): # Create an output image buffer with the total width and maximum height output = Oiio.ImageBuf( - Oiio.ImageSpec(total_width, max_height, img_buffers[0].nchannels, img_buffers[0].spec().format) + Oiio.ImageSpec(total_width, max_height, img_buffers[0].nchannels, + img_buffers[0].spec().format) ) # Initialize x_offset to 0 @@ -274,7 +280,8 @@ def apply_color_conversion( color_config = ResourceLoader.ocio_config_path() image = image_buf_to_np_array(image_buffer) - apply_color_converstion_to_np_array(image, from_transform, to_transform, color_config) + apply_color_converstion_to_np_array(image, from_transform, to_transform, + color_config) converted_buffer = img_buf_from_numpy_array(image) return converted_buffer @@ -361,13 +368,15 @@ def apply_display_conversion_to_np_array( color_config = ResourceLoader.ocio_config_path() config = ocio.Config().CreateFromFile(color_config) - processor = config.getProcessor(ocio.ROLE_SCENE_LINEAR, display, view, ocio.TRANSFORM_DIR_FORWARD) + processor = config.getProcessor(ocio.ROLE_SCENE_LINEAR, display, view, + ocio.TRANSFORM_DIR_FORWARD) cpu = processor.getDefaultCPUProcessor() cpu.applyRGB(image) def nest_analysis_swatches( - source_img: Oiio.ImageBuf, target_img: Oiio.ImageBuf, patch_size: tuple[int, int] = (200, 200), + source_img: Oiio.ImageBuf, target_img: Oiio.ImageBuf, + patch_size: tuple[int, int] = (200, 200), central_roi_size: tuple[int, int] = (100, 100)) -> Oiio.ImageBuf: """ The function takes a source image and a target image. For each patch of size patch_size in the source image, @@ -386,14 +395,17 @@ def nest_analysis_swatches( """ # Calculate the central offsets for the region of interest - central_roi_offset = ((patch_size[0] - central_roi_size[0]) // 2, (patch_size[1] - central_roi_size[1]) // 2) + central_roi_offset = ((patch_size[0] - central_roi_size[0]) // 2, + (patch_size[1] - central_roi_size[1]) // 2) # Iterate over the patches in the source image for i in range(0, source_img.spec().width, patch_size[0]): for j in range(0, source_img.spec().height, patch_size[1]): # Define the region of interest in the source image - src_roi = Oiio.ROI(i + central_roi_offset[0], i + central_roi_offset[0] + central_roi_size[0], - j + central_roi_offset[1], j + central_roi_offset[1] + central_roi_size[1]) + src_roi = Oiio.ROI(i + central_roi_offset[0], + i + central_roi_offset[0] + central_roi_size[0], + j + central_roi_offset[1], + j + central_roi_offset[1] + central_roi_size[1]) # Read the central region from the source image src_patch = source_img.get_pixels(roi=src_roi) @@ -453,7 +465,8 @@ def generate_image_cie(scale: int, file_path: str) -> bool: return True -def insert_resized_image(image_a: Oiio.ImageBuf, image_b: Oiio.ImageBuf, resize_percent) -> Oiio.ImageBuf: +def insert_resized_image(image_a: Oiio.ImageBuf, image_b: Oiio.ImageBuf, + resize_percent) -> Oiio.ImageBuf: """ Resize imageA by the given percentage and insert it into the center of imageB. @@ -472,7 +485,8 @@ def insert_resized_image(image_a: Oiio.ImageBuf, image_b: Oiio.ImageBuf, resize_ new_width = int(image_a.spec().width - width_reduction) new_height = int(image_a.spec().height - height_reduction) resized_image_a = Oiio.ImageBuf() - res = Oiio.ImageBufAlgo.resize(resized_image_a, image_a, "", 0, Oiio.ROI(0, new_width, 0, new_height)) + res = Oiio.ImageBufAlgo.resize(resized_image_a, image_a, "", 0, + Oiio.ROI(0, new_width, 0, new_height)) if not res: raise ValueError("Failed to resize image buffer") @@ -542,7 +556,8 @@ def get_scaled_cie_spectrum_bg_image(max_scale: int) -> Oiio.ImageBuf: return image_buf -def load_image_buffer_to_qimage(buffer: Oiio.ImageBuf, project_settings: "ProjectSettings") -> QImage: +def load_image_buffer_to_qimage(buffer: Oiio.ImageBuf, + project_settings: "ProjectSettings") -> QImage: """ Load an image buffer into a QImage Args: @@ -566,7 +581,8 @@ def load_image_buffer_to_qimage(buffer: Oiio.ImageBuf, project_settings: "Projec return image -def load_image_buffer_to_qpixmap(buffer: Oiio.ImageBuf, project_settings: "ProjectSettings") -> QPixmap: +def load_image_buffer_to_qpixmap(buffer: Oiio.ImageBuf, + project_settings: "ProjectSettings") -> QPixmap: """ Load an Oiio.ImageBuf into a QPixmap so we can display it Args: @@ -648,7 +664,8 @@ def create_and_stitch_analysis_strips( return sample_buffers_stitched, reference_buffers_stitched -def add_text_to_image_buffer(text: str, img_buffer: Oiio.ImageBuf, text_color: List, text_size: int) -> None: +def add_text_to_image_buffer(text: str, img_buffer: Oiio.ImageBuf, text_color: List, + text_size: int) -> None: """ Adds text to the given image buffer Args: @@ -721,7 +738,8 @@ def sample_image(img_buf: Oiio.ImageBuf) -> List: return result -def get_average_value_above_average(img_buf: Oiio.ImageBuf) -> Tuple[List[float], Oiio.ImageBuf]: +def get_average_value_above_average(img_buf: Oiio.ImageBuf) -> Tuple[ + List[float], Oiio.ImageBuf]: """ Eliminates any pixels which are below the average pixel value of the whole image, ie often black or very dark pixels, this leaves us the pixels from the whole image which are often not black, in this case the patch sections. @@ -749,7 +767,8 @@ def get_average_value_above_average(img_buf: Oiio.ImageBuf) -> Tuple[List[float] # Create a copy of the original image for mask output img_to_write_out_with_mask = np.copy(img_array) - img_to_write_out_with_mask[below_average_mask] = [1, 0, 0] # Set below average pixels to red + img_to_write_out_with_mask[below_average_mask] = [1, 0, + 0] # Set below average pixels to red # Create another copy for average calculation img_avg_copy = np.copy(img_array) @@ -847,7 +866,8 @@ def compute_clipped_mean(image: np.array, channel_idx: int, sigma: int = 3): upper_bound = mean_val + sigma * std_dev # Clip the outliers - clipped_data = channel_data[(channel_data >= lower_bound) & (channel_data <= upper_bound)] + clipped_data = channel_data[ + (channel_data >= lower_bound) & (channel_data <= upper_bound)] # Recompute the mean refined_mean = np.mean(clipped_data) @@ -873,3 +893,23 @@ def get_decoupled_white_samples_from_file(external_white_point_file: str) -> Lis image_buffer = Oiio.ImageBuf(external_white_point_file) return sample_image(image_buffer) + + +def calculate_distance(rgb1, rgb2) -> float: + """ Calculates Euclidean distance between two RGB values.""" + return np.sqrt(np.sum((np.array(rgb2) - np.array(rgb1)) ** 2)) * 100 + + +def is_within_range(value: float, target: float, x: float) -> bool: + """ + Check if the value is within x units of the target value. + + Parameters: + value (float): The value to check. + target (float): The target value. + x (float): The range to check within. + + Returns: + bool: True if value is within x units of target, False otherwise. + """ + return abs(value - target) <= x From ab174a9e572d8788bbfbfe1dd4ee13fbf50e42b4 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 28 May 2024 17:18:15 +0100 Subject: [PATCH 16/31] fix: remove the unneeded check for the camera gamut being the same as the target gamut, whilst this is not an advisable work flow, this technically is possible --- src/open_vp_cal/application_base.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/open_vp_cal/application_base.py b/src/open_vp_cal/application_base.py index fcb3f71..9fbbbee 100644 --- a/src/open_vp_cal/application_base.py +++ b/src/open_vp_cal/application_base.py @@ -124,11 +124,6 @@ def run_pre_checks(self, led_walls: List[LedWallSettings]) -> bool: """ led_wall_names = [led_wall.name for led_wall in led_walls] for led_wall in led_walls: - if led_wall.native_camera_gamut == led_wall.target_gamut: - message = f"Target Gamut & Native Camera Gamut Can Not Be The Same For {led_wall.name}" - self.error_message(message) - return False - if not led_wall.has_valid_white_balance_options(): message = f"Only Select 1 option from AutoWB, or Reference Wall or External White {led_wall.name}" self.error_message(message) From 647cf51f904bb7cb2688d893c106f3d1d754f159 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 28 May 2024 17:20:55 +0100 Subject: [PATCH 17/31] fix: make the labels for then custom gamut x & y vs X & Y --- src/open_vp_cal/widgets/project_settings_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/open_vp_cal/widgets/project_settings_widget.py b/src/open_vp_cal/widgets/project_settings_widget.py index ac2ef63..7b95e6f 100644 --- a/src/open_vp_cal/widgets/project_settings_widget.py +++ b/src/open_vp_cal/widgets/project_settings_widget.py @@ -500,8 +500,8 @@ def init_ui(self): layout = QFormLayout() self.grid_layout = QGridLayout() - self.grid_layout.addWidget(QLabel("X"), 0, 1) - self.grid_layout.addWidget(QLabel("Y"), 0, 2) + self.grid_layout.addWidget(QLabel("x"), 0, 1) + self.grid_layout.addWidget(QLabel("y"), 0, 2) # Add the line edit for the custom name at the top self.custom_name_edit = QLineEdit() From 5ad73a35dc0a7fc4028fbb34cbca1765940394c8 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 28 May 2024 17:49:12 +0100 Subject: [PATCH 18/31] Add in resizing of the calibration patch generation so it all happens as a final step --- src/open_vp_cal/framework/generation.py | 28 ++++++++++++++++-------- src/open_vp_cal/imaging/imaging_utils.py | 20 +++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/open_vp_cal/framework/generation.py b/src/open_vp_cal/framework/generation.py index dff8122..b7ece57 100644 --- a/src/open_vp_cal/framework/generation.py +++ b/src/open_vp_cal/framework/generation.py @@ -67,6 +67,8 @@ def __init__(self, led_wall: LedWallSettings, patch_size=(1000, 1000)): self.desaturated_green = np.array([]) self.desaturated_blue = np.array([]) self.flat_field = [0.5, 0.5, 0.5] + self._generation_width = 3840 + self._generation_height = 2160 self.calc_constants() @@ -341,8 +343,8 @@ def get_patch_start_positions(self) -> tuple[int, int]: """ patch_width, patch_height = self.patch_size - start_x = (self.led_wall.project_settings.resolution_width - patch_width) // 2 - start_y = (self.led_wall.project_settings.resolution_height - patch_height) // 2 + start_x = (self._generation_width - patch_width) // 2 + start_y = (self._generation_height - patch_height) // 2 return start_x, start_y def distort_and_roi(self, patch_values: list[int]) -> list[Oiio.ImageBuf]: @@ -363,7 +365,7 @@ def distort_and_roi(self, patch_values: list[int]) -> list[Oiio.ImageBuf]: roi_size, checker_size, checker_odd, checker_even = patch_values full_image = Oiio.ImageBuf(Oiio.ImageSpec( - self.led_wall.project_settings.resolution_width, self.led_wall.project_settings.resolution_height, 3, + self._generation_width, self._generation_height, 3, Oiio.FLOAT) ) @@ -570,8 +572,8 @@ def generate_slate_patch(self, patch_values: None) -> list[Oiio.ImageBuf]: src_patch = Oiio.ImageBuf(ResourceLoader.slate()) patch = Oiio.ImageBufAlgo.resize( src_patch, roi=Oiio.ROI( - 0, self.led_wall.project_settings.resolution_width, 0, - self.led_wall.project_settings.resolution_height + 0, self._generation_width, 0, + self._generation_height ) ) @@ -968,7 +970,7 @@ def generate_solid_patch_full(self, patch_values: tuple[float, float, float]) -> """ # Create the colour patch for the full image size full_image = Oiio.ImageBuf(Oiio.ImageSpec( - self.led_wall.project_settings.resolution_width, self.led_wall.project_settings.resolution_height, 3, + self._generation_width, self._generation_height, 3, Oiio.FLOAT) ) Oiio.ImageBufAlgo.fill(full_image, (patch_values[0], patch_values[1], patch_values[2])) @@ -984,8 +986,8 @@ def generate_patch(self, patch_name: constants.PATCHES) -> list[Oiio.ImageBuf]: Returns: Oiio.ImageBuf: The image buffer of the generated colour square. """ - image_width, image_height = self.led_wall.project_settings.resolution_width, \ - self.led_wall.project_settings.resolution_height + image_width, image_height = self._generation_width, \ + self._generation_height patches, _ = self.find_and_generate_patch_from_map(patch_name) @@ -1075,7 +1077,15 @@ def write_to_disk(self, img_buf, patch_name) -> str: if self.led_wall.project_settings.file_format == constants.FileFormats.FF_TIF: bit_depth = 16 - imaging_utils.write_image(img_buf, file_path, bit_depth, channel_mapping=None) + if (self.led_wall.project_settings.resolution_height != self._generation_height + or self.led_wall.project_settings.resolution_width != + self._generation_width): + img_buf = imaging_utils.resize_image( + img_buf, self.led_wall.project_settings.resolution_width, + self.led_wall.project_settings.resolution_height) + + imaging_utils.write_image( + img_buf, file_path, bit_depth, channel_mapping=None) return file_path def generate_patches(self, patch_names: list[constants.PATCHES]): diff --git a/src/open_vp_cal/imaging/imaging_utils.py b/src/open_vp_cal/imaging/imaging_utils.py index 8eb603b..8a5b7f0 100644 --- a/src/open_vp_cal/imaging/imaging_utils.py +++ b/src/open_vp_cal/imaging/imaging_utils.py @@ -500,6 +500,26 @@ def insert_resized_image(image_a: Oiio.ImageBuf, image_b: Oiio.ImageBuf, return image_b +def resize_image(image: Oiio.ImageBuf, width: int, height: int) -> Oiio.ImageBuf: + """ + Resize the image to the specified width and height. + + Args: + image (oiio.ImageBuf): The image to be resized. + width (int): The width of the resized image. + height (int): The height of the resized image. + + Returns: + oiio.ImageBuf: The resized image. + """ + resized_image = Oiio.ImageBuf() + res = Oiio.ImageBufAlgo.resize(resized_image, image, "", 0, Oiio.ROI( + 0, width, 0, height)) + if not res: + raise ValueError("Failed to resize image buffer") + return resized_image + + def list_to_roi(roi: list) -> Oiio.ROI: """ Converts a list to an Oiio.ROI From dd8db286ca892c99df4025e32a02dd94d51170a4 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 28 May 2024 17:51:22 +0100 Subject: [PATCH 19/31] Upping version for deploy --- src/open_vp_cal/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/open_vp_cal/__init__.py b/src/open_vp_cal/__init__.py index 09a02e2..488842d 100644 --- a/src/open_vp_cal/__init__.py +++ b/src/open_vp_cal/__init__.py @@ -15,7 +15,7 @@ Init Module defines a few module level variables """ -__version__ = "1.0.0" +__version__ = "1.1.0" __authors__ = [ "Adam Davis", "Adrian Pueyo", "Carol Payne", "Francesco Luigi Giardiello", "Daniel Heckenberg" ] From ceb3c8dd0c2b0662485fd3ac5b54720a6c23d10f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 22:45:29 +0000 Subject: [PATCH 20/31] Bump urllib3 from 2.1.0 to 2.2.2 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.1.0 to 2.2.2. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.1.0...2.2.2) --- updated-dependencies: - dependency-name: urllib3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6e6bfb3..8b3c976 100644 --- a/requirements.txt +++ b/requirements.txt @@ -65,6 +65,6 @@ sphinxcontrib-serializinghtml==1.1.9 tomli==2.0.1 tomlkit==0.12.3 typing_extensions==4.9.0 -urllib3==2.1.0 +urllib3==2.2.2 wrapt==1.16.0 zipp==3.17.0 From 37acfb1e378f45b15e72ec47f86d88f7f2bbb251 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 23:51:36 +0000 Subject: [PATCH 21/31] Bump certifi from 2023.11.17 to 2024.7.4 Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.11.17 to 2024.7.4. - [Commits](https://github.com/certifi/python-certifi/compare/2023.11.17...2024.07.04) --- updated-dependencies: - dependency-name: certifi dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6e6bfb3..8faed68 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ alabaster==0.7.13 altgraph==0.17.4 astroid==3.0.2 Babel==2.14.0 -certifi==2023.11.17 +certifi==2024.7.4 charset-normalizer==3.3.2 colour-checker-detection==0.1.5 colour-science==0.4.3 From 71c37ac7016c300e2d0379bde76a534dbd987ea3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 19:09:53 +0000 Subject: [PATCH 22/31] Bump zipp from 3.17.0 to 3.19.1 Bumps [zipp](https://github.com/jaraco/zipp) from 3.17.0 to 3.19.1. - [Release notes](https://github.com/jaraco/zipp/releases) - [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/zipp/compare/v3.17.0...v3.19.1) --- updated-dependencies: - dependency-name: zipp dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6e6bfb3..4f34704 100644 --- a/requirements.txt +++ b/requirements.txt @@ -67,4 +67,4 @@ tomlkit==0.12.3 typing_extensions==4.9.0 urllib3==2.1.0 wrapt==1.16.0 -zipp==3.17.0 +zipp==3.19.1 From 9352155f37088ecc5b378ef67db2b46f24a6dafe Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 23 Jul 2024 10:41:23 +0100 Subject: [PATCH 23/31] Fix: The macbeth chart sampling so that we do the detection in sRGB-Display and then convert back to input plate gamut for more reliable detection --- src/open_vp_cal/framework/sample_patch.py | 46 +++++++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/open_vp_cal/framework/sample_patch.py b/src/open_vp_cal/framework/sample_patch.py index aa0ab2a..1c77fb3 100644 --- a/src/open_vp_cal/framework/sample_patch.py +++ b/src/open_vp_cal/framework/sample_patch.py @@ -17,9 +17,10 @@ """ import threading import numpy as np -from colour_checker_detection.detection.segmentation import \ - detect_colour_checkers_segmentation +from colour_checker_detection.detection.segmentation import ( + detect_colour_checkers_segmentation) +from open_vp_cal.core.utils import find_factors_pairs from open_vp_cal.imaging import imaging_utils from open_vp_cal.core.structures import SamplePatchResults from open_vp_cal.framework.identify_separation import SeparationResults @@ -242,18 +243,47 @@ def _sample_patch(self) -> None: for frame_num in range(first_patch_frame + self.trim_frames, (last_patch_frame - self.trim_frames) + 1): frame = self.led_wall.sequence_loader.get_frame(frame_num) - section = frame.extract_roi(self.led_wall.roi) sample_results.frames.append(frame) - section_np_array = imaging_utils.image_buf_to_np_array(section) - for colour_checker_swatches_data in detect_colour_checkers_segmentation( - section_np_array, additional_data=True): + + # Extract our region + section_orig = frame.extract_roi(self.led_wall.roi) + + # Convert this to display for the detection and convert to numpy + section_display = imaging_utils.apply_color_conversion( + section_orig, str(self.led_wall.input_plate_gamut), + "sRGB - Display" + ) + section_display_np_array = imaging_utils.image_buf_to_np_array(section_display) + + # Run the detections + detections = detect_colour_checkers_segmentation( + section_display_np_array, additional_data=True) + + for colour_checker_swatches_data in detections: + # Get the swatch colours swatch_colours, _, _ = ( colour_checker_swatches_data.values) + # Reshape the number of swatches from a 24, 3 array to an x, y, 3 array + num_swatches = swatch_colours.shape[0] + factor_pairs = find_factors_pairs(num_swatches) + x, y = factor_pairs[0] + array_x_y_3 = swatch_colours.reshape(x, y, 3) + + # Convert the colours back to the input plate gamut + imaging_utils.apply_color_converstion_to_np_array( + array_x_y_3, + "sRGB - Display", + str(self.led_wall.input_plate_gamut)) + + # Reshape the array back to a 24, 3 array + swatch_colours = array_x_y_3.reshape(num_swatches, 3) + samples.append(swatch_colours) - # Compute the mean for each tuple index across all tuples, if the detection fails and we get nans, then we - # replace the nans with black patches as these are not used in the calibration directly + # Compute the mean for each tuple index across all tuples, if the + # detection fails, and we get nans, then we replace the nans with black patches + # as these are not used in the calibration directly averaged_tuple = np.mean(np.array(samples), axis=0) if not np.isnan(averaged_tuple).any(): sample_results.samples = averaged_tuple.tolist() From eaf5e42872430ce470d3e9352b52a52b001588f6 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Tue, 23 Jul 2024 10:51:06 +0100 Subject: [PATCH 24/31] up version for next round of development --- src/open_vp_cal/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/open_vp_cal/__init__.py b/src/open_vp_cal/__init__.py index 488842d..30d954f 100644 --- a/src/open_vp_cal/__init__.py +++ b/src/open_vp_cal/__init__.py @@ -15,7 +15,7 @@ Init Module defines a few module level variables """ -__version__ = "1.1.0" +__version__ = "1.2.0" __authors__ = [ "Adam Davis", "Adrian Pueyo", "Carol Payne", "Francesco Luigi Giardiello", "Daniel Heckenberg" ] From 6116456e0e3b92bd5d8d92300b9b795511fd1600 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Wed, 24 Jul 2024 16:30:35 +0100 Subject: [PATCH 25/31] disable avoid clipping by default --- src/open_vp_cal/led_wall_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/open_vp_cal/led_wall_settings.py b/src/open_vp_cal/led_wall_settings.py index 5e7fee5..272537e 100644 --- a/src/open_vp_cal/led_wall_settings.py +++ b/src/open_vp_cal/led_wall_settings.py @@ -60,7 +60,7 @@ def __init__(self, project_settings: "ProjectSettings", name="Wall1"): constants.LedWallSettingsKeys.WHITE_POINT_OFFSET_SOURCE: "", constants.LedWallSettingsKeys.IS_VERIFICATION_WALL: False, constants.LedWallSettingsKeys.VERIFICATION_WALL: "", - constants.LedWallSettingsKeys.AVOID_CLIPPING: True + constants.LedWallSettingsKeys.AVOID_CLIPPING: False } self._led_settings = copy.deepcopy(self._default_led_settings) From f6255a3b9fddc46010d942a3bcf84edcb131c9ce Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Wed, 24 Jul 2024 16:31:03 +0100 Subject: [PATCH 26/31] Fix: Clarify the validation messages so they are more human readable --- src/open_vp_cal/framework/validation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/open_vp_cal/framework/validation.py b/src/open_vp_cal/framework/validation.py index 6e968fb..13f4f35 100644 --- a/src/open_vp_cal/framework/validation.py +++ b/src/open_vp_cal/framework/validation.py @@ -74,7 +74,7 @@ def exposure_validation(calibration_results: Dict) -> ValidationResult: if measured_18_percent < quarter_stop_down_18_percent or measured_18_percent > quarter_stop_up_18_percent: result.status = ValidationStatus.FAIL result.message = ( - f"The Measured Exposure: {measured_18_percent}\n" + f"The Exposure of the 18% Patch is measured at {round(measured_18_percent, 1) * 100}%\n" "It seems that you have not exposed the calibration patches correctly. " "Please ensure to expose the first 18% patch correctly using the camera false colour or light meter." ) @@ -87,8 +87,7 @@ def exposure_validation(calibration_results: Dict) -> ValidationResult: else: result.status = ValidationStatus.WARNING result.message = ( - f"The Measured Exposure: {measured_18_percent} is not ideal\n" - "It seems that you have not exposed the calibration patches correctly. " + f"The Exposure of the 18% Patch is measured at {round(measured_18_percent, 1) * 100}%, this is not ideal.\n" "Please ensure to expose the first 18% patch correctly using the camera false colour or light meter." ) @@ -202,7 +201,7 @@ def check_scaled_18_percent(calibration_results: Dict) -> ValidationResult: if not is_between: result.status = ValidationStatus.FAIL result.message = ( - f"When scaled the measured 18 percent patch is not within a reasonable range: {scaled_18_percent_nits}." + f"When scaled the measured 18 percent patch is not within a reasonable range: {round(scaled_18_percent_nits, 1)} nits." " Please check that the wall settings match the actual peak luminance of your wall also check your " "imaging chain from content engine to LED processor and re shoot the plates" ) From d8039f567d089b888e1db902914d2a77a93be07b Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Wed, 24 Jul 2024 16:32:04 +0100 Subject: [PATCH 27/31] Fix: To improve the macbeth detection we normalize the images into a non linear format which results in better detection, which we then undo the normalization to acesscct to get the orginal colour values back --- src/open_vp_cal/framework/sample_patch.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/open_vp_cal/framework/sample_patch.py b/src/open_vp_cal/framework/sample_patch.py index 1c77fb3..8ba3f06 100644 --- a/src/open_vp_cal/framework/sample_patch.py +++ b/src/open_vp_cal/framework/sample_patch.py @@ -248,12 +248,12 @@ def _sample_patch(self) -> None: # Extract our region section_orig = frame.extract_roi(self.led_wall.roi) - # Convert this to display for the detection and convert to numpy - section_display = imaging_utils.apply_color_conversion( - section_orig, str(self.led_wall.input_plate_gamut), - "sRGB - Display" + section_display_np_array = imaging_utils.image_buf_to_np_array(section_orig) + imaging_utils.apply_color_converstion_to_np_array( + section_display_np_array, + str(self.led_wall.input_plate_gamut), + "ACEScct", ) - section_display_np_array = imaging_utils.image_buf_to_np_array(section_display) # Run the detections detections = detect_colour_checkers_segmentation( @@ -261,8 +261,8 @@ def _sample_patch(self) -> None: for colour_checker_swatches_data in detections: # Get the swatch colours - swatch_colours, _, _ = ( - colour_checker_swatches_data.values) + swatch_colours, _, _ = colour_checker_swatches_data.values + swatch_colours = np.array(swatch_colours, dtype=np.float32) # Reshape the number of swatches from a 24, 3 array to an x, y, 3 array num_swatches = swatch_colours.shape[0] @@ -273,7 +273,7 @@ def _sample_patch(self) -> None: # Convert the colours back to the input plate gamut imaging_utils.apply_color_converstion_to_np_array( array_x_y_3, - "sRGB - Display", + "ACEScct", str(self.led_wall.input_plate_gamut)) # Reshape the array back to a 24, 3 array From 99dff818776f887ffc8360099f6dbc60d5d68076 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Sat, 27 Jul 2024 11:04:54 +0100 Subject: [PATCH 28/31] Fix: Add methods to calculate white balance from the slate, and apply this as a white balance matrix, during speration, auto roi detection, and macbeth detection. Which improves accuracy when calibrating walls where the recorded colour is so heavily tinted we are unable to recognize the patches! --- src/open_vp_cal/framework/auto_roi.py | 35 ++++++++---- .../framework/identify_separation.py | 12 +++++ src/open_vp_cal/framework/sample_patch.py | 26 ++++++++- src/open_vp_cal/imaging/imaging_utils.py | 53 +++++++++++++++++++ 4 files changed, 116 insertions(+), 10 deletions(-) diff --git a/src/open_vp_cal/framework/auto_roi.py b/src/open_vp_cal/framework/auto_roi.py index 86de718..0c2d019 100644 --- a/src/open_vp_cal/framework/auto_roi.py +++ b/src/open_vp_cal/framework/auto_roi.py @@ -19,10 +19,11 @@ import sys from typing import List + from open_vp_cal.core.utils import clamp from open_vp_cal.led_wall_settings import LedWallSettings - +from open_vp_cal.imaging import imaging_utils from open_vp_cal.framework.sample_patch import BaseSamplePatch from open_vp_cal.framework.identify_separation import SeparationResults from open_vp_cal.core import constants @@ -32,6 +33,7 @@ class AutoROIResults: """ Class to store the results of the roi detection """ + def __init__(self): """ Initialize an instance of AutoROIResults. @@ -144,14 +146,17 @@ class AutoROI(BaseSamplePatch): The main class which deals with identifying the region of interest within the image sequence which we want to extract """ - def __init__(self, led_wall_settings: LedWallSettings, separation_results: SeparationResults): + + def __init__(self, led_wall_settings: LedWallSettings, + separation_results: SeparationResults): """ Initialize an instance of AutoROI Args: led_wall_settings: The LED wall we want to detect the roi for separation_results: The results of the separation detection for the LED wall sequence """ - super().__init__(led_wall_settings, separation_results, constants.PATCHES.DISTORT_AND_ROI) + super().__init__(led_wall_settings, separation_results, + constants.PATCHES.DISTORT_AND_ROI) def run(self) -> AutoROIResults: """ @@ -166,12 +171,22 @@ def run(self) -> AutoROIResults: if first_patch_frame > self.led_wall.sequence_loader.end_frame: return results - frame = self.led_wall.sequence_loader.get_frame(first_patch_frame + self.trim_frames) + frame = self.led_wall.sequence_loader.get_frame( + first_patch_frame + self.trim_frames) + pixel_buffer = 5 detection_threshold = 1.7 - for y_pos in range(frame.image_buf.spec().height): - for x_pos in range(frame.image_buf.spec().width): - pixel = frame.image_buf.getpixel(x_pos, y_pos) + + # Create the white balance matrix + white_balance_matrix = self.get_white_balance_matrix_from_slate() + + # Apply the white balance matrix to the frame + balanced_image = imaging_utils.apply_matrix_to_img_buf( + frame.image_buf, white_balance_matrix + ) + for y_pos in range(balanced_image.spec().height): + for x_pos in range(balanced_image.spec().width): + pixel = balanced_image.getpixel(x_pos, y_pos) red = clamp(pixel[0], 0, sys.float_info.max) green = clamp(pixel[1], 0, sys.float_info.max) blue = clamp(pixel[2], 0, sys.float_info.max) @@ -183,12 +198,14 @@ def run(self) -> AutoROIResults: if green > results.green_value: if green > max(red, blue) * detection_threshold: - results.green_pixel = (x_pos - pixel_buffer, y_pos + pixel_buffer) + results.green_pixel = ( + x_pos - pixel_buffer, y_pos + pixel_buffer) results.green_value = green if blue > results.blue_value: if blue > max(red, green) * detection_threshold: - results.blue_pixel = (x_pos + pixel_buffer, y_pos - pixel_buffer) + results.blue_pixel = ( + x_pos + pixel_buffer, y_pos - pixel_buffer) results.blue_value = blue white = (red + green + blue) / 3 diff --git a/src/open_vp_cal/framework/identify_separation.py b/src/open_vp_cal/framework/identify_separation.py index d978a33..061881a 100644 --- a/src/open_vp_cal/framework/identify_separation.py +++ b/src/open_vp_cal/framework/identify_separation.py @@ -127,10 +127,22 @@ def _find_first_red_and_green_frames(self) -> None: distances = [] previous_mean_frame = None + + slate_frame = self.led_wall.sequence_loader.get_frame( + self.led_wall.sequence_loader.start_frame + ) + white_balance_matrix = imaging_utils.calculate_white_balance_matrix_from_img_buf( + slate_frame.image_buf) + for frame in self.led_wall.sequence_loader: # Load the image from the frame image = frame.extract_roi(self.led_wall.roi) + # Apply the white balance matrix to the frame + image = imaging_utils.apply_matrix_to_img_buf( + image, white_balance_matrix + ) + # Compute the average for all the values which are above the initial average mean_color, _ = imaging_utils.get_average_value_above_average(image) distance = 0 diff --git a/src/open_vp_cal/framework/sample_patch.py b/src/open_vp_cal/framework/sample_patch.py index 8ba3f06..bfdfbad 100644 --- a/src/open_vp_cal/framework/sample_patch.py +++ b/src/open_vp_cal/framework/sample_patch.py @@ -82,6 +82,20 @@ def calculate_first_and_last_patch_frame(self) -> tuple[int, int]: self.trim_frames = trim_frames return first_patch_frame, last_patch_frame + def get_white_balance_matrix_from_slate(self) -> np.ndarray: + """ Get the white balance matrix from the slate frame + + Returns: + np.ndarray: The white balance matrix + + """ + slate_frame = self.led_wall.sequence_loader.get_frame( + self.led_wall.sequence_loader.start_frame + ) + white_balance_matrix = imaging_utils.calculate_white_balance_matrix_from_img_buf( + slate_frame.image_buf) + return white_balance_matrix + class SamplePatch(BaseSamplePatch): """ @@ -240,6 +254,7 @@ def _sample_patch(self) -> None: # We trim a number of frames off either side of the patch to ensure we remove multiplexing sample_results = SamplePatchResults() samples = [] + white_balance_matrix = self.get_white_balance_matrix_from_slate() for frame_num in range(first_patch_frame + self.trim_frames, (last_patch_frame - self.trim_frames) + 1): frame = self.led_wall.sequence_loader.get_frame(frame_num) @@ -248,6 +263,12 @@ def _sample_patch(self) -> None: # Extract our region section_orig = frame.extract_roi(self.led_wall.roi) + # White balance the images so we increase the detection likelihood of + # success + section_orig = imaging_utils.apply_matrix_to_img_buf( + section_orig, white_balance_matrix + ) + section_display_np_array = imaging_utils.image_buf_to_np_array(section_orig) imaging_utils.apply_color_converstion_to_np_array( section_display_np_array, @@ -276,9 +297,12 @@ def _sample_patch(self) -> None: "ACEScct", str(self.led_wall.input_plate_gamut)) + # Inverse the white balance back to the original values + inv_wb_matrix = np.linalg.inv(white_balance_matrix) + array_x_y_3 = array_x_y_3 @ inv_wb_matrix + # Reshape the array back to a 24, 3 array swatch_colours = array_x_y_3.reshape(num_swatches, 3) - samples.append(swatch_colours) # Compute the mean for each tuple index across all tuples, if the diff --git a/src/open_vp_cal/imaging/imaging_utils.py b/src/open_vp_cal/imaging/imaging_utils.py index 8a5b7f0..682084d 100644 --- a/src/open_vp_cal/imaging/imaging_utils.py +++ b/src/open_vp_cal/imaging/imaging_utils.py @@ -933,3 +933,56 @@ def is_within_range(value: float, target: float, x: float) -> bool: bool: True if value is within x units of target, False otherwise. """ return abs(value - target) <= x + + +def calculate_white_balance_matrix_from_img_buf( + image_buf: Oiio.ImageBuf, remove_percent: float = 0.20) -> np.array: + """ Takes a given image buffer and removes 20% of the pixels around the image, + returning a white balance matrix based on the average RGB values + of the center section + + Args: + image_buf: The image buffer to calculate the white balance matrix from + remove_percent: The percentage of pixels to remove from the image + + Returns: + np.array: The white balance matrix + + """ + image_np_array = image_buf_to_np_array(image_buf) + height, width, _ = image_np_array.shape + remove_height = int(height * remove_percent) + remove_width = int(width * remove_percent) + + # Slice the array to remove 20% from each side + center_section = image_np_array[ + remove_height:height - remove_height, + remove_width:width - remove_width + ] + + average_rgb = np.mean(center_section, axis=(0, 1)) + red_mult_val = average_rgb[1] / average_rgb[0] + blue_mult_val = average_rgb[1] / average_rgb[2] + white_balance_matrix = np.asarray( + [[red_mult_val, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, blue_mult_val]]) + return white_balance_matrix + + +def apply_matrix_to_img_buf( + image_buf: Oiio.ImageBuf, matrix: np.array) -> Oiio.ImageBuf: + """ Applies the given matrix to the image buffer, for instance a white balancing + matrix + + Args: + image_buf: The image buffer to apply the matrix to + matrix: The matrix to apply to the image buffer + + Returns: + Oiio.ImageBuf: The image buffer with the matrix applied + + """ + frame_np_array = image_buf_to_np_array(image_buf) + image_reshaped = frame_np_array.reshape((-1, 3)) + white_balanced_image = image_reshaped @ matrix + white_balanced_image = white_balanced_image.reshape(frame_np_array.shape) + return img_buf_from_numpy_array(white_balanced_image) From 8c424c968833d5422f08dc4f6a005b725689fd46 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Sat, 27 Jul 2024 11:21:00 +0100 Subject: [PATCH 29/31] Fix: Reduce the inner black boader of the 18% patch on the slate to a single pixel wide to avoid people over bluring the image --- src/open_vp_cal/framework/generation.py | 2 +- .../OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/open_vp_cal/framework/generation.py b/src/open_vp_cal/framework/generation.py index b7ece57..03148da 100644 --- a/src/open_vp_cal/framework/generation.py +++ b/src/open_vp_cal/framework/generation.py @@ -931,7 +931,7 @@ def _add_slate_inner_squares( """ patch_roi = Oiio.ROI(start_x, start_x + patch_width, start_y, start_y + patch_height) inner_edge_roi = self.reduce_roi(patch_roi, 1) - inner_patch_roi = self.reduce_roi(inner_edge_roi, 1) + inner_patch_roi = self.reduce_roi(inner_edge_roi, 0.25) Oiio.ImageBufAlgo.fill(patch, (self.percent_18_lum, self.percent_18_lum, self.percent_18_lum), roi=patch_roi) Oiio.ImageBufAlgo.fill(patch, (0.0, 0.0, 0.0), roi=inner_edge_roi) Oiio.ImageBufAlgo.fill(patch, (self.percent_18_lum, self.percent_18_lum, self.percent_18_lum), diff --git a/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr b/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr index 32ee471..819e10f 100644 --- a/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr +++ b/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08ca6cb664101923e74c82804e81d197d3ba04e20006c2ff203b2a47715b3f6c +oid sha256:56b0a1d660eb5c6b0de0b6bfc0760474a554de13427acf3ce25db519f8d8d3e6 size 49801538 From 7b414d6312faa71a9a97b67048a8dee84e061b12 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Sat, 27 Jul 2024 13:53:53 -0400 Subject: [PATCH 30/31] adjust border thickness to two pixels wide --- src/open_vp_cal/framework/generation.py | 2 +- .../OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/open_vp_cal/framework/generation.py b/src/open_vp_cal/framework/generation.py index 03148da..9929d88 100644 --- a/src/open_vp_cal/framework/generation.py +++ b/src/open_vp_cal/framework/generation.py @@ -931,7 +931,7 @@ def _add_slate_inner_squares( """ patch_roi = Oiio.ROI(start_x, start_x + patch_width, start_y, start_y + patch_height) inner_edge_roi = self.reduce_roi(patch_roi, 1) - inner_patch_roi = self.reduce_roi(inner_edge_roi, 0.25) + inner_patch_roi = self.reduce_roi(inner_edge_roi, 0.5) Oiio.ImageBufAlgo.fill(patch, (self.percent_18_lum, self.percent_18_lum, self.percent_18_lum), roi=patch_roi) Oiio.ImageBufAlgo.fill(patch, (0.0, 0.0, 0.0), roi=inner_edge_roi) Oiio.ImageBufAlgo.fill(patch, (self.percent_18_lum, self.percent_18_lum, self.percent_18_lum), diff --git a/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr b/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr index 819e10f..9c76aaa 100644 --- a/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr +++ b/tests/test_open_vp_cal/resources/export/patches/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084/OpenVPCal_Wall1_ITU_R_BT_2020_ST_2084.000000.exr @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56b0a1d660eb5c6b0de0b6bfc0760474a554de13427acf3ce25db519f8d8d3e6 +oid sha256:6cffcf4c732702a14fd5dc4829dc27c0d0fd6e3ff88d9b4eaf22ffeda402ba9f size 49801538 From 83c5d3ae173889f7bc74be880fa8227e939f1428 Mon Sep 17 00:00:00 2001 From: Adam Davis Date: Fri, 2 Aug 2024 10:21:05 -0600 Subject: [PATCH 31/31] Add in a conda environment for easier building as this handles everything invluding oiio --- environment.yml | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 environment.yml diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..e615421 --- /dev/null +++ b/environment.yml @@ -0,0 +1,80 @@ +name: OpenVPCal +channels: + - conda-forge + - defaults + - pytorch-nightly +dependencies: + - python=3.11 + - conda-forge::openimageio=2.5.9.0 + - conda-forge::py-openimageio=2.5.9.0 + - pip: + - alabaster==0.7.13 + - altgraph==0.17.4 + - astroid==3.0.2 + - Babel==2.14.0 + - certifi==2024.7.4 + - charset-normalizer==3.3.2 + - colour-checker-detection==0.1.5 + - colour-science==0.4.3 + - contourpy==1.2.0 + - coverage==7.3.3 + - cycler==0.12.1 + - dill==0.3.7 + - docutils==0.20.1 + - execnet==2.0.2 + - flake8==6.1.0 + - fonttools==4.46.0 + - idna==3.7 + - imageio==2.33.1 + - imagesize==1.4.1 + - importlib-resources==6.1.1 + - iniconfig==2.0.0 + - isort==5.13.2 + - Jinja2==3.1.4 + - kiwisolver==1.4.5 + - lazy-object-proxy==1.9.0 + - macholib==1.16.3 + - MarkupSafe==2.1.3 + - matplotlib==3.8.2 + - mccabe==0.7.0 + - numpy==1.26.2 + - opencolorio==2.3.1 + - opencv-python==4.8.1.78 + - packaging==23.2 + - pillow==10.3.0 + - platformdirs==4.1.0 + - pluggy==1.3.0 + - pycodestyle==2.11.1 + - pyflakes==3.1.0 + - Pygments==2.17.2 + - pyinstaller==6.3.0 + - pyinstaller-hooks-contrib==2023.10 + - pylint==3.0.3 + - pyparsing==3.1.1 + - pyqtgraph==0.13.3 + - PySide6==6.5.3 + - PySide6-Addons==6.5.3 + - PySide6-Essentials==6.5.3 + - pytest==7.4.3 + - pytest-xdist==3.5.0 + - python-dateutil==2.8.2 + - requests==2.32.0 + - scipy==1.11.4 + - shiboken6==6.5.3 + - six==1.16.0 + - snowballstemmer==2.2.0 + - Sphinx==7.2.6 + - sphinx-rtd-theme==2.0.0 + - sphinxcontrib-applehelp==1.0.7 + - sphinxcontrib-devhelp==1.0.5 + - sphinxcontrib-htmlhelp==2.0.4 + - sphinxcontrib-jquery==4.1 + - sphinxcontrib-jsmath==1.0.1 + - sphinxcontrib-qthelp==1.0.6 + - sphinxcontrib-serializinghtml==1.1.9 + - tomli==2.0.1 + - tomlkit==0.12.3 + - typing_extensions==4.9.0 + - urllib3==2.2.2 + - wrapt==1.16.0 + - zipp==3.19.1 \ No newline at end of file
-EXTERNAL WHITE BALANCE CALIBRATION CALIBRATION +WHITE BALANCE OFFSET CALIBRATION CALIBRATION