"""
Author: Ismael Seidel
Affiliation: Embedded Computing Lab (ECL), Federal University of Santa Catarina (UFSC)
Description:
This module renames PPM files to select a subset of the views and starts counting from view 000_000.
This implementation is for JPEG Pleno CE 14.
"""
import json
import os
import shutil
import sys
from hashlib import md5
# Add the parent directory to the Python path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from lfc_toolkit.src.converters.ffmpeg import FFMPEGLightFieldFormatConverter
from lfc_toolkit.src.data_handlers.lightfield import (
LightField, RAW_BT709_FR_YUV444p10le_LightField_Data, RAWLightFieldData)
[docs]
def get_lenslet_lf(name: str) -> LightField:
"""Create a LightField object for a lenslet configuration (11x11 views).
:param name: Light field name
:type name: str
:return: Light field object with lenslet specifications
:rtype: LightField
"""
return LightField(
name=name, view_width=625, view_height=434, n_views_width=11, n_views_height=11
)
[docs]
def get_raw_lenslet_lf_ppm(name: str, raw_ppm_path: str) -> RAWLightFieldData:
"""Create a raw light field data object for lenslet PPM files.
:param name: Light field name
:type name: str
:param raw_ppm_path: Path to the raw PPM files
:type raw_ppm_path: str
:return: Raw light field data object
:rtype: RAWLightFieldData
"""
return RAWLightFieldData(
lightfield=get_lenslet_lf(name),
initial_width=0,
initial_height=0,
raw_path=f"{raw_ppm_path}/{name}",
pix_fmt="PPM",
colour_space="sRGB",
)
[docs]
def load_paths_from_json(cfg_file: str) -> dict:
"""Load paths configuration from a JSON file.
Loads the raw_ppm_path, selected_ppm_path, and other paths from a JSON configuration file.
:param cfg_file: Path to the JSON configuration file
:type cfg_file: str
:return: Dictionary containing paths from the JSON file
:rtype: dict
"""
with open(cfg_file, "r") as file:
data = json.load(file)
return data
[docs]
def copy_and_rename_files(
raw_ppm_path: str, selected_ppm_path: str, xxx: int, yyy: int, limit_xxx: int, limit_yyy: int
) -> None:
"""Copy and rename PPM files from source to destination with view indexing.
Copies files from raw_ppm_path to selected_ppm_path, renaming them from xxx_yyy.ppm
to 000_000.ppm format, within the specified view limits.
:param raw_ppm_path: Path to the raw source files
:type raw_ppm_path: str
:param selected_ppm_path: Path to the output folder
:type selected_ppm_path: str
:param xxx: Offset for the first view index dimension
:type xxx: int
:param yyy: Offset for the second view index dimension
:type yyy: int
:param limit_xxx: Maximum value for first dimension before stopping
:type limit_xxx: int
:param limit_yyy: Maximum value for second dimension before resetting
:type limit_yyy: int
:return: None
:rtype: None
"""
# Create output folder if it doesn't exist
if not os.path.exists(selected_ppm_path):
os.makedirs(selected_ppm_path)
# Loop to rename and copy files
source_xxx = xxx
source_yyy = yyy
target_xxx = 0
target_yyy = 0
while target_xxx < limit_xxx:
# Format the source file name with source_xxx and source_yyy
source_file = f"{source_xxx:03d}_{source_yyy:03d}.ppm"
source_path = os.path.join(raw_ppm_path, source_file)
# Check if the file exists in the source folder
if not os.path.exists(source_path):
print(f"File {source_file} not found. Stopping.")
break
# Format the target file name with target_xxx and target_yyy
target_file = f"{target_xxx:03d}_{target_yyy:03d}.ppm"
target_path = os.path.join(selected_ppm_path, target_file)
# Copy the file to the output folder with the new name
shutil.copy(source_path, target_path)
print(f"Copied {source_file} to {target_file}")
target_yyy += 1
source_yyy += 1
if target_yyy >= limit_yyy:
target_yyy = 0
source_yyy = yyy
target_xxx += 1
source_xxx += 1
[docs]
def main() -> None:
"""Generate 11x11 PPM files for lenslet light fields.
Processes lenslet light fields to extract and rename views into 11x11 grid format,
starting from view 000_000. Performs validation by converting to YUV and comparing
with previously generated YUV files.
Usage: python generate_11x11_ppm_for_lenslets.py <configuration.json>
:return: None
:rtype: None
"""
if not len(sys.argv) == 2:
print(
"Error. Expecting a parameter with the name of the JSON configuration file with the paths (ppm, yuv, and pgx)."
)
exit(1)
cfg_file = sys.argv[1]
print("Using the paths from", cfg_file)
# Open and read the JSON file
configuration = load_paths_from_json(cfg_file)
# Extract paths
raw_paths = configuration["raw_paths"]
raw_ppm_path = raw_paths["ppm"]
selected_ppm_path = raw_paths["ppm11x11"]
print(f"{selected_ppm_path =}")
# using the raw_yuv_from_pgx_path to cross check
raw_yuv_from_pgx_path = raw_paths["yuv_check"]
remove_yuv_from_pgx_after_check = configuration["remove_yuv_from_pgx_after_check"]
# the ideia is to compare with already generated YUV files that should be in this path
raw_yuv_path = raw_paths["yuv"]
# TODO: use this!
output_log_filename = configuration["logs"]["generate_11x11_ppm"]
lenslets = configuration["lightfields"]["lenslets"]
# Set offsets and limits
xxx = 2 # Set the offset for xxx
yyy = 2 # Set the offset for yyy
limit_xxx = 11 # Maximum value for xxx
limit_yyy = 11 # Maximum value for yyy
matches = dict()
# Call the function to copy and rename files
for lenslet in lenslets:
print(f"Creating 11x11 version for {lenslet} with PPM files.")
copy_and_rename_files(
f"{raw_ppm_path}/{lenslet}",
f"{selected_ppm_path}/{lenslet}",
xxx,
yyy,
limit_xxx,
limit_yyy,
)
# performing cross check by generating yuv file
print(f"Generating YUV for validation with previously generated YUV")
already_existing_yuv = RAW_BT709_FR_YUV444p10le_LightField_Data(
lightfield=get_lenslet_lf(lenslet), yuv_path=raw_yuv_path
)
md5_of_already_existing_yuv = md5(
open(already_existing_yuv.raw_path, "rb").read()
).hexdigest()
print("MD5 from existing YUV:", md5_of_already_existing_yuv)
print("Converting the new PPM to YUV for crosscheck")
yuv_from_ppm: RAW_BT709_FR_YUV444p10le_LightField_Data = (
FFMPEGLightFieldFormatConverter.convert_rgb_ppm_lf_to_bt709fr_yuv(
get_raw_lenslet_lf_ppm(lenslet, os.path.abspath(selected_ppm_path)),
yuv_path=raw_yuv_from_pgx_path,
)
)
md5_of_yuv_from_ppm = md5(yuv_from_ppm.raw_path.read_bytes()).hexdigest()
matches[lenslet] = md5_of_yuv_from_ppm == md5_of_already_existing_yuv
print("MD5 from new YUV:", md5_of_yuv_from_ppm)
if matches[lenslet]:
print(
"Already existing YUV file matches an YUV created from the 11x11 PPM."
)
else:
print(
"Something went wrong! Already existing YUV file does not match the YUV created from the 11x11 PPM."
)
if remove_yuv_from_pgx_after_check and os.path.exists(yuv_from_ppm.raw_path):
print("Removing the YUV generated from 11x11 PPM (created to crosscheck).")
os.remove(yuv_from_ppm.raw_path)
print("")
print("Finished for all lenslets. Summary: ")
for lenslet, match in matches.items():
print(lenslet, "OK" if match else "Error")
if __name__ == "__main__":
main()