More Refactor Fixes, Issuetracker updated
This commit is contained in:
parent
beb8640085
commit
5bf53f036c
@ -2,6 +2,7 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import List
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
from .base_stage import ProcessingStage
|
from .base_stage import ProcessingStage
|
||||||
from ..asset_context import AssetProcessingContext
|
from ..asset_context import AssetProcessingContext
|
||||||
@ -35,135 +36,158 @@ class GlossToRoughConversionStage(ProcessingStage):
|
|||||||
logger.debug(f"Asset '{asset_name_for_log}': Skipping GlossToRoughConversionStage due to skip_asset flag.")
|
logger.debug(f"Asset '{asset_name_for_log}': Skipping GlossToRoughConversionStage due to skip_asset flag.")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
if not context.files_to_process or not context.processed_maps_details:
|
if not context.processed_maps_details: # files_to_process might be empty if only gloss maps existed and all are converted
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Asset '{asset_name_for_log}': No files to process or processed_maps_details empty "
|
f"Asset '{asset_name_for_log}': processed_maps_details is empty in GlossToRoughConversionStage. Skipping."
|
||||||
f"in GlossToRoughConversionStage. Skipping."
|
|
||||||
)
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
new_files_to_process: List[FileRule] = []
|
# Start with a copy of the current file rules. We will modify this list.
|
||||||
|
new_files_to_process: List[FileRule] = list(context.files_to_process) if context.files_to_process else []
|
||||||
processed_a_gloss_map = False
|
processed_a_gloss_map = False
|
||||||
|
successful_conversion_statuses = ['BasePOTSaved', 'Processed_With_Variants', 'Processed_No_Variants']
|
||||||
|
|
||||||
logger.info(f"Asset '{asset_name_for_log}': Starting Gloss to Roughness Conversion Stage.")
|
logger.info(f"Asset '{asset_name_for_log}': Starting Gloss to Roughness Conversion Stage. Examining {len(context.processed_maps_details)} processed map entries.")
|
||||||
|
|
||||||
for idx, file_rule in enumerate(context.files_to_process):
|
# Iterate using the index (map_key_index) as the key, which is now standard.
|
||||||
# Assuming FileRule has 'map_type' and 'id' (with a .hex attribute) and 'source_file_path'
|
for map_key_index, map_details in context.processed_maps_details.items():
|
||||||
# These might need to be checked with hasattr if they are optional or could be missing
|
processing_map_type = map_details.get('processing_map_type', '')
|
||||||
if hasattr(file_rule, 'map_type') and file_rule.map_type == "GLOSS":
|
map_status = map_details.get('status')
|
||||||
if not hasattr(file_rule, 'id') or not hasattr(file_rule.id, 'hex'):
|
original_temp_path_str = map_details.get('temp_processed_file')
|
||||||
logger.warning(f"Asset '{asset_name_for_log}': GLOSS FileRule missing 'id.hex'. Skipping conversion for this rule: {file_rule}")
|
# source_file_rule_idx from details should align with map_key_index.
|
||||||
new_files_to_process.append(file_rule)
|
# We primarily use map_key_index for accessing FileRule from context.files_to_process.
|
||||||
continue
|
source_file_rule_idx_from_details = map_details.get('source_file_rule_index')
|
||||||
map_detail_key = file_rule.id.hex
|
processing_tag = map_details.get('processing_tag')
|
||||||
|
|
||||||
source_file_path_for_log = file_rule.source_file_path if hasattr(file_rule, 'source_file_path') else "Unknown source path"
|
if map_key_index != source_file_rule_idx_from_details:
|
||||||
|
|
||||||
if map_detail_key not in context.processed_maps_details:
|
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Asset '{asset_name_for_log}': GLOSS map '{source_file_path_for_log}' "
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index}: Mismatch between map key index and 'source_file_rule_index' ({source_file_rule_idx_from_details}) in details. "
|
||||||
f"(ID: {map_detail_key}) found in files_to_process but not in processed_maps_details. "
|
f"Using map_key_index ({map_key_index}) for FileRule lookup. This might indicate a data consistency issue from previous stage."
|
||||||
f"Adding original rule and skipping conversion for this map."
|
|
||||||
)
|
)
|
||||||
new_files_to_process.append(file_rule)
|
|
||||||
|
if not processing_tag:
|
||||||
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index}: 'processing_tag' is missing in map_details. Using a fallback for temp filename. This is unexpected.")
|
||||||
|
processing_tag = f"mki_{map_key_index}_fallback_tag"
|
||||||
|
|
||||||
|
|
||||||
|
if not processing_map_type.startswith("MAP_GLOSS"):
|
||||||
|
# logger.debug(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index}: Type '{processing_map_type}' is not GLOSS. Skipping.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
map_details = context.processed_maps_details[map_detail_key]
|
logger.info(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Identified potential GLOSS map (Type: {processing_map_type}).")
|
||||||
|
|
||||||
if map_details.get('status') != 'Processed' or 'temp_processed_file' not in map_details:
|
if map_status not in successful_conversion_statuses:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Asset '{asset_name_for_log}': GLOSS map '{source_file_path_for_log}' "
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}) (GLOSS): Status '{map_status}' is not one of {successful_conversion_statuses}. "
|
||||||
f"(ID: {map_detail_key}) not successfully processed by previous stage or temp file missing. "
|
f"Skipping conversion for this map."
|
||||||
f"Status: {map_details.get('status')}. Adding original rule and skipping conversion."
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not original_temp_path_str:
|
||||||
|
logger.warning(
|
||||||
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}) (GLOSS): 'temp_processed_file' missing in details. "
|
||||||
|
f"Skipping conversion."
|
||||||
)
|
)
|
||||||
new_files_to_process.append(file_rule)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
original_temp_path_str = map_details['temp_processed_file']
|
|
||||||
original_temp_path = Path(original_temp_path_str)
|
original_temp_path = Path(original_temp_path_str)
|
||||||
|
|
||||||
if not original_temp_path.exists():
|
if not original_temp_path.exists():
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Asset '{asset_name_for_log}': Temporary file {original_temp_path_str} for GLOSS map "
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}) (GLOSS): Temporary file {original_temp_path_str} "
|
||||||
f"(ID: {map_detail_key}) does not exist. Adding original rule and skipping conversion."
|
f"does not exist. Skipping conversion."
|
||||||
)
|
)
|
||||||
new_files_to_process.append(file_rule)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.debug(f"Asset '{asset_name_for_log}': Processing GLOSS map {original_temp_path} for conversion.")
|
# Use map_key_index directly to access the FileRule
|
||||||
image_data = ipu.load_image(original_temp_path)
|
# Ensure map_key_index is a valid index for context.files_to_process
|
||||||
|
if not isinstance(map_key_index, int) or map_key_index < 0 or map_key_index >= len(context.files_to_process):
|
||||||
|
logger.error(
|
||||||
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}) (GLOSS): Invalid map_key_index ({map_key_index}) for accessing files_to_process (len: {len(context.files_to_process)}). "
|
||||||
|
f"Skipping conversion."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
original_file_rule = context.files_to_process[map_key_index]
|
||||||
|
source_file_path_for_log = original_file_rule.file_path if hasattr(original_file_rule, 'file_path') else "Unknown source path"
|
||||||
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Processing GLOSS map from '{original_temp_path_str}' (Original FileRule path: '{source_file_path_for_log}') for conversion.")
|
||||||
|
|
||||||
|
image_data = ipu.load_image(str(original_temp_path))
|
||||||
if image_data is None:
|
if image_data is None:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Asset '{asset_name_for_log}': Failed to load image data from {original_temp_path} "
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Failed to load image data from {original_temp_path_str}. "
|
||||||
f"for GLOSS map (ID: {map_detail_key}). Adding original rule and skipping conversion."
|
f"Skipping conversion."
|
||||||
)
|
)
|
||||||
new_files_to_process.append(file_rule)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Perform Inversion
|
# Perform Inversion
|
||||||
inverted_image_data: np.ndarray
|
inverted_image_data: np.ndarray
|
||||||
if np.issubdtype(image_data.dtype, np.floating):
|
if np.issubdtype(image_data.dtype, np.floating):
|
||||||
inverted_image_data = 1.0 - image_data
|
inverted_image_data = 1.0 - image_data
|
||||||
inverted_image_data = np.clip(inverted_image_data, 0.0, 1.0) # Ensure range for floats
|
inverted_image_data = np.clip(inverted_image_data, 0.0, 1.0)
|
||||||
logger.debug(f"Asset '{asset_name_for_log}': Inverted float image data for {original_temp_path}.")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Inverted float image data.")
|
||||||
elif np.issubdtype(image_data.dtype, np.integer):
|
elif np.issubdtype(image_data.dtype, np.integer):
|
||||||
max_val = np.iinfo(image_data.dtype).max
|
max_val = np.iinfo(image_data.dtype).max
|
||||||
inverted_image_data = max_val - image_data
|
inverted_image_data = max_val - image_data
|
||||||
logger.debug(f"Asset '{asset_name_for_log}': Inverted integer image data (max_val: {max_val}) for {original_temp_path}.")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Inverted integer image data (max_val: {max_val}).")
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Asset '{asset_name_for_log}': Unsupported image data type {image_data.dtype} "
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Unsupported image data type {image_data.dtype} "
|
||||||
f"for GLOSS map {original_temp_path}. Cannot invert. Adding original rule."
|
f"for GLOSS map. Cannot invert. Skipping conversion."
|
||||||
)
|
)
|
||||||
new_files_to_process.append(file_rule)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Save New Temporary (Roughness) Map
|
# Save New Temporary (Roughness) Map
|
||||||
# Using original_temp_path.suffix ensures we keep the format (e.g., .png, .exr)
|
new_temp_filename = f"rough_from_gloss_{processing_tag}{original_temp_path.suffix}"
|
||||||
# Ensure file_rule.map_type exists before using sanitize_filename
|
|
||||||
map_type_for_filename = file_rule.map_type if hasattr(file_rule, 'map_type') else "unknownmaptype"
|
|
||||||
new_temp_filename = f"rough_from_gloss_{sanitize_filename(map_type_for_filename)}_{file_rule.id.hex}{original_temp_path.suffix}"
|
|
||||||
new_temp_path = context.engine_temp_dir / new_temp_filename
|
new_temp_path = context.engine_temp_dir / new_temp_filename
|
||||||
|
|
||||||
save_success = ipu.save_image(new_temp_path, inverted_image_data)
|
save_success = ipu.save_image(str(new_temp_path), inverted_image_data)
|
||||||
|
|
||||||
if save_success:
|
if save_success:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Asset '{asset_name_for_log}': Converted GLOSS map {original_temp_path} "
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Converted GLOSS map {original_temp_path_str} "
|
||||||
f"to ROUGHNESS map {new_temp_path}."
|
f"to ROUGHNESS map {new_temp_path}."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Assuming FileRule has model_copy method
|
update_dict = {'item_type': "MAP_ROUGH", 'item_type_override': "MAP_ROUGH"}
|
||||||
modified_file_rule = file_rule.model_copy(deep=True) if hasattr(file_rule, 'model_copy') else file_rule
|
|
||||||
modified_file_rule.map_type = "ROUGHNESS" # Ensure map_type can be set
|
|
||||||
|
|
||||||
# Update context.processed_maps_details for the original file_rule.id.hex
|
modified_file_rule: Optional[FileRule] = None
|
||||||
context.processed_maps_details[map_detail_key]['temp_processed_file'] = str(new_temp_path)
|
if hasattr(original_file_rule, 'model_copy') and callable(original_file_rule.model_copy): # Pydantic
|
||||||
context.processed_maps_details[map_detail_key]['original_map_type_before_conversion'] = "GLOSS"
|
modified_file_rule = original_file_rule.model_copy(update=update_dict)
|
||||||
context.processed_maps_details[map_detail_key]['notes'] = "Converted from GLOSS by GlossToRoughConversionStage"
|
elif dataclasses.is_dataclass(original_file_rule): # Dataclass
|
||||||
|
modified_file_rule = dataclasses.replace(original_file_rule, **update_dict)
|
||||||
|
else:
|
||||||
|
logger.error(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Original FileRule is neither Pydantic nor dataclass. Cannot modify. Skipping update for this rule.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_files_to_process[map_key_index] = modified_file_rule # Replace using map_key_index
|
||||||
|
|
||||||
|
# Update context.processed_maps_details for this map_key_index
|
||||||
|
map_details['temp_processed_file'] = str(new_temp_path)
|
||||||
|
map_details['original_map_type_before_conversion'] = processing_map_type
|
||||||
|
map_details['processing_map_type'] = "MAP_ROUGH"
|
||||||
|
map_details['map_type'] = "Roughness"
|
||||||
|
map_details['status'] = "Converted_To_Rough"
|
||||||
|
map_details['notes'] = map_details.get('notes', '') + "; Converted from GLOSS by GlossToRoughConversionStage"
|
||||||
|
if 'base_pot_resolution_name' in map_details:
|
||||||
|
map_details['processed_resolution_name'] = map_details['base_pot_resolution_name']
|
||||||
|
|
||||||
new_files_to_process.append(modified_file_rule)
|
|
||||||
processed_a_gloss_map = True
|
processed_a_gloss_map = True
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Asset '{asset_name_for_log}': Failed to save inverted ROUGHNESS map to {new_temp_path} "
|
f"Asset '{asset_name_for_log}', Map Key Index {map_key_index} (Tag: {processing_tag}): Failed to save inverted ROUGHNESS map to {new_temp_path}. "
|
||||||
f"for original GLOSS map (ID: {map_detail_key}). Adding original rule."
|
f"Original GLOSS FileRule remains."
|
||||||
)
|
)
|
||||||
new_files_to_process.append(file_rule)
|
|
||||||
else: # Not a gloss map
|
|
||||||
new_files_to_process.append(file_rule)
|
|
||||||
|
|
||||||
context.files_to_process = new_files_to_process
|
context.files_to_process = new_files_to_process
|
||||||
|
|
||||||
if processed_a_gloss_map:
|
if processed_a_gloss_map:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Asset '{asset_name_for_log}': Gloss to Roughness conversion stage successfully processed one or more maps and updated file list."
|
f"Asset '{asset_name_for_log}': Gloss to Roughness conversion stage finished. Processed one or more maps and updated file list and map details."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(
|
logger.info(
|
||||||
f"Asset '{asset_name_for_log}': No gloss maps were successfully converted in GlossToRoughConversionStage. "
|
f"Asset '{asset_name_for_log}': No gloss maps were converted in GlossToRoughConversionStage. "
|
||||||
f"File list for next stage contains original non-gloss maps and any gloss maps that failed conversion."
|
f"File list for next stage contains original non-gloss maps and any gloss maps that failed or were ineligible for conversion."
|
||||||
)
|
)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
@ -48,9 +48,9 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
context.status_flags['individual_map_processing_failed'] = True
|
context.status_flags['individual_map_processing_failed'] = True
|
||||||
# Mark all file_rules as failed
|
# Mark all file_rules as failed
|
||||||
for fr_idx, file_rule_to_fail in enumerate(context.files_to_process):
|
for fr_idx, file_rule_to_fail in enumerate(context.files_to_process):
|
||||||
temp_id_for_fail = f"fr_fail_{fr_idx}" # Temporary ID for status update
|
# Use fr_idx as the key for status update for these early failures
|
||||||
map_type_for_fail = file_rule_to_fail.item_type_override or file_rule_to_fail.item_type or "UnknownMapType"
|
map_type_for_fail = file_rule_to_fail.item_type_override or file_rule_to_fail.item_type or "UnknownMapType"
|
||||||
self._update_file_rule_status(context, temp_id_for_fail, 'Failed', map_type=map_type_for_fail, details="SourceRule.input_path missing")
|
self._update_file_rule_status(context, fr_idx, 'Failed', map_type=map_type_for_fail, details="SourceRule.input_path missing")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
# The workspace_path in the context should be the directory where files are extracted/available.
|
# The workspace_path in the context should be the directory where files are extracted/available.
|
||||||
@ -59,9 +59,9 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
logger.error(f"Asset '{asset_name_for_log}': Workspace path '{source_base_path}' is not a valid directory.")
|
logger.error(f"Asset '{asset_name_for_log}': Workspace path '{source_base_path}' is not a valid directory.")
|
||||||
context.status_flags['individual_map_processing_failed'] = True
|
context.status_flags['individual_map_processing_failed'] = True
|
||||||
for fr_idx, file_rule_to_fail in enumerate(context.files_to_process):
|
for fr_idx, file_rule_to_fail in enumerate(context.files_to_process):
|
||||||
temp_id_for_fail = f"fr_fail_{fr_idx}" # Use a temporary unique ID for this status update
|
# Use fr_idx as the key for status update
|
||||||
map_type_for_fail = file_rule_to_fail.item_type_override or file_rule_to_fail.item_type or "UnknownMapType"
|
map_type_for_fail = file_rule_to_fail.item_type_override or file_rule_to_fail.item_type or "UnknownMapType"
|
||||||
self._update_file_rule_status(context, temp_id_for_fail, 'Failed', map_type=map_type_for_fail, details="Workspace path invalid")
|
self._update_file_rule_status(context, fr_idx, 'Failed', map_type=map_type_for_fail, details="Workspace path invalid")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
# Fetch config settings once before the loop
|
# Fetch config settings once before the loop
|
||||||
@ -70,8 +70,17 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
output_filename_pattern = getattr(context.config_obj, "output_filename_pattern", "[assetname]_[maptype]_[resolution].[ext]")
|
output_filename_pattern = getattr(context.config_obj, "output_filename_pattern", "[assetname]_[maptype]_[resolution].[ext]")
|
||||||
|
|
||||||
for file_rule_idx, file_rule in enumerate(context.files_to_process):
|
for file_rule_idx, file_rule in enumerate(context.files_to_process):
|
||||||
# Generate a unique ID for this file_rule processing instance for processed_maps_details
|
# file_rule_idx will be the key for processed_maps_details.
|
||||||
current_map_id_hex = f"map_{file_rule_idx}_{uuid.uuid4().hex[:8]}"
|
# processing_instance_tag is for unique temp files and detailed logging for this specific run.
|
||||||
|
processing_instance_tag = f"map_{file_rule_idx}_{uuid.uuid4().hex[:8]}"
|
||||||
|
current_map_key = file_rule_idx # Key for processed_maps_details
|
||||||
|
|
||||||
|
if not file_rule.file_path: # Ensure file_path exists, critical for later stages if they rely on it from FileRule
|
||||||
|
logger.error(f"Asset '{asset_name_for_log}', FileRule at index {file_rule_idx} has an empty or None file_path. Skipping this rule.")
|
||||||
|
self._update_file_rule_status(context, current_map_key, 'Failed',
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
details="FileRule has no file_path")
|
||||||
|
continue
|
||||||
|
|
||||||
initial_current_map_type = file_rule.item_type_override or file_rule.item_type or "UnknownMapType"
|
initial_current_map_type = file_rule.item_type_override or file_rule.item_type or "UnknownMapType"
|
||||||
|
|
||||||
@ -130,89 +139,98 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
# --- END NEW SUFFIXING LOGIC ---
|
# --- END NEW SUFFIXING LOGIC ---
|
||||||
|
|
||||||
# --- START: Filename-friendly map type derivation ---
|
# --- START: Filename-friendly map type derivation ---
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: --- Starting Filename-Friendly Map Type Logic for: {current_map_type} ---")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: --- Starting Filename-Friendly Map Type Logic for: {current_map_type} ---")
|
||||||
filename_friendly_map_type = current_map_type # Fallback
|
filename_friendly_map_type = current_map_type # Fallback
|
||||||
|
|
||||||
# 1. Access FILE_TYPE_DEFINITIONS
|
# 1. Access FILE_TYPE_DEFINITIONS
|
||||||
file_type_definitions = None
|
file_type_definitions = None
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Attempting to access context.config_obj.FILE_TYPE_DEFINITIONS.")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Attempting to access context.config_obj.FILE_TYPE_DEFINITIONS.")
|
||||||
try:
|
try:
|
||||||
file_type_definitions = context.config_obj.FILE_TYPE_DEFINITIONS
|
file_type_definitions = context.config_obj.FILE_TYPE_DEFINITIONS
|
||||||
if not file_type_definitions: # Check if it's None or empty
|
if not file_type_definitions: # Check if it's None or empty
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: FILE_TYPE_DEFINITIONS is present but empty or None.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: FILE_TYPE_DEFINITIONS is present but empty or None.")
|
||||||
else:
|
else:
|
||||||
sample_defs_log = {k: file_type_definitions[k] for k in list(file_type_definitions.keys())[:2]} # Log first 2 for brevity
|
sample_defs_log = {k: file_type_definitions[k] for k in list(file_type_definitions.keys())[:2]} # Log first 2 for brevity
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Accessed FILE_TYPE_DEFINITIONS. Sample: {sample_defs_log}, Total keys: {len(file_type_definitions)}.")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Accessed FILE_TYPE_DEFINITIONS. Sample: {sample_defs_log}, Total keys: {len(file_type_definitions)}.")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Could not access context.config_obj.FILE_TYPE_DEFINITIONS via direct attribute.")
|
logger.error(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Could not access context.config_obj.FILE_TYPE_DEFINITIONS via direct attribute.")
|
||||||
|
|
||||||
base_map_key = None
|
base_map_key_val = None # Renamed from base_map_key to avoid conflict with current_map_key
|
||||||
suffix_part = ""
|
suffix_part = ""
|
||||||
|
|
||||||
if file_type_definitions and isinstance(file_type_definitions, dict) and len(file_type_definitions) > 0:
|
if file_type_definitions and isinstance(file_type_definitions, dict) and len(file_type_definitions) > 0:
|
||||||
base_map_key = None
|
base_map_key_val = None
|
||||||
suffix_part = ""
|
suffix_part = ""
|
||||||
|
|
||||||
sorted_known_base_keys = sorted(list(file_type_definitions.keys()), key=len, reverse=True)
|
sorted_known_base_keys = sorted(list(file_type_definitions.keys()), key=len, reverse=True)
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Sorted known base keys for parsing: {sorted_known_base_keys}")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Sorted known base keys for parsing: {sorted_known_base_keys}")
|
||||||
|
|
||||||
for known_key in sorted_known_base_keys:
|
for known_key in sorted_known_base_keys:
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Checking if '{current_map_type}' starts with '{known_key}'")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Checking if '{current_map_type}' starts with '{known_key}'")
|
||||||
if current_map_type.startswith(known_key):
|
if current_map_type.startswith(known_key):
|
||||||
base_map_key = known_key
|
base_map_key_val = known_key
|
||||||
suffix_part = current_map_type[len(known_key):]
|
suffix_part = current_map_type[len(known_key):]
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Match found! current_map_type: '{current_map_type}', base_map_key: '{base_map_key}', suffix_part: '{suffix_part}'")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Match found! current_map_type: '{current_map_type}', base_map_key_val: '{base_map_key_val}', suffix_part: '{suffix_part}'")
|
||||||
break
|
break
|
||||||
|
|
||||||
if base_map_key is None:
|
if base_map_key_val is None:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Could not parse base_map_key from '{current_map_type}' using known keys. Fallback: filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Could not parse base_map_key_val from '{current_map_type}' using known keys. Fallback: filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
||||||
else:
|
else:
|
||||||
definition = file_type_definitions.get(base_map_key)
|
definition = file_type_definitions.get(base_map_key_val)
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Definition for '{base_map_key}': {definition}")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Definition for '{base_map_key_val}': {definition}")
|
||||||
if definition and isinstance(definition, dict):
|
if definition and isinstance(definition, dict):
|
||||||
standard_type_alias = definition.get("standard_type")
|
standard_type_alias = definition.get("standard_type")
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Standard type alias for '{base_map_key}': '{standard_type_alias}'")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Standard type alias for '{base_map_key_val}': '{standard_type_alias}'")
|
||||||
if standard_type_alias and isinstance(standard_type_alias, str) and standard_type_alias.strip():
|
if standard_type_alias and isinstance(standard_type_alias, str) and standard_type_alias.strip():
|
||||||
filename_friendly_map_type = standard_type_alias.strip() + suffix_part
|
filename_friendly_map_type = standard_type_alias.strip() + suffix_part
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Successfully transformed map type: '{current_map_type}' -> '{filename_friendly_map_type}' (standard_type_alias: '{standard_type_alias}', suffix_part: '{suffix_part}').")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Successfully transformed map type: '{current_map_type}' -> '{filename_friendly_map_type}' (standard_type_alias: '{standard_type_alias}', suffix_part: '{suffix_part}').")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Standard type alias for '{base_map_key}' is missing, empty, or not a string (value: '{standard_type_alias}'). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Standard type alias for '{base_map_key_val}' is missing, empty, or not a string (value: '{standard_type_alias}'). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: No definition or invalid definition for '{base_map_key}' (value: {definition}). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: No definition or invalid definition for '{base_map_key_val}' (value: {definition}). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
||||||
elif file_type_definitions is None:
|
elif file_type_definitions is None:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: FILE_TYPE_DEFINITIONS not available for lookup (was None). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: FILE_TYPE_DEFINITIONS not available for lookup (was None). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
||||||
elif not isinstance(file_type_definitions, dict):
|
elif not isinstance(file_type_definitions, dict):
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: FILE_TYPE_DEFINITIONS is not a dictionary (type: {type(file_type_definitions)}). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: FILE_TYPE_DEFINITIONS is not a dictionary (type: {type(file_type_definitions)}). Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: FILE_TYPE_DEFINITIONS is an empty dictionary. Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: FILE_TYPE_DEFINITIONS is an empty dictionary. Using fallback. filename_friendly_map_type = '{filename_friendly_map_type}'.")
|
||||||
|
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Final filename_friendly_map_type: '{filename_friendly_map_type}'")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Final filename_friendly_map_type: '{filename_friendly_map_type}'")
|
||||||
# --- END: Filename-friendly map type derivation ---
|
# --- END: Filename-friendly map type derivation ---
|
||||||
|
|
||||||
if not current_map_type or not current_map_type.startswith("MAP_") or current_map_type == "MAP_GEN_COMPOSITE":
|
if not current_map_type or not current_map_type.startswith("MAP_") or current_map_type == "MAP_GEN_COMPOSITE":
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}': Skipping, item_type '{current_map_type}' (initial: '{initial_current_map_type}') not targeted for individual processing.")
|
logger.debug(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}': Skipping, item_type '{current_map_type}' (initial: '{initial_current_map_type}') not targeted for individual processing.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Type: {current_map_type}, Initial Type: {initial_current_map_type}, ID: {current_map_id_hex}): Starting individual processing.")
|
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Type: {current_map_type}, Initial Type: {initial_current_map_type}, Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Starting individual processing.")
|
||||||
|
|
||||||
# A. Find Source File (using file_rule.file_path as the pattern relative to source_base_path)
|
# A. Find Source File (using file_rule.file_path as the pattern relative to source_base_path)
|
||||||
# The _find_source_file might need adjustment if file_rule.file_path is absolute or needs complex globbing.
|
source_file_path = self._find_source_file(source_base_path, file_rule.file_path, asset_name_for_log, processing_instance_tag)
|
||||||
# For now, assume file_rule.file_path is a relative pattern or exact name.
|
|
||||||
source_file_path = self._find_source_file(source_base_path, file_rule.file_path, asset_name_for_log, current_map_id_hex)
|
|
||||||
if not source_file_path:
|
if not source_file_path:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Source file not found with path/pattern '{file_rule.file_path}' in '{source_base_path}'.")
|
logger.error(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Source file not found with path/pattern '{file_rule.file_path}' in '{source_base_path}'.")
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Failed', map_type=filename_friendly_map_type, details="Source file not found")
|
self._update_file_rule_status(context, current_map_key, 'Failed',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
details="Source file not found")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# B. Load and Transform Image
|
# B. Load and Transform Image
|
||||||
image_data: Optional[np.ndarray] = ipu.load_image(str(source_file_path))
|
image_data: Optional[np.ndarray] = ipu.load_image(str(source_file_path))
|
||||||
if image_data is None:
|
if image_data is None:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Failed to load image from '{source_file_path}'.")
|
logger.error(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Failed to load image from '{source_file_path}'.")
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Failed', map_type=filename_friendly_map_type, source_file=str(source_file_path), details="Image load failed")
|
self._update_file_rule_status(context, current_map_key, 'Failed',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
source_file=str(source_file_path),
|
||||||
|
details="Image load failed")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
original_height, original_width = image_data.shape[:2]
|
original_height, original_width = image_data.shape[:2]
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Loaded image '{source_file_path}' with dimensions {original_width}x{original_height}.")
|
logger.debug(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Loaded image '{source_file_path}' with dimensions {original_width}x{original_height}.")
|
||||||
|
|
||||||
# 1. Initial Power-of-Two (POT) Downscaling
|
# 1. Initial Power-of-Two (POT) Downscaling
|
||||||
pot_width = ipu.get_nearest_power_of_two_downscale(original_width)
|
pot_width = ipu.get_nearest_power_of_two_downscale(original_width)
|
||||||
@ -286,7 +304,7 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
base_pot_width, base_pot_height = 1, 1
|
base_pot_width, base_pot_height = 1, 1
|
||||||
|
|
||||||
|
|
||||||
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Original dims: ({original_width},{original_height}), Initial POT Scaled Dims: ({base_pot_width},{base_pot_height}).")
|
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Original dims: ({original_width},{original_height}), Initial POT Scaled Dims: ({base_pot_width},{base_pot_height}).")
|
||||||
|
|
||||||
# Calculate and store aspect ratio change string
|
# Calculate and store aspect ratio change string
|
||||||
if original_width > 0 and original_height > 0 and base_pot_width > 0 and base_pot_height > 0:
|
if original_width > 0 and original_height > 0 and base_pot_width > 0 and base_pot_height > 0:
|
||||||
@ -297,19 +315,26 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
if aspect_change_str:
|
if aspect_change_str:
|
||||||
# This will overwrite if multiple maps are processed; specified by requirements.
|
# This will overwrite if multiple maps are processed; specified by requirements.
|
||||||
context.asset_metadata['aspect_ratio_change_string'] = aspect_change_str
|
context.asset_metadata['aspect_ratio_change_string'] = aspect_change_str
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Map Type {current_map_type}: Calculated aspect ratio change string: '{aspect_change_str}' (Original: {original_width}x{original_height}, Base POT: {base_pot_width}x{base_pot_height}). Stored in asset_metadata.")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Map Type {current_map_type}: Calculated aspect ratio change string: '{aspect_change_str}' (Original: {original_width}x{original_height}, Base POT: {base_pot_width}x{base_pot_height}). Stored in asset_metadata.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Map Type {current_map_type}: Failed to calculate aspect ratio change string.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Map Type {current_map_type}: Failed to calculate aspect ratio change string.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Map Type {current_map_type}: Skipping aspect ratio change string calculation due to invalid dimensions (Original: {original_width}x{original_height}, Base POT: {base_pot_width}x{base_pot_height}).")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Map Type {current_map_type}: Skipping aspect ratio change string calculation due to invalid dimensions (Original: {original_width}x{original_height}, Base POT: {base_pot_width}x{base_pot_height}).")
|
||||||
|
|
||||||
base_pot_image_data = image_data.copy()
|
base_pot_image_data = image_data.copy()
|
||||||
if (base_pot_width, base_pot_height) != (original_width, original_height):
|
if (base_pot_width, base_pot_height) != (original_width, original_height):
|
||||||
interpolation = cv2.INTER_AREA # Good for downscaling
|
interpolation = cv2.INTER_AREA # Good for downscaling
|
||||||
base_pot_image_data = ipu.resize_image(base_pot_image_data, base_pot_width, base_pot_height, interpolation=interpolation)
|
base_pot_image_data = ipu.resize_image(base_pot_image_data, base_pot_width, base_pot_height, interpolation=interpolation)
|
||||||
if base_pot_image_data is None:
|
if base_pot_image_data is None:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Failed to resize image to base POT dimensions.")
|
logger.error(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Failed to resize image to base POT dimensions.")
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Failed', map_type=filename_friendly_map_type, source_file=str(source_file_path), original_dimensions=(original_width, original_height), details="Base POT resize failed")
|
self._update_file_rule_status(context, current_map_key, 'Failed',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
source_file=str(source_file_path),
|
||||||
|
original_dimensions=(original_width, original_height),
|
||||||
|
details="Base POT resize failed")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Color Profile Management (after initial POT resize, before multi-res saving)
|
# Color Profile Management (after initial POT resize, before multi-res saving)
|
||||||
@ -323,14 +348,14 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
custom_transform_settings = file_rule.channel_merge_instructions['transform']
|
custom_transform_settings = file_rule.channel_merge_instructions['transform']
|
||||||
if isinstance(custom_transform_settings, dict):
|
if isinstance(custom_transform_settings, dict):
|
||||||
transform_settings.update(custom_transform_settings)
|
transform_settings.update(custom_transform_settings)
|
||||||
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Loaded transform settings for color/output from file_rule.")
|
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Loaded transform settings for color/output from file_rule.")
|
||||||
|
|
||||||
if transform_settings['color_profile_management'] and transform_settings['target_color_profile'] == "RGB":
|
if transform_settings['color_profile_management'] and transform_settings['target_color_profile'] == "RGB":
|
||||||
if len(base_pot_image_data.shape) == 3 and base_pot_image_data.shape[2] == 3: # BGR to RGB
|
if len(base_pot_image_data.shape) == 3 and base_pot_image_data.shape[2] == 3: # BGR to RGB
|
||||||
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Converting BGR to RGB for base POT image.")
|
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Converting BGR to RGB for base POT image.")
|
||||||
base_pot_image_data = ipu.convert_bgr_to_rgb(base_pot_image_data)
|
base_pot_image_data = ipu.convert_bgr_to_rgb(base_pot_image_data)
|
||||||
elif len(base_pot_image_data.shape) == 3 and base_pot_image_data.shape[2] == 4: # BGRA to RGBA
|
elif len(base_pot_image_data.shape) == 3 and base_pot_image_data.shape[2] == 4: # BGRA to RGBA
|
||||||
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (ID: {current_map_id_hex}): Converting BGRA to RGBA for base POT image.")
|
logger.info(f"Asset '{asset_name_for_log}', FileRule path '{file_rule.file_path}' (Key: {current_map_key}, Proc. Tag: {processing_instance_tag}): Converting BGRA to RGBA for base POT image.")
|
||||||
base_pot_image_data = ipu.convert_bgra_to_rgba(base_pot_image_data)
|
base_pot_image_data = ipu.convert_bgra_to_rgba(base_pot_image_data)
|
||||||
|
|
||||||
# Ensure engine_temp_dir exists before saving base POT
|
# Ensure engine_temp_dir exists before saving base POT
|
||||||
@ -340,11 +365,17 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
logger.info(f"Asset '{asset_name_for_log}': Created engine_temp_dir at '{context.engine_temp_dir}'")
|
logger.info(f"Asset '{asset_name_for_log}': Created engine_temp_dir at '{context.engine_temp_dir}'")
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.error(f"Asset '{asset_name_for_log}': Failed to create engine_temp_dir '{context.engine_temp_dir}': {e}")
|
logger.error(f"Asset '{asset_name_for_log}': Failed to create engine_temp_dir '{context.engine_temp_dir}': {e}")
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Failed', map_type=filename_friendly_map_type, source_file=str(source_file_path), details="Failed to create temp directory for base POT")
|
self._update_file_rule_status(context, current_map_key, 'Failed',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
source_file=str(source_file_path),
|
||||||
|
details="Failed to create temp directory for base POT")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
temp_filename_suffix = Path(source_file_path).suffix
|
temp_filename_suffix = Path(source_file_path).suffix
|
||||||
base_pot_temp_filename = f"{current_map_id_hex}_basePOT{temp_filename_suffix}"
|
base_pot_temp_filename = f"{processing_instance_tag}_basePOT{temp_filename_suffix}" # Use processing_instance_tag
|
||||||
base_pot_temp_path = context.engine_temp_dir / base_pot_temp_filename
|
base_pot_temp_path = context.engine_temp_dir / base_pot_temp_filename
|
||||||
|
|
||||||
# Determine save parameters for base POT image (can be different from variants if needed)
|
# Determine save parameters for base POT image (can be different from variants if needed)
|
||||||
@ -354,18 +385,29 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
# For now, using simple save.
|
# For now, using simple save.
|
||||||
|
|
||||||
if not ipu.save_image(str(base_pot_temp_path), base_pot_image_data, params=base_save_params):
|
if not ipu.save_image(str(base_pot_temp_path), base_pot_image_data, params=base_save_params):
|
||||||
logger.error(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Failed to save base POT image to '{base_pot_temp_path}'.")
|
logger.error(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Failed to save base POT image to '{base_pot_temp_path}'.")
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Failed', map_type=filename_friendly_map_type, source_file=str(source_file_path), original_dimensions=(original_width, original_height), base_pot_dimensions=(base_pot_width, base_pot_height), details="Base POT image save failed")
|
self._update_file_rule_status(context, current_map_key, 'Failed',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
source_file=str(source_file_path),
|
||||||
|
original_dimensions=(original_width, original_height),
|
||||||
|
base_pot_dimensions=(base_pot_width, base_pot_height),
|
||||||
|
details="Base POT image save failed")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Successfully saved base POT image to '{base_pot_temp_path}' with dims ({base_pot_width}x{base_pot_height}).")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Successfully saved base POT image to '{base_pot_temp_path}' with dims ({base_pot_width}x{base_pot_height}).")
|
||||||
|
|
||||||
# Initialize/update the status for this map in processed_maps_details
|
# Initialize/update the status for this map in processed_maps_details
|
||||||
self._update_file_rule_status(
|
self._update_file_rule_status(
|
||||||
context,
|
context,
|
||||||
current_map_id_hex,
|
current_map_key, # Use file_rule_idx as key
|
||||||
'BasePOTSaved', # Intermediate status, will be updated after variant check
|
'BasePOTSaved', # Intermediate status, will be updated after variant check
|
||||||
map_type=filename_friendly_map_type,
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag, # Store the tag
|
||||||
source_file=str(source_file_path),
|
source_file=str(source_file_path),
|
||||||
original_dimensions=(original_width, original_height),
|
original_dimensions=(original_width, original_height),
|
||||||
base_pot_dimensions=(base_pot_width, base_pot_height),
|
base_pot_dimensions=(base_pot_width, base_pot_height),
|
||||||
@ -375,20 +417,20 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
# 2. Multiple Resolution Output (Variants)
|
# 2. Multiple Resolution Output (Variants)
|
||||||
processed_at_least_one_resolution_variant = False
|
processed_at_least_one_resolution_variant = False
|
||||||
# Resolution variants are attempted for all map types individually processed.
|
# Resolution variants are attempted for all map types individually processed.
|
||||||
# The filter at the beginning of the loop (around line 72) ensures only relevant maps reach this stage.
|
# The filter at the beginning of the loop ensures only relevant maps reach this stage.
|
||||||
generate_variants_for_this_map_type = True
|
generate_variants_for_this_map_type = True
|
||||||
|
|
||||||
if generate_variants_for_this_map_type: # This will now always be true if code execution reaches here
|
if generate_variants_for_this_map_type: # This will now always be true if code execution reaches here
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Map type '{current_map_type}' is eligible for individual processing. Attempting to generate resolution variants.")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Map type '{current_map_type}' is eligible for individual processing. Attempting to generate resolution variants.")
|
||||||
# Sort resolutions from largest to smallest
|
# Sort resolutions from largest to smallest
|
||||||
sorted_resolutions = sorted(image_resolutions.items(), key=lambda item: item[1], reverse=True)
|
sorted_resolutions = sorted(image_resolutions.items(), key=lambda item: item[1], reverse=True)
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Sorted resolutions for variant processing: {sorted_resolutions}")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Sorted resolutions for variant processing: {sorted_resolutions}")
|
||||||
|
|
||||||
for res_key, res_max_dim in sorted_resolutions:
|
for res_key, res_max_dim in sorted_resolutions:
|
||||||
current_w, current_h = base_pot_image_data.shape[1], base_pot_image_data.shape[0]
|
current_w, current_h = base_pot_image_data.shape[1], base_pot_image_data.shape[0]
|
||||||
|
|
||||||
if current_w <= 0 or current_h <=0:
|
if current_w <= 0 or current_h <=0:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Res {res_key}: Base POT image has zero dimension ({current_w}x{current_h}). Skipping this resolution variant.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Res {res_key}: Base POT image has zero dimension ({current_w}x{current_h}). Skipping this resolution variant.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if max(current_w, current_h) >= res_max_dim:
|
if max(current_w, current_h) >= res_max_dim:
|
||||||
@ -401,24 +443,24 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
target_h_res = res_max_dim
|
target_h_res = res_max_dim
|
||||||
target_w_res = max(1, round(target_h_res * (current_w / current_h)))
|
target_w_res = max(1, round(target_h_res * (current_w / current_h)))
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Res {res_key}: Base POT image ({current_w}x{current_h}) is smaller than target max dim {res_max_dim}. Skipping this resolution variant.")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Res {res_key}: Base POT image ({current_w}x{current_h}) is smaller than target max dim {res_max_dim}. Skipping this resolution variant.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
target_w_res = min(target_w_res, current_w)
|
target_w_res = min(target_w_res, current_w)
|
||||||
target_h_res = min(target_h_res, current_h)
|
target_h_res = min(target_h_res, current_h)
|
||||||
|
|
||||||
if target_w_res <=0 or target_h_res <=0:
|
if target_w_res <=0 or target_h_res <=0:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Res {res_key}: Calculated target variant dims are zero or negative ({target_w_res}x{target_h_res}). Skipping.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Res {res_key}: Calculated target variant dims are zero or negative ({target_w_res}x{target_h_res}). Skipping.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Res {res_key}: Processing variant for {res_max_dim}. Base POT Dims: ({current_w}x{current_h}), Target Dims for {res_key}: ({target_w_res}x{target_h_res}).")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Res {res_key}: Processing variant for {res_max_dim}. Base POT Dims: ({current_w}x{current_h}), Target Dims for {res_key}: ({target_w_res}x{target_h_res}).")
|
||||||
|
|
||||||
output_image_data_for_res = base_pot_image_data
|
output_image_data_for_res = base_pot_image_data
|
||||||
if (target_w_res, target_h_res) != (current_w, current_h):
|
if (target_w_res, target_h_res) != (current_w, current_h):
|
||||||
interpolation_res = cv2.INTER_AREA
|
interpolation_res = cv2.INTER_AREA
|
||||||
output_image_data_for_res = ipu.resize_image(base_pot_image_data, target_w_res, target_h_res, interpolation=interpolation_res)
|
output_image_data_for_res = ipu.resize_image(base_pot_image_data, target_w_res, target_h_res, interpolation=interpolation_res)
|
||||||
if output_image_data_for_res is None:
|
if output_image_data_for_res is None:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Res {res_key}: Failed to resize image for resolution variant {res_key}.")
|
logger.error(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Res {res_key}: Failed to resize image for resolution variant {res_key}.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
assetname_placeholder = context.asset_rule.asset_name if context.asset_rule else "UnknownAsset"
|
assetname_placeholder = context.asset_rule.asset_name if context.asset_rule else "UnknownAsset"
|
||||||
@ -431,7 +473,7 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
.replace("[maptype]", sanitize_filename(filename_friendly_map_type)) \
|
.replace("[maptype]", sanitize_filename(filename_friendly_map_type)) \
|
||||||
.replace("[resolution]", sanitize_filename(resolution_placeholder)) \
|
.replace("[resolution]", sanitize_filename(resolution_placeholder)) \
|
||||||
.replace("[ext]", output_ext_variant)
|
.replace("[ext]", output_ext_variant)
|
||||||
temp_output_filename_variant = f"{current_map_id_hex}_variant_{temp_output_filename_variant}" # Distinguish variant temp files
|
temp_output_filename_variant = f"{processing_instance_tag}_variant_{temp_output_filename_variant}" # Use processing_instance_tag
|
||||||
temp_output_path_variant = context.engine_temp_dir / temp_output_filename_variant
|
temp_output_path_variant = context.engine_temp_dir / temp_output_filename_variant
|
||||||
|
|
||||||
save_params_variant = []
|
save_params_variant = []
|
||||||
@ -446,26 +488,26 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
save_success_variant = ipu.save_image(str(temp_output_path_variant), output_image_data_for_res, params=save_params_variant)
|
save_success_variant = ipu.save_image(str(temp_output_path_variant), output_image_data_for_res, params=save_params_variant)
|
||||||
|
|
||||||
if not save_success_variant:
|
if not save_success_variant:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Res {res_key}: Failed to save temporary variant image to '{temp_output_path_variant}'.")
|
logger.error(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Res {res_key}: Failed to save temporary variant image to '{temp_output_path_variant}'.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Res {res_key}: Successfully saved temporary variant map to '{temp_output_path_variant}' with dims ({target_w_res}x{target_h_res}).")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Res {res_key}: Successfully saved temporary variant map to '{temp_output_path_variant}' with dims ({target_w_res}x{target_h_res}).")
|
||||||
processed_at_least_one_resolution_variant = True
|
processed_at_least_one_resolution_variant = True
|
||||||
|
|
||||||
if 'variants' not in context.processed_maps_details[current_map_id_hex]:
|
if 'variants' not in context.processed_maps_details[current_map_key]: # Use current_map_key (file_rule_idx)
|
||||||
context.processed_maps_details[current_map_id_hex]['variants'] = []
|
context.processed_maps_details[current_map_key]['variants'] = []
|
||||||
|
|
||||||
context.processed_maps_details[current_map_id_hex]['variants'].append({
|
context.processed_maps_details[current_map_key]['variants'].append({ # Use current_map_key (file_rule_idx)
|
||||||
'resolution_key': res_key,
|
'resolution_key': res_key,
|
||||||
'temp_path': str(temp_output_path_variant), # Changed 'path' to 'temp_path'
|
'temp_path': str(temp_output_path_variant),
|
||||||
'dimensions': (target_w_res, target_h_res),
|
'dimensions': (target_w_res, target_h_res),
|
||||||
'resolution_name': f"{target_w_res}x{target_h_res}" # Retain for potential use
|
'resolution_name': f"{target_w_res}x{target_h_res}"
|
||||||
})
|
})
|
||||||
|
|
||||||
if 'processed_files' not in context.asset_metadata:
|
if 'processed_files' not in context.asset_metadata:
|
||||||
context.asset_metadata['processed_files'] = []
|
context.asset_metadata['processed_files'] = []
|
||||||
context.asset_metadata['processed_files'].append({
|
context.asset_metadata['processed_files'].append({
|
||||||
'processed_map_key': current_map_id_hex,
|
'processed_map_key': current_map_key, # Use current_map_key (file_rule_idx)
|
||||||
'resolution_key': res_key,
|
'resolution_key': res_key,
|
||||||
'path': str(temp_output_path_variant),
|
'path': str(temp_output_path_variant),
|
||||||
'type': 'temporary_map_variant',
|
'type': 'temporary_map_variant',
|
||||||
@ -479,11 +521,11 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
source_of_stats_image = "unknown"
|
source_of_stats_image = "unknown"
|
||||||
|
|
||||||
if processed_at_least_one_resolution_variant and \
|
if processed_at_least_one_resolution_variant and \
|
||||||
current_map_id_hex in context.processed_maps_details and \
|
current_map_key in context.processed_maps_details and \
|
||||||
'variants' in context.processed_maps_details[current_map_id_hex] and \
|
'variants' in context.processed_maps_details[current_map_key] and \
|
||||||
context.processed_maps_details[current_map_id_hex]['variants']:
|
context.processed_maps_details[current_map_key]['variants']:
|
||||||
|
|
||||||
variants_list = context.processed_maps_details[current_map_id_hex]['variants']
|
variants_list = context.processed_maps_details[current_map_key]['variants']
|
||||||
valid_variants_for_stats = [
|
valid_variants_for_stats = [
|
||||||
v for v in variants_list
|
v for v in variants_list
|
||||||
if isinstance(v.get('dimensions'), tuple) and len(v['dimensions']) == 2 and v['dimensions'][0] > 0 and v['dimensions'][1] > 0
|
if isinstance(v.get('dimensions'), tuple) and len(v['dimensions']) == 2 and v['dimensions'][0] > 0 and v['dimensions'][1] > 0
|
||||||
@ -494,25 +536,25 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
|
|
||||||
if smallest_variant and 'temp_path' in smallest_variant and smallest_variant.get('dimensions'):
|
if smallest_variant and 'temp_path' in smallest_variant and smallest_variant.get('dimensions'):
|
||||||
smallest_res_w, smallest_res_h = smallest_variant['dimensions']
|
smallest_res_w, smallest_res_h = smallest_variant['dimensions']
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Identified smallest variant for stats: {smallest_variant.get('resolution_key', 'N/A')} ({smallest_res_w}x{smallest_res_h}) at {smallest_variant['temp_path']}")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Identified smallest variant for stats: {smallest_variant.get('resolution_key', 'N/A')} ({smallest_res_w}x{smallest_res_h}) at {smallest_variant['temp_path']}")
|
||||||
lowest_res_image_data_for_stats = ipu.load_image(smallest_variant['temp_path'])
|
lowest_res_image_data_for_stats = ipu.load_image(smallest_variant['temp_path'])
|
||||||
image_to_stat_path_for_log = smallest_variant['temp_path']
|
image_to_stat_path_for_log = smallest_variant['temp_path']
|
||||||
source_of_stats_image = f"variant {smallest_variant.get('resolution_key', 'N/A')}"
|
source_of_stats_image = f"variant {smallest_variant.get('resolution_key', 'N/A')}"
|
||||||
if lowest_res_image_data_for_stats is None:
|
if lowest_res_image_data_for_stats is None:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Failed to load smallest variant image '{smallest_variant['temp_path']}' for stats.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Failed to load smallest variant image '{smallest_variant['temp_path']}' for stats.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Could not determine smallest variant for stats from valid variants list (details missing).")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Could not determine smallest variant for stats from valid variants list (details missing).")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: No valid variants found to determine the smallest one for stats.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: No valid variants found to determine the smallest one for stats.")
|
||||||
|
|
||||||
if lowest_res_image_data_for_stats is None:
|
if lowest_res_image_data_for_stats is None:
|
||||||
if base_pot_image_data is not None:
|
if base_pot_image_data is not None:
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Using base POT image for stats (dimensions: {base_pot_width}x{base_pot_height}). Smallest variant not available/loaded or no variants generated.")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Using base POT image for stats (dimensions: {base_pot_width}x{base_pot_height}). Smallest variant not available/loaded or no variants generated.")
|
||||||
lowest_res_image_data_for_stats = base_pot_image_data
|
lowest_res_image_data_for_stats = base_pot_image_data
|
||||||
image_to_stat_path_for_log = f"In-memory base POT image (dims: {base_pot_width}x{base_pot_height})"
|
image_to_stat_path_for_log = f"In-memory base POT image (dims: {base_pot_width}x{base_pot_height})"
|
||||||
source_of_stats_image = "base POT"
|
source_of_stats_image = "base POT"
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Base POT image data is also None. Cannot calculate stats.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Base POT image data is also None. Cannot calculate stats.")
|
||||||
|
|
||||||
if lowest_res_image_data_for_stats is not None:
|
if lowest_res_image_data_for_stats is not None:
|
||||||
stats_dict = ipu.calculate_image_stats(lowest_res_image_data_for_stats)
|
stats_dict = ipu.calculate_image_stats(lowest_res_image_data_for_stats)
|
||||||
@ -520,43 +562,59 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
if 'image_stats_lowest_res' not in context.asset_metadata:
|
if 'image_stats_lowest_res' not in context.asset_metadata:
|
||||||
context.asset_metadata['image_stats_lowest_res'] = {}
|
context.asset_metadata['image_stats_lowest_res'] = {}
|
||||||
|
|
||||||
context.asset_metadata['image_stats_lowest_res'][current_map_type] = stats_dict
|
context.asset_metadata['image_stats_lowest_res'][current_map_type] = stats_dict # Keyed by map_type
|
||||||
logger.info(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Map Type '{current_map_type}': Calculated and stored image stats from '{source_of_stats_image}' (source ref: '{image_to_stat_path_for_log}').")
|
logger.info(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Map Type '{current_map_type}': Calculated and stored image stats from '{source_of_stats_image}' (source ref: '{image_to_stat_path_for_log}').")
|
||||||
elif stats_dict and "error" in stats_dict:
|
elif stats_dict and "error" in stats_dict:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Map Type '{current_map_type}': Error calculating image stats from '{source_of_stats_image}': {stats_dict['error']}.")
|
logger.error(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Map Type '{current_map_type}': Error calculating image stats from '{source_of_stats_image}': {stats_dict['error']}.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Map Type '{current_map_type}': Failed to calculate image stats from '{source_of_stats_image}' (result was None or empty).")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Map Type '{current_map_type}': Failed to calculate image stats from '{source_of_stats_image}' (result was None or empty).")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}, Map Type '{current_map_type}': No image data available (from variant or base POT) to calculate stats.")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}, Map Type '{current_map_type}': No image data available (from variant or base POT) to calculate stats.")
|
||||||
|
|
||||||
# Final status update based on whether variants were generated (and expected)
|
# Final status update based on whether variants were generated (and expected)
|
||||||
if generate_variants_for_this_map_type:
|
if generate_variants_for_this_map_type:
|
||||||
if processed_at_least_one_resolution_variant:
|
if processed_at_least_one_resolution_variant:
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Processed_With_Variants', map_type=filename_friendly_map_type, details="Successfully processed with multiple resolution variants.")
|
self._update_file_rule_status(context, current_map_key, 'Processed_With_Variants',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
details="Successfully processed with multiple resolution variants.")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Variants were expected for map type '{current_map_type}', but none were generated (e.g., base POT too small for any variant tier).")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key {current_map_key}, Proc. Tag {processing_instance_tag}: Variants were expected for map type '{current_map_type}', but none were generated (e.g., base POT too small for any variant tier).")
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Processed_No_Variants', map_type=filename_friendly_map_type, details="Variants expected but none generated (e.g., base POT too small).")
|
self._update_file_rule_status(context, current_map_key, 'Processed_No_Variants',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
details="Variants expected but none generated (e.g., base POT too small).")
|
||||||
else: # No variants were expected for this map type
|
else: # No variants were expected for this map type
|
||||||
self._update_file_rule_status(context, current_map_id_hex, 'Processed_No_Variants', map_type=filename_friendly_map_type, details="Processed to base POT; variants not applicable for this map type.")
|
self._update_file_rule_status(context, current_map_key, 'Processed_No_Variants',
|
||||||
|
map_type=filename_friendly_map_type,
|
||||||
|
processing_map_type=current_map_type,
|
||||||
|
source_file_rule_index=file_rule_idx,
|
||||||
|
processing_tag=processing_instance_tag,
|
||||||
|
details="Processed to base POT; variants not applicable for this map type.")
|
||||||
|
|
||||||
logger.info(f"Asset '{asset_name_for_log}': Finished individual map processing stage.")
|
logger.info(f"Asset '{asset_name_for_log}': Finished individual map processing stage.")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _find_source_file(self, base_path: Path, pattern: str, asset_name_for_log: str, current_map_id_hex: str) -> Optional[Path]: # asset_id -> asset_name_for_log, file_rule_id_hex -> current_map_id_hex
|
def _find_source_file(self, base_path: Path, pattern: str, asset_name_for_log: str, processing_instance_tag: str) -> Optional[Path]:
|
||||||
"""
|
"""
|
||||||
Finds a single source file matching the pattern within the base_path.
|
Finds a single source file matching the pattern within the base_path.
|
||||||
|
Logs use processing_instance_tag for specific run tracing.
|
||||||
"""
|
"""
|
||||||
if not pattern: # pattern is now file_rule.file_path
|
if not pattern:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Empty file_path provided in FileRule.")
|
logger.warning(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: Empty file_path provided in FileRule.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# If pattern is an absolute path, use it directly
|
# If pattern is an absolute path, use it directly
|
||||||
potential_abs_path = Path(pattern)
|
potential_abs_path = Path(pattern)
|
||||||
if potential_abs_path.is_absolute() and potential_abs_path.exists():
|
if potential_abs_path.is_absolute() and potential_abs_path.exists():
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: file_path '{pattern}' is absolute and exists. Using it directly.")
|
logger.debug(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: file_path '{pattern}' is absolute and exists. Using it directly.")
|
||||||
return potential_abs_path
|
return potential_abs_path
|
||||||
elif potential_abs_path.is_absolute():
|
elif potential_abs_path.is_absolute():
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: file_path '{pattern}' is absolute but does not exist.")
|
logger.warning(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: file_path '{pattern}' is absolute but does not exist.")
|
||||||
# Fall through to try resolving against base_path if it's just a name/relative pattern
|
# Fall through to try resolving against base_path if it's just a name/relative pattern
|
||||||
|
|
||||||
# Treat pattern as relative to base_path
|
# Treat pattern as relative to base_path
|
||||||
@ -565,46 +623,49 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
# First, check if pattern is an exact relative path
|
# First, check if pattern is an exact relative path
|
||||||
exact_match_path = base_path / pattern
|
exact_match_path = base_path / pattern
|
||||||
if exact_match_path.exists() and exact_match_path.is_file():
|
if exact_match_path.exists() and exact_match_path.is_file():
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Found exact match for '{pattern}' at '{exact_match_path}'.")
|
logger.debug(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: Found exact match for '{pattern}' at '{exact_match_path}'.")
|
||||||
return exact_match_path
|
return exact_match_path
|
||||||
|
|
||||||
# If not an exact match, try as a glob pattern (recursive)
|
# If not an exact match, try as a glob pattern (recursive)
|
||||||
matched_files_rglob = list(base_path.rglob(pattern))
|
matched_files_rglob = list(base_path.rglob(pattern))
|
||||||
if matched_files_rglob:
|
if matched_files_rglob:
|
||||||
if len(matched_files_rglob) > 1:
|
if len(matched_files_rglob) > 1:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Multiple files ({len(matched_files_rglob)}) found for pattern '{pattern}' in '{base_path}' (recursive). Using first: {matched_files_rglob[0]}. Files: {matched_files_rglob}")
|
logger.warning(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: Multiple files ({len(matched_files_rglob)}) found for pattern '{pattern}' in '{base_path}' (recursive). Using first: {matched_files_rglob[0]}. Files: {matched_files_rglob}")
|
||||||
return matched_files_rglob[0]
|
return matched_files_rglob[0]
|
||||||
|
|
||||||
# Try non-recursive glob if rglob fails
|
# Try non-recursive glob if rglob fails
|
||||||
matched_files_glob = list(base_path.glob(pattern))
|
matched_files_glob = list(base_path.glob(pattern))
|
||||||
if matched_files_glob:
|
if matched_files_glob:
|
||||||
if len(matched_files_glob) > 1:
|
if len(matched_files_glob) > 1:
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Multiple files ({len(matched_files_glob)}) found for pattern '{pattern}' in '{base_path}' (non-recursive). Using first: {matched_files_glob[0]}. Files: {matched_files_glob}")
|
logger.warning(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: Multiple files ({len(matched_files_glob)}) found for pattern '{pattern}' in '{base_path}' (non-recursive). Using first: {matched_files_glob[0]}. Files: {matched_files_glob}")
|
||||||
return matched_files_glob[0]
|
return matched_files_glob[0]
|
||||||
|
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: No files found matching pattern '{pattern}' in '{base_path}' (exact, recursive, or non-recursive).")
|
logger.debug(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: No files found matching pattern '{pattern}' in '{base_path}' (exact, recursive, or non-recursive).")
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Asset '{asset_name_for_log}', Map ID {current_map_id_hex}: Error searching for file with pattern '{pattern}' in '{base_path}': {e}")
|
logger.error(f"Asset '{asset_name_for_log}', Proc. Tag {processing_instance_tag}: Error searching for file with pattern '{pattern}' in '{base_path}': {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _update_file_rule_status(self, context: AssetProcessingContext, map_id_hex: str, status: str, **kwargs): # file_rule_id_hex -> map_id_hex
|
def _update_file_rule_status(self, context: AssetProcessingContext, map_key_index: int, status: str, **kwargs): # Renamed map_id_hex to map_key_index
|
||||||
"""Helper to update processed_maps_details for a map."""
|
"""Helper to update processed_maps_details for a map, keyed by file_rule_idx."""
|
||||||
asset_name_for_log = context.asset_rule.asset_name if context.asset_rule else "Unknown Asset"
|
asset_name_for_log = context.asset_rule.asset_name if context.asset_rule else "Unknown Asset"
|
||||||
if map_id_hex not in context.processed_maps_details:
|
if map_key_index not in context.processed_maps_details:
|
||||||
context.processed_maps_details[map_id_hex] = {}
|
context.processed_maps_details[map_key_index] = {}
|
||||||
|
|
||||||
context.processed_maps_details[map_id_hex]['status'] = status
|
context.processed_maps_details[map_key_index]['status'] = status
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
context.processed_maps_details[map_id_hex][key] = value
|
# Ensure source_file_rule_id_hex is not added if it was somehow passed (it shouldn't be)
|
||||||
|
if key == 'source_file_rule_id_hex':
|
||||||
|
continue
|
||||||
|
context.processed_maps_details[map_key_index][key] = value
|
||||||
|
|
||||||
if 'map_type' not in context.processed_maps_details[map_id_hex] and 'map_type' in kwargs:
|
if 'map_type' not in context.processed_maps_details[map_key_index] and 'map_type' in kwargs:
|
||||||
context.processed_maps_details[map_id_hex]['map_type'] = kwargs['map_type']
|
context.processed_maps_details[map_key_index]['map_type'] = kwargs['map_type']
|
||||||
|
|
||||||
# Add formatted resolution names
|
# Add formatted resolution names
|
||||||
if 'original_dimensions' in kwargs and isinstance(kwargs['original_dimensions'], tuple) and len(kwargs['original_dimensions']) == 2:
|
if 'original_dimensions' in kwargs and isinstance(kwargs['original_dimensions'], tuple) and len(kwargs['original_dimensions']) == 2:
|
||||||
orig_w, orig_h = kwargs['original_dimensions']
|
orig_w, orig_h = kwargs['original_dimensions']
|
||||||
context.processed_maps_details[map_id_hex]['original_resolution_name'] = f"{orig_w}x{orig_h}"
|
context.processed_maps_details[map_key_index]['original_resolution_name'] = f"{orig_w}x{orig_h}"
|
||||||
|
|
||||||
# Determine the correct dimensions to use for 'processed_resolution_name'
|
# Determine the correct dimensions to use for 'processed_resolution_name'
|
||||||
# This name refers to the base POT scaled image dimensions before variant generation.
|
# This name refers to the base POT scaled image dimensions before variant generation.
|
||||||
@ -619,21 +680,21 @@ class IndividualMapProcessingStage(ProcessingStage):
|
|||||||
if dims_to_log_as_base_processed:
|
if dims_to_log_as_base_processed:
|
||||||
proc_w, proc_h = dims_to_log_as_base_processed
|
proc_w, proc_h = dims_to_log_as_base_processed
|
||||||
resolution_name_str = f"{proc_w}x{proc_h}"
|
resolution_name_str = f"{proc_w}x{proc_h}"
|
||||||
context.processed_maps_details[map_id_hex]['base_pot_resolution_name'] = resolution_name_str
|
context.processed_maps_details[map_key_index]['base_pot_resolution_name'] = resolution_name_str
|
||||||
# Ensure 'processed_resolution_name' is also set for OutputOrganizationStage compatibility
|
# Ensure 'processed_resolution_name' is also set for OutputOrganizationStage compatibility
|
||||||
context.processed_maps_details[map_id_hex]['processed_resolution_name'] = resolution_name_str
|
context.processed_maps_details[map_key_index]['processed_resolution_name'] = resolution_name_str
|
||||||
elif 'processed_dimensions' in kwargs or 'base_pot_dimensions' in kwargs:
|
elif 'processed_dimensions' in kwargs or 'base_pot_dimensions' in kwargs:
|
||||||
details_for_warning = kwargs.get('processed_dimensions', kwargs.get('base_pot_dimensions'))
|
details_for_warning = kwargs.get('processed_dimensions', kwargs.get('base_pot_dimensions'))
|
||||||
logger.warning(f"Asset '{asset_name_for_log}', Map ID {map_id_hex}: 'processed_dimensions' or 'base_pot_dimensions' key present but its value is not a valid 2-element tuple: {details_for_warning}")
|
logger.warning(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index}: 'processed_dimensions' or 'base_pot_dimensions' key present but its value is not a valid 2-element tuple: {details_for_warning}")
|
||||||
|
|
||||||
# If temp_processed_file was passed, ensure it's in the details
|
# If temp_processed_file was passed, ensure it's in the details
|
||||||
if 'temp_processed_file' in kwargs:
|
if 'temp_processed_file' in kwargs:
|
||||||
context.processed_maps_details[map_id_hex]['temp_processed_file'] = kwargs['temp_processed_file']
|
context.processed_maps_details[map_key_index]['temp_processed_file'] = kwargs['temp_processed_file']
|
||||||
|
|
||||||
|
|
||||||
# Log all details being stored for clarity, including the newly added resolution names
|
# Log all details being stored for clarity, including the newly added resolution names
|
||||||
log_details = context.processed_maps_details[map_id_hex].copy()
|
log_details = context.processed_maps_details[map_key_index].copy()
|
||||||
# Avoid logging full image data if it accidentally gets into kwargs
|
# Avoid logging full image data if it accidentally gets into kwargs
|
||||||
if 'image_data' in log_details: del log_details['image_data']
|
if 'image_data' in log_details: del log_details['image_data']
|
||||||
if 'base_pot_image_data' in log_details: del log_details['base_pot_image_data']
|
if 'base_pot_image_data' in log_details: del log_details['base_pot_image_data']
|
||||||
logger.debug(f"Asset '{asset_name_for_log}', Map ID {map_id_hex}: Status updated to '{status}'. Details: {log_details}")
|
logger.debug(f"Asset '{asset_name_for_log}', Map Key Index {map_key_index}: Status updated to '{status}'. Details: {log_details}")
|
||||||
@ -125,27 +125,64 @@ class MapMergingStage(ProcessingStage):
|
|||||||
required_input_map_types = set(inputs_map_type_to_channel.values())
|
required_input_map_types = set(inputs_map_type_to_channel.values())
|
||||||
|
|
||||||
for required_map_type in required_input_map_types:
|
for required_map_type in required_input_map_types:
|
||||||
found_processed_map = None
|
found_processed_map_details = None
|
||||||
processed_map_key = None
|
# The key `p_key_idx` is the file_rule_idx from the IndividualMapProcessingStage
|
||||||
for p_key, p_details in context.processed_maps_details.items():
|
for p_key_idx, p_details in context.processed_maps_details.items(): # p_key_idx is an int
|
||||||
processed_map_type_in_details = p_details.get('map_type')
|
processed_map_identifier = p_details.get('processing_map_type', p_details.get('map_type'))
|
||||||
# Check for direct match or match with "MAP_" prefix
|
|
||||||
if (processed_map_type_in_details == required_map_type or \
|
# Comprehensive list of valid statuses for an input map to be used in merging
|
||||||
processed_map_type_in_details == f"MAP_{required_map_type}") and \
|
valid_input_statuses = ['BasePOTSaved', 'Processed_With_Variants', 'Processed_No_Variants', 'Converted_To_Rough']
|
||||||
p_details.get('status') == 'Processed':
|
|
||||||
found_processed_map = p_details
|
is_match = False
|
||||||
processed_map_key = p_key # The UUID hex key from individual processing
|
if processed_map_identifier == required_map_type:
|
||||||
|
is_match = True
|
||||||
|
elif required_map_type.startswith("MAP_") and processed_map_identifier == required_map_type.split("MAP_")[-1]:
|
||||||
|
is_match = True
|
||||||
|
elif not required_map_type.startswith("MAP_") and processed_map_identifier == f"MAP_{required_map_type}":
|
||||||
|
is_match = True
|
||||||
|
|
||||||
|
if is_match and p_details.get('status') in valid_input_statuses:
|
||||||
|
found_processed_map_details = p_details
|
||||||
|
# The key `p_key_idx` (which is the FileRule index) is implicitly associated with these details.
|
||||||
break
|
break
|
||||||
|
|
||||||
if not found_processed_map:
|
if not found_processed_map_details:
|
||||||
logger.warning(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Required input map_type '{required_map_type}' for output '{output_map_type}' not found or not processed in processed_maps_details.")
|
can_be_fully_defaulted = True
|
||||||
# Option: Use default value for the entire map if one could be constructed for this map_type
|
channels_requiring_this_map = [
|
||||||
# For now, we fail the merge if a required map is missing.
|
ch_key for ch_key, map_type_val in inputs_map_type_to_channel.items()
|
||||||
all_inputs_valid = False
|
if map_type_val == required_map_type
|
||||||
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': f"Required input map_type '{required_map_type}' missing."}
|
]
|
||||||
break # Break from finding inputs for this merge rule
|
|
||||||
|
|
||||||
temp_file_path = Path(found_processed_map['temp_processed_file'])
|
if not channels_requiring_this_map:
|
||||||
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Internal logic error. Required map_type '{required_map_type}' is not actually used by any output channel. Configuration: {inputs_map_type_to_channel}")
|
||||||
|
all_inputs_valid = False
|
||||||
|
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': f"Internal error: required map_type '{required_map_type}' not in use."}
|
||||||
|
break
|
||||||
|
|
||||||
|
for channel_char_needing_default in channels_requiring_this_map:
|
||||||
|
if default_values.get(channel_char_needing_default) is None:
|
||||||
|
can_be_fully_defaulted = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if can_be_fully_defaulted:
|
||||||
|
logger.info(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Required input map_type '{required_map_type}' for output '{output_map_type}' not found or not in usable state. Will attempt to use default values for its channels: {channels_requiring_this_map}.")
|
||||||
|
else:
|
||||||
|
logger.warning(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Required input map_type '{required_map_type}' for output '{output_map_type}' not found/unusable, AND not all its required channels ({channels_requiring_this_map}) have defaults. Failing merge op.")
|
||||||
|
all_inputs_valid = False
|
||||||
|
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': f"Input '{required_map_type}' missing and defaults incomplete."}
|
||||||
|
break
|
||||||
|
|
||||||
|
if found_processed_map_details:
|
||||||
|
temp_file_path_str = found_processed_map_details.get('temp_processed_file')
|
||||||
|
if not temp_file_path_str:
|
||||||
|
# Log with p_key_idx if available, or just the map type if not (though it should be if found_processed_map_details is set)
|
||||||
|
log_key_info = f"(Associated Key Index: {p_key_idx})" if 'p_key_idx' in locals() and found_processed_map_details else "" # Use locals() to check if p_key_idx is defined in this scope
|
||||||
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: 'temp_processed_file' missing in details for found map_type '{required_map_type}' {log_key_info}.")
|
||||||
|
all_inputs_valid = False
|
||||||
|
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': f"Temp file path missing for input '{required_map_type}'."}
|
||||||
|
break
|
||||||
|
|
||||||
|
temp_file_path = Path(temp_file_path_str)
|
||||||
if not temp_file_path.exists():
|
if not temp_file_path.exists():
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Temp file {temp_file_path} for input map_type '{required_map_type}' does not exist.")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Temp file {temp_file_path} for input map_type '{required_map_type}' does not exist.")
|
||||||
all_inputs_valid = False
|
all_inputs_valid = False
|
||||||
@ -153,7 +190,7 @@ class MapMergingStage(ProcessingStage):
|
|||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image_data = ipu.load_image(temp_file_path)
|
image_data = ipu.load_image(str(temp_file_path))
|
||||||
if image_data is None: raise ValueError("Loaded image is None")
|
if image_data is None: raise ValueError("Loaded image is None")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error loading image {temp_file_path} for input map_type '{required_map_type}': {e}")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error loading image {temp_file_path} for input map_type '{required_map_type}': {e}")
|
||||||
@ -170,9 +207,9 @@ class MapMergingStage(ProcessingStage):
|
|||||||
elif current_dims != target_dims:
|
elif current_dims != target_dims:
|
||||||
logger.warning(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Input map '{required_map_type}' dims {current_dims} differ from target {target_dims}. Resizing.")
|
logger.warning(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Input map '{required_map_type}' dims {current_dims} differ from target {target_dims}. Resizing.")
|
||||||
try:
|
try:
|
||||||
image_data = ipu.resize_image(image_data, target_dims[0], target_dims[1])
|
image_data_resized = ipu.resize_image(image_data, target_dims[0], target_dims[1])
|
||||||
if image_data is None: raise ValueError("Resize returned None")
|
if image_data_resized is None: raise ValueError("Resize returned None")
|
||||||
loaded_input_maps[required_map_type] = image_data
|
loaded_input_maps[required_map_type] = image_data_resized
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Failed to resize '{required_map_type}': {e}")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Failed to resize '{required_map_type}': {e}")
|
||||||
all_inputs_valid = False
|
all_inputs_valid = False
|
||||||
@ -183,14 +220,25 @@ class MapMergingStage(ProcessingStage):
|
|||||||
logger.warning(f"Asset {asset_name_for_log}: Skipping merge for Op ID {merge_op_id} ('{output_map_type}') due to invalid inputs.")
|
logger.warning(f"Asset {asset_name_for_log}: Skipping merge for Op ID {merge_op_id} ('{output_map_type}') due to invalid inputs.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not loaded_input_maps or target_dims is None:
|
if not loaded_input_maps and not any(default_values.get(ch) is not None for ch in inputs_map_type_to_channel.keys()):
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: No input maps loaded or target_dims not set for '{output_map_type}'. This shouldn't happen if all_inputs_valid was true.")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: No input maps loaded and no defaults available for any channel for '{output_map_type}'. Cannot proceed.")
|
||||||
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': 'Internal error: input maps not loaded or target_dims missing.'}
|
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': 'No input maps loaded and no defaults available.'}
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Determine output channels (e.g., 3 for RGB, 1 for Grayscale)
|
if target_dims is None:
|
||||||
# This depends on the keys in inputs_map_type_to_channel (R,G,B,A)
|
default_res_key = context.config_obj.get("default_output_resolution_key_for_merge", "1K")
|
||||||
output_channel_keys = sorted(list(inputs_map_type_to_channel.keys())) # e.g. ['B', 'G', 'R']
|
image_resolutions_cfg = getattr(context.config_obj, "image_resolutions", {})
|
||||||
|
default_max_dim = image_resolutions_cfg.get(default_res_key)
|
||||||
|
|
||||||
|
if default_max_dim:
|
||||||
|
target_dims = (default_max_dim, default_max_dim)
|
||||||
|
logger.info(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Target dimensions not set by inputs (all defaulted). Using configured default resolution '{default_res_key}': {target_dims}.")
|
||||||
|
else:
|
||||||
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Target dimensions could not be determined for '{output_map_type}' (all inputs defaulted and no default output resolution configured).")
|
||||||
|
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': 'Target dimensions undetermined for fully defaulted merge.'}
|
||||||
|
continue
|
||||||
|
|
||||||
|
output_channel_keys = sorted(list(inputs_map_type_to_channel.keys()))
|
||||||
num_output_channels = len(output_channel_keys)
|
num_output_channels = len(output_channel_keys)
|
||||||
|
|
||||||
if num_output_channels == 0:
|
if num_output_channels == 0:
|
||||||
@ -199,79 +247,86 @@ class MapMergingStage(ProcessingStage):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if num_output_channels == 1: # Grayscale output
|
output_dtype = np.uint8
|
||||||
merged_image = np.zeros((target_dims[1], target_dims[0]), dtype=np.uint8)
|
|
||||||
else: # Color output
|
if num_output_channels == 1:
|
||||||
merged_image = np.zeros((target_dims[1], target_dims[0], num_output_channels), dtype=np.uint8)
|
merged_image = np.zeros((target_dims[1], target_dims[0]), dtype=output_dtype)
|
||||||
|
else:
|
||||||
|
merged_image = np.zeros((target_dims[1], target_dims[0], num_output_channels), dtype=output_dtype)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error creating empty merged image for '{output_map_type}': {e}")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error creating empty merged image for '{output_map_type}': {e}")
|
||||||
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': f'Error creating output canvas: {e}'}
|
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': f'Error creating output canvas: {e}'}
|
||||||
continue
|
continue
|
||||||
|
|
||||||
merge_op_failed_detail = False
|
merge_op_failed_detail = False
|
||||||
for i, out_channel_char in enumerate(output_channel_keys): # e.g. R, G, B
|
for i, out_channel_char in enumerate(output_channel_keys):
|
||||||
input_map_type_for_this_channel = inputs_map_type_to_channel[out_channel_char]
|
input_map_type_for_this_channel = inputs_map_type_to_channel[out_channel_char]
|
||||||
source_image = loaded_input_maps.get(input_map_type_for_this_channel)
|
source_image = loaded_input_maps.get(input_map_type_for_this_channel)
|
||||||
|
|
||||||
source_data_this_channel = None
|
source_data_this_channel = None
|
||||||
if source_image is not None:
|
if source_image is not None:
|
||||||
if source_image.ndim == 2: # Grayscale source
|
if source_image.dtype != np.uint8:
|
||||||
|
logger.warning(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Input map '{input_map_type_for_this_channel}' has dtype {source_image.dtype}, expected uint8. Attempting conversion.")
|
||||||
|
source_image = ipu.convert_to_uint8(source_image)
|
||||||
|
if source_image is None:
|
||||||
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Failed to convert input '{input_map_type_for_this_channel}' to uint8.")
|
||||||
|
merge_op_failed_detail = True; break
|
||||||
|
|
||||||
|
|
||||||
|
if source_image.ndim == 2:
|
||||||
source_data_this_channel = source_image
|
source_data_this_channel = source_image
|
||||||
elif source_image.ndim == 3 or source_image.ndim == 4: # Color source (3-channel BGR or 4-channel BGRA), assumed loaded by ipu.load_image
|
elif source_image.ndim == 3:
|
||||||
# Standard BGR(A) channel indexing: B=0, G=1, R=2, A=3 (if present)
|
|
||||||
# This map helps get NRM's Red data for 'R' output, NRM's Green for 'G' output etc.
|
|
||||||
# based on the semantic meaning of out_channel_char.
|
|
||||||
semantic_to_bgr_idx = {'R': 2, 'G': 1, 'B': 0, 'A': 3}
|
semantic_to_bgr_idx = {'R': 2, 'G': 1, 'B': 0, 'A': 3}
|
||||||
|
|
||||||
if input_map_type_for_this_channel == "NRM":
|
idx_to_extract = semantic_to_bgr_idx.get(out_channel_char.upper())
|
||||||
idx_to_extract = semantic_to_bgr_idx.get(out_channel_char)
|
|
||||||
|
|
||||||
if idx_to_extract is not None and idx_to_extract < source_image.shape[2]:
|
if idx_to_extract is not None and idx_to_extract < source_image.shape[2]:
|
||||||
source_data_this_channel = source_image[:, :, idx_to_extract]
|
source_data_this_channel = source_image[:, :, idx_to_extract]
|
||||||
logger.debug(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: For output '{out_channel_char}', using NRM's semantic '{out_channel_char}' channel (BGR(A) index {idx_to_extract}).")
|
logger.debug(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: For output '{out_channel_char}', using source '{input_map_type_for_this_channel}' semantic '{out_channel_char}' (BGR(A) index {idx_to_extract}).")
|
||||||
else:
|
else:
|
||||||
# Fallback if out_channel_char isn't R,G,B,A or NRM doesn't have the channel (e.g. 3-channel NRM and 'A' requested)
|
logger.warning(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Could not map output '{out_channel_char}' to a specific BGR(A) channel of '{input_map_type_for_this_channel}' (shape {source_image.shape}). Defaulting to its channel 0 (Blue).")
|
||||||
logger.warning(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Could not map output '{out_channel_char}' to a specific BGR(A) channel of NRM (shape {source_image.shape}). Defaulting to NRM's channel 0 (Blue).")
|
|
||||||
source_data_this_channel = source_image[:, :, 0]
|
source_data_this_channel = source_image[:, :, 0]
|
||||||
else:
|
else:
|
||||||
# For other multi-channel sources (e.g., ROUGH as RGB, or other color maps not "NRM")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Source image '{input_map_type_for_this_channel}' has unexpected dimensions: {source_image.ndim} (shape {source_image.shape}).")
|
||||||
# Default to taking the first channel (Blue in BGR).
|
merge_op_failed_detail = True; break
|
||||||
# This covers "Roughness map's greyscale data" if ROUGH is RGB (by taking one of its channels as a proxy).
|
|
||||||
source_data_this_channel = source_image[:, :, 0]
|
else:
|
||||||
logger.debug(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: For output '{out_channel_char}', source {input_map_type_for_this_channel} (shape {source_image.shape}) is multi-channel but not NRM. Using its channel 0 (Blue).")
|
|
||||||
else: # Source map was not found, use default
|
|
||||||
default_val_for_channel = default_values.get(out_channel_char)
|
default_val_for_channel = default_values.get(out_channel_char)
|
||||||
if default_val_for_channel is not None:
|
if default_val_for_channel is not None:
|
||||||
# Convert 0-1 float default to 0-255 uint8
|
try:
|
||||||
source_data_this_channel = np.full((target_dims[1], target_dims[0]), int(default_val_for_channel * 255), dtype=np.uint8)
|
scaled_default_val = int(float(default_val_for_channel) * 255)
|
||||||
logger.info(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Using default value {default_val_for_channel} for output channel '{out_channel_char}' as input map '{input_map_type_for_this_channel}' was missing.")
|
source_data_this_channel = np.full((target_dims[1], target_dims[0]), scaled_default_val, dtype=np.uint8)
|
||||||
|
logger.info(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Using default value {default_val_for_channel} (scaled to {scaled_default_val}) for output channel '{out_channel_char}' as input map '{input_map_type_for_this_channel}' was missing.")
|
||||||
|
except ValueError:
|
||||||
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Default value '{default_val_for_channel}' for channel '{out_channel_char}' is not a valid float. Cannot scale.")
|
||||||
|
merge_op_failed_detail = True; break
|
||||||
else:
|
else:
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Input map '{input_map_type_for_this_channel}' for output channel '{out_channel_char}' is missing and no default value provided.")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Input map '{input_map_type_for_this_channel}' for output channel '{out_channel_char}' is missing and no default value provided.")
|
||||||
merge_op_failed_detail = True; break
|
merge_op_failed_detail = True; break
|
||||||
|
|
||||||
if source_data_this_channel is None: # Should be caught by default value logic or earlier checks
|
if source_data_this_channel is None:
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Failed to get source data for output channel '{out_channel_char}'.")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Failed to get source data for output channel '{out_channel_char}'.")
|
||||||
merge_op_failed_detail = True; break
|
merge_op_failed_detail = True; break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if merged_image.ndim == 2: # Single channel output
|
if merged_image.ndim == 2:
|
||||||
merged_image = source_data_this_channel
|
merged_image = source_data_this_channel
|
||||||
else: # Multi-channel output
|
else:
|
||||||
merged_image[:, :, i] = source_data_this_channel
|
merged_image[:, :, i] = source_data_this_channel
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error assigning data to output channel '{out_channel_char}' (index {i}): {e}")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error assigning data to output channel '{out_channel_char}' (index {i}): {e}. Merged shape: {merged_image.shape}, Source data shape: {source_data_this_channel.shape}")
|
||||||
merge_op_failed_detail = True; break
|
merge_op_failed_detail = True; break
|
||||||
|
|
||||||
if merge_op_failed_detail:
|
if merge_op_failed_detail:
|
||||||
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': 'Error during channel assignment.'}
|
context.merged_maps_details[merge_op_id] = {'map_type': output_map_type, 'status': 'Failed', 'reason': 'Error during channel assignment.'}
|
||||||
continue
|
continue
|
||||||
|
|
||||||
output_format = 'png' # Default, can be configured per rule later
|
output_format = 'png'
|
||||||
temp_merged_filename = f"merged_{sanitize_filename(output_map_type)}_{merge_op_id}.{output_format}"
|
temp_merged_filename = f"merged_{sanitize_filename(output_map_type)}_{merge_op_id}.{output_format}"
|
||||||
temp_merged_path = context.engine_temp_dir / temp_merged_filename
|
temp_merged_path = context.engine_temp_dir / temp_merged_filename
|
||||||
|
|
||||||
try:
|
try:
|
||||||
save_success = ipu.save_image(temp_merged_path, merged_image)
|
save_success = ipu.save_image(str(temp_merged_path), merged_image)
|
||||||
if not save_success: raise ValueError("Save image returned false")
|
if not save_success: raise ValueError("Save image returned false")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error saving merged image {temp_merged_path}: {e}")
|
logger.error(f"Asset {asset_name_for_log}, Merge Op ID {merge_op_id}: Error saving merged image {temp_merged_path}: {e}")
|
||||||
|
|||||||
@ -66,7 +66,56 @@ class MetadataFinalizationAndSaveStage(ProcessingStage):
|
|||||||
context.asset_metadata['status'] = "Processed"
|
context.asset_metadata['status'] = "Processed"
|
||||||
|
|
||||||
# Add details of processed and merged maps
|
# Add details of processed and merged maps
|
||||||
context.asset_metadata['processed_map_details'] = getattr(context, 'processed_maps_details', {})
|
# Restructure processed_map_details before assigning
|
||||||
|
restructured_processed_maps = {}
|
||||||
|
# getattr(context, 'processed_maps_details', {}) is the source (plural 'maps')
|
||||||
|
original_processed_maps = getattr(context, 'processed_maps_details', {})
|
||||||
|
|
||||||
|
# Define keys to remove at the top level of each map entry
|
||||||
|
map_keys_to_remove = [
|
||||||
|
"status", "source_file_path", "temp_processed_file", # Assuming "source_file_path" is the correct key
|
||||||
|
"original_resolution_name", "base_pot_resolution_name", "processed_resolution_name"
|
||||||
|
]
|
||||||
|
# Define keys to remove from each variant
|
||||||
|
variant_keys_to_remove = ["temp_path", "dimensions"]
|
||||||
|
|
||||||
|
for map_key, map_detail_original in original_processed_maps.items():
|
||||||
|
# Create a new dictionary for the modified map entry
|
||||||
|
new_map_entry = {}
|
||||||
|
for key, value in map_detail_original.items():
|
||||||
|
if key not in map_keys_to_remove:
|
||||||
|
new_map_entry[key] = value
|
||||||
|
|
||||||
|
if "variants" in map_detail_original and isinstance(map_detail_original["variants"], dict):
|
||||||
|
new_variants_dict = {}
|
||||||
|
for variant_name, variant_data_original in map_detail_original["variants"].items():
|
||||||
|
new_variant_entry = {}
|
||||||
|
for key, value in variant_data_original.items():
|
||||||
|
if key not in variant_keys_to_remove:
|
||||||
|
new_variant_entry[key] = value
|
||||||
|
|
||||||
|
# Add 'path_to_file'
|
||||||
|
# This path is expected to be set by OutputOrganizationStage in the context.
|
||||||
|
# It should be a Path object representing the path relative to the metadata directory,
|
||||||
|
# or an absolute Path that make_serializable can convert.
|
||||||
|
# Using 'final_output_path_for_metadata' as the key from context.
|
||||||
|
if 'final_output_path_for_metadata' in variant_data_original:
|
||||||
|
new_variant_entry['path_to_file'] = variant_data_original['final_output_path_for_metadata']
|
||||||
|
else:
|
||||||
|
# Log a warning if the expected path is not found
|
||||||
|
logger.warning(
|
||||||
|
f"Asset '{asset_name_for_log}': 'final_output_path_for_metadata' "
|
||||||
|
f"missing for variant '{variant_name}' in map '{map_key}'. "
|
||||||
|
f"Metadata will be incomplete for this variant's path."
|
||||||
|
)
|
||||||
|
new_variant_entry['path_to_file'] = "ERROR_PATH_NOT_FOUND" # Placeholder
|
||||||
|
new_variants_dict[variant_name] = new_variant_entry
|
||||||
|
new_map_entry["variants"] = new_variants_dict
|
||||||
|
|
||||||
|
restructured_processed_maps[map_key] = new_map_entry
|
||||||
|
|
||||||
|
# Assign the restructured details. Note: 'processed_map_details' (singular 'map') is the key in asset_metadata.
|
||||||
|
context.asset_metadata['processed_map_details'] = restructured_processed_maps
|
||||||
context.asset_metadata['merged_map_details'] = getattr(context, 'merged_maps_details', {})
|
context.asset_metadata['merged_map_details'] = getattr(context, 'merged_maps_details', {})
|
||||||
|
|
||||||
# (Optional) Add a list of all temporary files
|
# (Optional) Add a list of all temporary files
|
||||||
|
|||||||
@ -212,6 +212,8 @@ class OutputOrganizationStage(ProcessingStage):
|
|||||||
variant_detail['status'] = 'Organized'
|
variant_detail['status'] = 'Organized'
|
||||||
|
|
||||||
variant_detail['final_output_path'] = str(final_variant_path)
|
variant_detail['final_output_path'] = str(final_variant_path)
|
||||||
|
# Store the Path object for metadata stage to make it relative later
|
||||||
|
variant_detail['final_output_path_for_metadata'] = final_variant_path
|
||||||
relative_final_variant_path_str = str(Path(relative_dir_path_str_variant) / Path(output_filename_variant))
|
relative_final_variant_path_str = str(Path(relative_dir_path_str_variant) / Path(output_filename_variant))
|
||||||
map_metadata_entry['variant_paths'][variant_resolution_key] = relative_final_variant_path_str
|
map_metadata_entry['variant_paths'][variant_resolution_key] = relative_final_variant_path_str
|
||||||
processed_any_variant_successfully = True
|
processed_any_variant_successfully = True
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user