import os
import shutil
import glob
import mesh2hrtf as m2h
[docs]
def process_multiple_outputs2hrtf(
paths, merge=False, inspect=False, pattern=None,
plot=None, plane="horizontal", atol=.1, savedir=None):
"""
Process NumCalc outputs from multiple projects and write data to disk.
This is a convenience function that process multiple Mesh2HRTF projects and
offers to merge and plot the results in one call:
1.
Run :py:func:`~mesh2hrtf.output2hrtf` in folders specified by
`paths`. This also calls
:py:func:`~mesh2hrtf.write_output_report`,
:py:func:`~mesh2hrtf.reference_hrtfs`, and
:py:func:`~mesh2hrtf.compute_hrirs`.
2.
Run :py:func:`~mesh2hrtf.merge_sofa_files` (optional).
3.
Run :py:func:`~mesh2hrtf.inspect_sofa_files` (optional). If merge is
``True``, only the merged data is inspected.
4.
Move data to `savedir` (optional). If merge is ``True`` only the merged
data will be moved and the remaining data is deleted. The file names
of the moved data stat with the name of the folder from which they were
moved to `savedir`.
Parameters
----------
paths : str, tuple of strings
Paths to search for Mesh2HRTF projects. If `paths` is a tuple, the
projects found in the paths will be merged if `merge` is ``True``.
E.g., if ``paths[0]`` is ``"some/path/left/*"`` and ``paths[1]`` is
``"some/path/right/*"`` all SOFA files in the matching folders will be
merged. If `merge` is ``False`` all projects will be processed
independently.
merge : bool, optional
``True`` to merge data from different projets using
:py:func:`~mesh2hrtf.merge_sofa_files` (see `paths` above). The
default ``False`` does not merge data.
inspect : bool, optional
``True`` to plot data the results from different projets using
:py:func:`~mesh2hrtf.inspect_sofa_files`. If `merge` is ``True`` this
is only done for the merged data. The default ``False`` does not plot
the data.
pattern : str, optional
Merge, inspect and move only SOFA files that have `pattern` in their
file names. E.g., if `pattern` is ``"HRIR"`` only the SOFA files
containing HRIR in their name will be merged, inspected, and coped to
`savedir` (see below).
plot : str, optional
This parameter is passed to :py:func:`~mesh2hrtf.inspect_sofa_files`.
plane : str, optional
This parameter is passed to :py:func:`~mesh2hrtf.inspect_sofa_files`.
atol : float, optional
This parameter is passed to :py:func:`~mesh2hrtf.inspect_sofa_files`.
savedir : str, optional
Path under which the results are saved. The default ``None`` will
leave the data in their project folders, any other value will move
the data to `savedir` and delete all data in
`project_folder/Output2HRTF` except for the project reports.
Raises
------
ValueError
If issues were detected in any Mesh2HRTF project. The value error is
raised after all data was processed.
"""
# check input -------------------------------------------------------------
if isinstance(paths, str):
paths = (paths, )
if not isinstance(paths, (tuple, list)) or len(paths) > 2:
raise ValueError(
"paths must be a string or a tuple with a maximum length of two")
if merge and len(paths) != 2:
raise ValueError("path must be a tuple of length two if merge is True")
# create save directories if it does not exist
# (already done here to throw early errors if savedir is invalid)
if savedir is not None and not os.path.isdir(savedir):
os.mkdir(savedir)
# loop paths for running output_2_hrtf in folders -------------------------
print("Generating SOFA files")
print("---------------------")
# for tracking of issues
issues = []
for pp, path in enumerate(paths):
# loop folders in path
folders = glob.glob(path)
for ff, folder in enumerate(folders):
# check
name = os.path.basename(folder)
if not os.path.isfile(os.path.join(folder, "parameters.json")):
print(f"Skipping: {name}")
continue
print((f"{name} (path {pp+1}/{len(paths)}, "
f"folder {ff+1}/{len(folders)})"))
# run output2hrtf
m2h.output2hrtf(folder)
# track issues
if os.path.isfile(os.path.join(
folder, "Output2HRTF", "report_issues.txt")):
issues.append(folder)
# merge SOFA files --------------------------------------------------------
if merge:
print("\nMerging SOFA files")
print("------------------")
m2h.merge_sofa_files(paths, pattern)
# if SOFA files were merged, only the merged files are used in the
# following (merged files saved under paths[0])
paths_inspect = (paths[0], )
pattern = "*merged*" if pattern is None else pattern + "*merged*"
else:
paths_inspect = paths
# inspect SOFA files ------------------------------------------------------
if inspect:
print("\nInspecting SOFA files")
print("---------------------")
for pp, path in enumerate(paths_inspect):
print(f"{path} ({pp+1}/{len(paths_inspect)})")
m2h.inspect_sofa_files(path, pattern, plot, plane, atol)
# return if data should be left in place ----------------------------------
if savedir is None:
if issues:
msg = ("Detected issues in NumCalc output. Check report files in "
"the following Output2HRTF folders:\n")
msg += "\n".join(issues)
raise ValueError(msg)
print("\nfinished")
return
# move data to savedir ----------------------------------------------------
print("\nMoving results to savedir")
print("-------------------------")
for pp, path in enumerate(paths):
# loop folders in path
folders = glob.glob(path)
if pattern is None:
pattern = "*"
for ff, folder in enumerate(folders):
name = os.path.basename(folder)
# copy data
if pp == 0 or not merge:
# sofa files and plots
for file in glob.glob(os.path.join(
folder, "Output2HRTF", pattern)):
if file.endswith((".sofa", ".pdf", ".jpeg")):
copy_name = name + "_" + os.path.basename(
file).replace("_merged", "")
shutil.copyfile(file,
os.path.join(savedir, copy_name))
# remove data
for file in glob.glob(os.path.join(folder, "Output2HRTF", "*")):
if "report" not in file:
os.remove(file)
if issues:
msg = ("Detected issues in NumCalc output. Check report files in "
f"{savedir} and the following Output2HRTF folders:\n")
msg += "\n".join(issues)
with open(os.path.join(savedir, "report_issues.txt"), "w") as issues:
issues.write(msg)
raise ValueError(msg)
print("\nfinished")
return