fix: normalize line endings to LF (#118)

This commit is contained in:
slz
2026-08-05 15:16:27 +08:00
parent e967d85067
commit 66541c6d32
17 changed files with 4219 additions and 4204 deletions
+103 -103
View File
@@ -1,104 +1,104 @@
# ----------------------------- # -----------------------------
# Options affecting formatting. # Options affecting formatting.
# ----------------------------- # -----------------------------
with section("format"): with section("format"):
# Disable formatting entirely, making cmake-format a no-op # Disable formatting entirely, making cmake-format a no-op
disable = False disable = False
# How wide to allow formatted cmake files # How wide to allow formatted cmake files
line_width = 120 line_width = 120
# How many spaces to tab for indent # How many spaces to tab for indent
tab_size = 4 tab_size = 4
# If true, lines are indented using tab characters (utf-8 0x09) instead of # If true, lines are indented using tab characters (utf-8 0x09) instead of
# <tab_size> space characters (utf-8 0x20). In cases where the layout would # <tab_size> space characters (utf-8 0x20). In cases where the layout would
# require a fractional tab character, the behavior of the fractional # require a fractional tab character, the behavior of the fractional
# indentation is governed by <fractional_tab_policy> # indentation is governed by <fractional_tab_policy>
use_tabchars = False use_tabchars = False
# If <use_tabchars> is True, then the value of this variable indicates how # If <use_tabchars> is True, then the value of this variable indicates how
# fractional indentions are handled during whitespace replacement. If set to # fractional indentions are handled during whitespace replacement. If set to
# 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set
# to `round-up` fractional indentation is replaced with a single tab character # to `round-up` fractional indentation is replaced with a single tab character
# (utf-8 0x09) effectively shifting the column to the next tabstop # (utf-8 0x09) effectively shifting the column to the next tabstop
fractional_tab_policy = 'use-space' fractional_tab_policy = 'use-space'
# If an argument group contains more than this many sub-groups (parg or kwarg # If an argument group contains more than this many sub-groups (parg or kwarg
# groups) then force it to a vertical layout. # groups) then force it to a vertical layout.
max_subgroups_hwrap = 4 max_subgroups_hwrap = 4
# If a positional argument group contains more than this many arguments, then # If a positional argument group contains more than this many arguments, then
# force it to a vertical layout. # force it to a vertical layout.
max_pargs_hwrap = 6 max_pargs_hwrap = 6
# If a cmdline positional group consumes more than this many lines without # If a cmdline positional group consumes more than this many lines without
# nesting, then invalidate the layout (and nest) # nesting, then invalidate the layout (and nest)
max_rows_cmdline = 2 max_rows_cmdline = 2
# If true, separate flow control names from their parentheses with a space # If true, separate flow control names from their parentheses with a space
separate_ctrl_name_with_space = False separate_ctrl_name_with_space = False
# If true, separate function names from parentheses with a space # If true, separate function names from parentheses with a space
separate_fn_name_with_space = False separate_fn_name_with_space = False
# If a statement is wrapped to more than one line, than dangle the closing # If a statement is wrapped to more than one line, than dangle the closing
# parenthesis on its own line. # parenthesis on its own line.
dangle_parens = True dangle_parens = True
# If the trailing parenthesis must be 'dangled' on its on line, then align it # If the trailing parenthesis must be 'dangled' on its on line, then align it
# to this reference: `prefix`: the start of the statement, `prefix-indent`: # to this reference: `prefix`: the start of the statement, `prefix-indent`:
# the start of the statement, plus one indentation level, `child`: align to # the start of the statement, plus one indentation level, `child`: align to
# the column of the arguments # the column of the arguments
dangle_align = 'prefix' dangle_align = 'prefix'
# If the statement spelling length (including space and parenthesis) is # If the statement spelling length (including space and parenthesis) is
# smaller than this amount, then force reject nested layouts. # smaller than this amount, then force reject nested layouts.
min_prefix_chars = 4 min_prefix_chars = 4
# If the statement spelling length (including space and parenthesis) is larger # If the statement spelling length (including space and parenthesis) is larger
# than the tab width by more than this amount, then force reject un-nested # than the tab width by more than this amount, then force reject un-nested
# layouts. # layouts.
max_prefix_chars = 10 max_prefix_chars = 10
# If a candidate layout is wrapped horizontally but it exceeds this many # If a candidate layout is wrapped horizontally but it exceeds this many
# lines, then reject the layout. # lines, then reject the layout.
max_lines_hwrap = 2 max_lines_hwrap = 2
# What style line endings to use in the output. # What style line endings to use in the output.
line_ending = 'unix' line_ending = 'unix'
# Format command names consistently as 'lower' or 'upper' case # Format command names consistently as 'lower' or 'upper' case
command_case = 'canonical' command_case = 'canonical'
# Format keywords consistently as 'lower' or 'upper' case # Format keywords consistently as 'lower' or 'upper' case
keyword_case = 'unchanged' keyword_case = 'unchanged'
# A list of command names which should always be wrapped # A list of command names which should always be wrapped
always_wrap = [] always_wrap = []
# If true, the argument lists which are known to be sortable will be sorted # If true, the argument lists which are known to be sortable will be sorted
# lexicographicall # lexicographicall
enable_sort = True enable_sort = True
# If true, the parsers may infer whether or not an argument list is sortable # If true, the parsers may infer whether or not an argument list is sortable
# (without annotation). # (without annotation).
autosort = False autosort = False
# By default, if cmake-format cannot successfully fit everything into the # By default, if cmake-format cannot successfully fit everything into the
# desired linewidth it will apply the last, most agressive attempt that it # desired linewidth it will apply the last, most agressive attempt that it
# made. If this flag is True, however, cmake-format will print error, exit # made. If this flag is True, however, cmake-format will print error, exit
# with non-zero status code, and write-out nothing # with non-zero status code, and write-out nothing
require_valid_layout = False require_valid_layout = False
# A dictionary mapping layout nodes to a list of wrap decisions. See the # A dictionary mapping layout nodes to a list of wrap decisions. See the
# documentation for more information. # documentation for more information.
layout_passes = {} layout_passes = {}
# ----------------------------- # -----------------------------
# Options affecting comment formatting (indirectly). # Options affecting comment formatting (indirectly).
# ----------------------------- # -----------------------------
with section("markup"): with section("markup"):
# Disabling markup avoids merging consecutive comment lines # Disabling markup avoids merging consecutive comment lines
enable_markup = False enable_markup = False
+15
View File
@@ -0,0 +1,15 @@
# Normalize text files to LF in both the repository and working tree.
* text=auto eol=lf
# Treat generated assets and bundled binaries as binary files.
*.stl binary
*.STL binary
*.so binary
*.so.* binary
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.pdf binary
*.zip binary
*.gz binary
@@ -1,8 +1,8 @@
[Parameter] [Parameter]
# Unit: FPS. Please fill in according to the actual dataset frame rate, # Unit: FPS. Please fill in according to the actual dataset frame rate,
# otherwise the matching will be inaccurate. # otherwise the matching will be inaccurate.
frameRate=30 frameRate=30
# Unit: ms. When the timestamp range difference of a matched frame group # Unit: ms. When the timestamp range difference of a matched frame group
# is greater than or equal to tspRangeThreshold, the file name will be marked. # is greater than or equal to tspRangeThreshold, the file name will be marked.
tspRangeThreshold=4 tspRangeThreshold=4
@@ -1,7 +1,7 @@
## config_frameMatch.py ## config_frameMatch.py
**Definition of the three folders:** **Definition of the three folders:**
- **matchFrames**: Data constrained within a half-frame. - **matchFrames**: Data constrained within a half-frame.
- **notMatchFrames**: Data not constrained within a half-frame, unmatched. - **notMatchFrames**: Data not constrained within a half-frame, unmatched.
- **abnormal**: Data within the group that exceeds the `tspRangeThreshold` setting. - **abnormal**: Data within the group that exceeds the `tspRangeThreshold` setting.
@@ -1,90 +1,90 @@
import os import os
import subprocess import subprocess
import json import json
import platform import platform
def list_subdirectories(path): def list_subdirectories(path):
for f in os.listdir(path): for f in os.listdir(path):
if os.path.isdir(os.path.join(path, f)): if os.path.isdir(os.path.join(path, f)):
yield f yield f
def read_pid_from_device_json(frames_dir): def read_pid_from_device_json(frames_dir):
file_path = frames_dir + "/DevicesInfo.txt" file_path = frames_dir + "/DevicesInfo.txt"
if not os.path.exists(file_path): if not os.path.exists(file_path):
print(file_path + " not exists. Please check it") print(file_path + " not exists. Please check it")
return "" return ""
with open(file_path, 'r') as f: with open(file_path, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devicePid' in data: if not 'devicePid' in data:
print("Not found 'devicePid' in " + file_path) print("Not found 'devicePid' in " + file_path)
return "" return ""
return data['devicePid'] return data['devicePid']
def execute_sync_width_pid(frames_dir, pid): def execute_sync_width_pid(frames_dir, pid):
sync_script_file="" sync_script_file=""
if pid.lower() == "0x0675": if pid.lower() == "0x0675":
sync_script_file="script/config_frameMatch_Gemini2VL.py" sync_script_file="script/config_frameMatch_Gemini2VL.py"
elif pid.lower() == "0x0670" or pid.lower() == "0x0701": elif pid.lower() == "0x0670" or pid.lower() == "0x0701":
sync_script_file="script/config_frameMatch-systemTimestamp.py" sync_script_file="script/config_frameMatch-systemTimestamp.py"
elif pid.lower() == "0x0660": elif pid.lower() == "0x0660":
sync_script_file="script/config_frameMatch-Astra2_new.py" sync_script_file="script/config_frameMatch-Astra2_new.py"
elif pid.lower() == "0x0800" or pid.lower() == "0x0804" or pid.lower() == "0x0803" or pid.lower() == "0x0807" or pid.lower() == "0x0801" or pid.lower() == "0x0805" or pid.lower() == "0x080b" or pid.lower() == "0x080e" or pid.lower() == "0x080f" or pid.lower() == "0x0A13": elif pid.lower() == "0x0800" or pid.lower() == "0x0804" or pid.lower() == "0x0803" or pid.lower() == "0x0807" or pid.lower() == "0x0801" or pid.lower() == "0x0805" or pid.lower() == "0x080b" or pid.lower() == "0x080e" or pid.lower() == "0x080f" or pid.lower() == "0x0A13":
sync_script_file="script/config_frameMatch_g330.py" sync_script_file="script/config_frameMatch_g330.py"
else: else:
sync_script_file="script/config_frameMatch.py" sync_script_file="script/config_frameMatch.py"
if len(sync_script_file) > 0: if len(sync_script_file) > 0:
print(f"execute synchronize frames. script_file={sync_script_file}, frames_dir={frames_dir}") print(f"execute synchronize frames. script_file={sync_script_file}, frames_dir={frames_dir}")
if platform.system() == "Windows": if platform.system() == "Windows":
output = subprocess.check_output(["python", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()]) output = subprocess.check_output(["python", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()])
print(output.decode("gbk")) print(output.decode("gbk"))
else: else:
output = subprocess.check_output(["python3", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()]) output = subprocess.check_output(["python3", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()])
print(output.decode("utf-8")) print(output.decode("utf-8"))
else: else:
print("Invalid pid=" + pid + ", not match sync frame script") print("Invalid pid=" + pid + ", not match sync frame script")
def sync_frames(frames_dir): def sync_frames(frames_dir):
pid = read_pid_from_device_json(frames_dir) pid = read_pid_from_device_json(frames_dir)
if len(pid) <= 0: if len(pid) <= 0:
print("Get pid failed.") print("Get pid failed.")
return return
execute_sync_width_pid(frames_dir, pid) execute_sync_width_pid(frames_dir, pid)
def main(): def main():
frames_output_path = os.path.abspath("../output") frames_output_path = os.path.abspath("../output")
subdirectories = list(list_subdirectories(frames_output_path)) subdirectories = list(list_subdirectories(frames_output_path))
if not subdirectories: if not subdirectories:
print("No subdirectories found in the specified directory.") print("No subdirectories found in the specified directory.")
for i, subdir in enumerate(subdirectories): for i, subdir in enumerate(subdirectories):
print(f"{i + 1}. {subdir}") print(f"{i + 1}. {subdir}")
choice = input("Please select a subdirectory by number (or 'q' to quit): ") choice = input("Please select a subdirectory by number (or 'q' to quit): ")
if choice.lower() == 'q': if choice.lower() == 'q':
return return
is_index_valid = False is_index_valid = False
try: try:
index = int(choice) - 1 index = int(choice) - 1
if 0 <= index < len(subdirectories): if 0 <= index < len(subdirectories):
is_index_valid = True is_index_valid = True
else: else:
print(f"Invalid choice. index={index}. Please enter a number between 1 and the total number of subdirectories.") print(f"Invalid choice. index={index}. Please enter a number between 1 and the total number of subdirectories.")
except ValueError: except ValueError:
print("Invalid choice(ValueException). Please enter a number between 1 and the total number of subdirectories") print("Invalid choice(ValueException). Please enter a number between 1 and the total number of subdirectories")
if is_index_valid: if is_index_valid:
selected_subdir = subdirectories[index] selected_subdir = subdirectories[index]
print(f"You selected the subdirectory: {selected_subdir}") print(f"You selected the subdirectory: {selected_subdir}")
sync_frames(f"{frames_output_path }/{selected_subdir}") sync_frames(f"{frames_output_path }/{selected_subdir}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()
@@ -1,392 +1,392 @@
import os.path import os.path
import argparse import argparse
import sys import sys
import json import json
import shutil import shutil
import configparser import configparser
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
## Config.ini configuration ## Config.ini configuration
# Unit: FPS. Please fill in according to the actual dataset frame rate, # Unit: FPS. Please fill in according to the actual dataset frame rate,
# otherwise it will cause inaccurate matching. # otherwise it will cause inaccurate matching.
frameRate = -1 frameRate = -1
# Unit: ms. When the timestamp range difference of a matched frame group # Unit: ms. When the timestamp range difference of a matched frame group
# is greater than or equal to tspRangeThreshold, the file name will be marked. # is greater than or equal to tspRangeThreshold, the file name will be marked.
tspRangeThreshold = -1 tspRangeThreshold = -1
## Parsed automatically by Python script, DO NOT modify ## Parsed automatically by Python script, DO NOT modify
primaryId = '-1' # Host ID primaryId = '-1' # Host ID
mainPythonWorkPath = "" # Working directory of SyncFramesMain.py mainPythonWorkPath = "" # Working directory of SyncFramesMain.py
sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory
sourceRootPath = "" # Root directory containing TotalMode sourceRootPath = "" # Root directory containing TotalMode
resultPath = "" # Path to save sync analysis results resultPath = "" # Path to save sync analysis results
streamProfileDict = dict() # Store Python results streamProfileDict = dict() # Store Python results
class PictureInfo(object): class PictureInfo(object):
class Struct(object): class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId self.deviceId = deviceId
self.sensorType = sensorType self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath self.picturePath = picturePath
self.deviceIdFull = deviceIdFull self.deviceIdFull = deviceIdFull
self.fileExt = fileExt self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt) return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo: class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps): def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType self.sensorType = sensorType
self.width = width self.width = width
self.height = height self.height = height
self.format = format self.format = format
self.fps = fps self.fps = fps
def initPrimaryId(): def initPrimaryId():
global primaryId global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists") print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False return False
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'") print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False return False
for item in data['devices']: for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']: if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index']) primaryId = str(item['index'])
return True return True
return False return False
def initSyncConfigParams(): def initSyncConfigParams():
global frameRate, tspRangeThreshold global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini" configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath): if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.") print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False return False
# Create ConfigParser object # Create ConfigParser object
config = configparser.ConfigParser() config = configparser.ConfigParser()
# Read ini file # Read ini file
config.read(configPath, 'utf-8') config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'): if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False return False
if not config.has_option('Parameter', 'tspRangeThreshold'): if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False return False
frameRate = config.getfloat('Parameter', 'frameRate') frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000: if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}") print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold') tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0: if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}") print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False return False
return True return True
def getDeviceCount(): def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists") print(f"Get device count failed. {deviceInfoPath} not exists")
return 0 return 0
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'") print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0 return 0
return len(data['devices']) return len(data['devices'])
def initProfileInfoDict(): def initProfileInfoDict():
global streamProfileDict global streamProfileDict
streamProfileDict = dict() streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt" profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath): if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists") print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False return False
with open(profilePath, 'r') as f: with open(profilePath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'streamProfiles' in data: if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'") print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False return False
for item in data['streamProfiles']: for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item: if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps']) profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo) streamProfileDict.setdefault(item['sensorType'], profileInfo)
else: else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}") print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0 return len(streamProfileDict.items()) > 0
def initResultDir(): def initResultDir():
global resultPath global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S") timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}" resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath): if not os.path.exists(resultPath):
os.makedirs(resultPath) os.makedirs(resultPath)
def isFrameFile(fileName): def isFrameFile(fileName):
fileExt = os.path.splitext(fileName) fileExt = os.path.splitext(fileName)
if len(fileExt) < 2: if len(fileExt) < 2:
return False return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"} frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts return fileExt[1] in frameExts
def initPictureInfoDictionary(rootFilePath, pictureInfoDict): def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0 frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath): for dirpath, dirnames, filenames in os.walk(rootFilePath):
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath)) # print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
for fileName in filenames: for fileName in filenames:
filePath = os.path.join(dirpath, fileName) filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName): if not isFrameFile(fileName):
continue continue
frameFileCount += 1 frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0] fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_') pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:] deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_') sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:]) syncTimeStamp = int(pictureNameList[3][1:])
# Create struct # Create struct
pictureInfo = PictureInfo() pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1]) info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# Format dictionary: one key corresponds to one array # Format dictionary: one key corresponds to one array
pictureInfoDict.setdefault(deviceId, []).append(info) pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount: if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file") print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict): def matchFrame(pictureInfoDict):
global primaryId, frameRate global primaryId, frameRate
global streamProfileDict global streamProfileDict
# Get comparison image array, take the Color array of the first device for comparison # Get comparison image array, take the Color array of the first device for comparison
compareList = [] compareList = []
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color': if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info) compareList.append(info)
sorted_dict = dict(sorted(pictureInfoDict.items(), key=lambda x: x[1], reverse=False)) sorted_dict = dict(sorted(pictureInfoDict.items(), key=lambda x: x[1], reverse=False))
pictureInfoDict = sorted_dict pictureInfoDict = sorted_dict
# Dynamically analyze based on stream opening status to reduce loop iterations # Dynamically analyze based on stream opening status to reduce loop iterations
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
# Match adjacent frames # Match adjacent frames
resultDict = {} resultDict = {}
consumedList = [] consumedList = []
dictIndex = 0 dictIndex = 0
frameTspGap = int(1000.0/frameRate+0.5) frameTspGap = int(1000.0/frameRate+0.5)
for comparePic in compareList: for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic) resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
# Compare Color # Compare Color
if hasColorProfile: if hasColorProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'color' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap: if info.sensorType == 'color' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare Depth # Compare Depth
if hasDepthProfile: if hasDepthProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'depth' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap: if info.sensorType == 'depth' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare IR # Compare IR
if hasIRProfile: if hasIRProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap: if info.sensorType == 'ir' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Calculate deviation # Calculate deviation
for info in resultDict[dictIndex]: for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - comparePic.syncTimeStamp info.tspDiff = info.syncTimeStamp - comparePic.syncTimeStamp
dictIndex += 1 dictIndex += 1
return resultDict return resultDict
def safe_make_dir(path): def safe_make_dir(path):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
def handleSyncFrames(): def handleSyncFrames():
global resultPath global resultPath
global sourceFramesPath global sourceFramesPath
global streamProfileDict global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w") fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict(): if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit") print("Initialize stream profile dictionary failed. exit")
return return
print(f"Stream profiles: {streamProfileDict}") print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items()) streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {} pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict) initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0: if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0") print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return return
# # Dump pictureInfoDict # # Dump pictureInfoDict
# for key in pictureInfoDict: # for key in pictureInfoDict:
# print("===================key:%s" % key) # print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key]) # listTmp = list(pictureInfoDict[key])
# for info in listTmp: # for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath)) # print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount() deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount) print("=================device count:%d" % deviceCount)
resultDict = {} resultDict = {}
resultDict = matchFrame(pictureInfoDict) resultDict = matchFrame(pictureInfoDict)
for key in resultDict: for key in resultDict:
fp.write("===================result key:%s\n" % key) fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key) print("===================result key:%s" % key)
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
for info in listTmp: for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % ( fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % ( print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/" matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath) safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/" notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/" abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict: for key in resultDict:
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)): if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False haveAbnormalData = False
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
haveAbnormalData = True haveAbnormalData = True
else: else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
if haveAbnormalData: if haveAbnormalData:
safe_make_dir(abnormalPath) safe_make_dir(abnormalPath)
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
safe_make_dir(notMatchPath) safe_make_dir(notMatchPath)
for info in listTmp: for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath) shutil.copy(info.picturePath, newFilePath)
def main(): def main():
if not initPrimaryId(): if not initPrimaryId():
print("Initialize primaryId failed. exit.") print("Initialize primaryId failed. exit.")
return return
if not initSyncConfigParams(): if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.") print("Initialized Sync config parameter failed. exit.")
return return
initResultDir() initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}") print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames() handleSyncFrames()
return return
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Astra2'") parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Astra2'")
parser.add_argument('frames_dir', type=str, help="Frames directory") parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory") parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args() args = parser.parse_args()
sourceRootPath = args.frames_dir sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames" sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir mainPythonWorkPath = args.main_script_root_dir
print(f"Start of Astra2 synchronize frame. sourceRootPath={sourceRootPath}") print(f"Start of Astra2 synchronize frame. sourceRootPath={sourceRootPath}")
main() main()
print("Finish of Astra2 synchronize frame.") print("Finish of Astra2 synchronize frame.")
@@ -1,403 +1,403 @@
import os.path import os.path
import argparse import argparse
import sys import sys
import json import json
import shutil import shutil
import configparser import configparser
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
## Config.ini configuration ## Config.ini configuration
# Unit: FPS. Please fill in according to the actual dataset frame rate, # Unit: FPS. Please fill in according to the actual dataset frame rate,
# otherwise it will cause inaccurate matching. # otherwise it will cause inaccurate matching.
frameRate = -1 frameRate = -1
# Unit: ms. When the timestamp range difference of a matched frame group # Unit: ms. When the timestamp range difference of a matched frame group
# is greater than or equal to tspRangeThreshold, the file name will be marked. # is greater than or equal to tspRangeThreshold, the file name will be marked.
tspRangeThreshold = -1 tspRangeThreshold = -1
## Parsed automatically by Python script, DO NOT modify ## Parsed automatically by Python script, DO NOT modify
primaryId = '-1' # Host ID primaryId = '-1' # Host ID
mainPythonWorkPath = "" # Working directory of SyncFramesMain.py mainPythonWorkPath = "" # Working directory of SyncFramesMain.py
sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory
sourceRootPath = "" # Root directory containing TotalMode sourceRootPath = "" # Root directory containing TotalMode
resultPath = "" # Path to save sync analysis results resultPath = "" # Path to save sync analysis results
streamProfileDict = dict() # Store Python results streamProfileDict = dict() # Store Python results
class PictureInfo(object): class PictureInfo(object):
class Struct(object): class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId self.deviceId = deviceId
self.sensorType = sensorType self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath self.picturePath = picturePath
self.deviceIdFull = deviceIdFull self.deviceIdFull = deviceIdFull
self.fileExt = fileExt self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt) return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo: class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps): def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType self.sensorType = sensorType
self.width = width self.width = width
self.height = height self.height = height
self.format = format self.format = format
self.fps = fps self.fps = fps
def initPrimaryId(): def initPrimaryId():
global primaryId global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists") print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False return False
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'") print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False return False
for item in data['devices']: for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']: if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index']) primaryId = str(item['index'])
return True return True
return False return False
def initSyncConfigParams(): def initSyncConfigParams():
global frameRate, tspRangeThreshold global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini" configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath): if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.") print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False return False
# Create ConfigParser object # Create ConfigParser object
config = configparser.ConfigParser() config = configparser.ConfigParser()
# Read ini file # Read ini file
config.read(configPath, 'utf-8') config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'): if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False return False
if not config.has_option('Parameter', 'tspRangeThreshold'): if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False return False
frameRate = config.getfloat('Parameter', 'frameRate') frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000: if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}") print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold') tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0: if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}") print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False return False
return True return True
def getDeviceCount(): def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists") print(f"Get device count failed. {deviceInfoPath} not exists")
return 0 return 0
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'") print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0 return 0
return len(data['devices']) return len(data['devices'])
def initProfileInfoDict(): def initProfileInfoDict():
global streamProfileDict global streamProfileDict
streamProfileDict = dict() streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt" profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath): if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists") print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False return False
with open(profilePath, 'r') as f: with open(profilePath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'streamProfiles' in data: if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'") print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False return False
for item in data['streamProfiles']: for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item: if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps']) profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo) streamProfileDict.setdefault(item['sensorType'], profileInfo)
else: else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}") print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0 return len(streamProfileDict.items()) > 0
def initResultDir(): def initResultDir():
global resultPath global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S") timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}" resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath): if not os.path.exists(resultPath):
os.makedirs(resultPath) os.makedirs(resultPath)
def isFrameFile(fileName): def isFrameFile(fileName):
fileExt = os.path.splitext(fileName) fileExt = os.path.splitext(fileName)
if len(fileExt) < 2: if len(fileExt) < 2:
return False return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"} frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts return fileExt[1] in frameExts
def initPictureInfoDictionary(rootFilePath, pictureInfoDict): def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0 frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath): for dirpath, dirnames, filenames in os.walk(rootFilePath):
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath)) # print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
for fileName in filenames: for fileName in filenames:
filePath = os.path.join(dirpath, fileName) filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName): if not isFrameFile(fileName):
continue continue
frameFileCount += 1 frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0] fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_') pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:] deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_') sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[5][1:]) syncTimeStamp = int(pictureNameList[5][1:])
# Create struct # Create struct
pictureInfo = PictureInfo() pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1]) info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# Format dictionary: one key corresponds to one array # Format dictionary: one key corresponds to one array
pictureInfoDict.setdefault(deviceId, []).append(info) pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount: if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file") print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict): def matchFrame(pictureInfoDict):
global primaryId, frameRate global primaryId, frameRate
global streamProfileDict global streamProfileDict
# Get comparison image array, take the Color array of the first device for comparison # Get comparison image array, take the Color array of the first device for comparison
compareList = [] compareList = []
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color': if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info) compareList.append(info)
# Dynamically analyze based on stream opening status to reduce loop iterations # Dynamically analyze based on stream opening status to reduce loop iterations
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# Match adjacent frames # Match adjacent frames
resultDict = {} resultDict = {}
consumedList = [] consumedList = []
dictIndex = 0 dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5) frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList: for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic) resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
# Compare Color # Compare Color
if hasColorProfile: if hasColorProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare Depth # Compare Depth
if hasDepthProfile: if hasDepthProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# IR # IR
if hasIRProfile: if hasIRProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Left IR # Left IR
if hasIRLeftProfile: if hasIRLeftProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Right IR # Right IR
if hasIRRightProfile: if hasIRRightProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Calculate deviation # Calculate deviation
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp) minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]: for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1 dictIndex += 1
return resultDict return resultDict
def safe_make_dir(path): def safe_make_dir(path):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
def handleSyncFrames(): def handleSyncFrames():
global resultPath global resultPath
global sourceFramesPath global sourceFramesPath
global streamProfileDict global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w") fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict(): if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit") print("Initialize stream profile dictionary failed. exit")
return return
print(f"Stream profiles: {streamProfileDict}") print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items()) streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {} pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict) initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0: if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0") print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return return
deviceCount = getDeviceCount() deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount) print("=================device count:%d" % deviceCount)
resultDict = {} resultDict = {}
resultDict = matchFrame(pictureInfoDict) resultDict = matchFrame(pictureInfoDict)
for key in resultDict: for key in resultDict:
fp.write("===================result key:%s\n" % key) fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key) print("===================result key:%s" % key)
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
for info in listTmp: for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % ( fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % ( print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/" matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath) safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/" notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/" abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict: for key in resultDict:
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)): if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False haveAbnormalData = False
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
haveAbnormalData = True haveAbnormalData = True
else: else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
if haveAbnormalData: if haveAbnormalData:
safe_make_dir(abnormalPath) safe_make_dir(abnormalPath)
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
safe_make_dir(notMatchPath) safe_make_dir(notMatchPath)
for info in listTmp: for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath) shutil.copy(info.picturePath, newFilePath)
def main(): def main():
if not initPrimaryId(): if not initPrimaryId():
print("Initialize primaryId failed. exit.") print("Initialize primaryId failed. exit.")
return return
if not initSyncConfigParams(): if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.") print("Initialized Sync config parameter failed. exit.")
return return
initResultDir() initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}") print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames() handleSyncFrames()
return return
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2'") parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2'")
parser.add_argument('frames_dir', type=str, help="Frames directory") parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory") parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args() args = parser.parse_args()
sourceRootPath = args.frames_dir sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames" sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir mainPythonWorkPath = args.main_script_root_dir
print(f"Start of Gemini2 synchronize frame. sourceRootPath={sourceRootPath}") print(f"Start of Gemini2 synchronize frame. sourceRootPath={sourceRootPath}")
main() main()
print("Finish of Gemini2 synchronize frame.") print("Finish of Gemini2 synchronize frame.")
@@ -1,425 +1,425 @@
import os.path import os.path
import argparse import argparse
import sys import sys
import json import json
import shutil import shutil
import configparser import configparser
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
import os import os
import re import re
## Config.ini configuration ## Config.ini configuration
# Unit: FPS. Please fill in according to the actual dataset frame rate, # Unit: FPS. Please fill in according to the actual dataset frame rate,
# otherwise it will cause inaccurate matching. # otherwise it will cause inaccurate matching.
frameRate = -1 frameRate = -1
# Unit: ms. When the timestamp range difference of a matched frame group # Unit: ms. When the timestamp range difference of a matched frame group
# is greater than or equal to tspRangeThreshold, the file name will be marked. # is greater than or equal to tspRangeThreshold, the file name will be marked.
tspRangeThreshold = -1 tspRangeThreshold = -1
## Parsed automatically by Python script, DO NOT modify ## Parsed automatically by Python script, DO NOT modify
primaryId = '-1' # Host ID primaryId = '-1' # Host ID
mainPythonWorkPath = "" # Working directory of SyncFramesMain.py mainPythonWorkPath = "" # Working directory of SyncFramesMain.py
sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory
sourceRootPath = "" # Root directory containing TotalMode sourceRootPath = "" # Root directory containing TotalMode
resultPath = "" # Path to save sync analysis results resultPath = "" # Path to save sync analysis results
streamProfileDict = dict() # Store Python results streamProfileDict = dict() # Store Python results
class PictureInfo(object): class PictureInfo(object):
class Struct(object): class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId self.deviceId = deviceId
self.sensorType = sensorType self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath self.picturePath = picturePath
self.deviceIdFull = deviceIdFull self.deviceIdFull = deviceIdFull
self.fileExt = fileExt self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt) return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo: class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps): def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType self.sensorType = sensorType
self.width = width self.width = width
self.height = height self.height = height
self.format = format self.format = format
self.fps = fps self.fps = fps
def initPrimaryId(): def initPrimaryId():
global primaryId global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists") print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False return False
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'") print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False return False
for item in data['devices']: for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']: if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index']) primaryId = str(item['index'])
return True return True
return False return False
def initSyncConfigParams(): def initSyncConfigParams():
global frameRate, tspRangeThreshold global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini" configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath): if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.") print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False return False
# Create ConfigParser object # Create ConfigParser object
config = configparser.ConfigParser() config = configparser.ConfigParser()
# Read ini file # Read ini file
config.read(configPath, 'utf-8') config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'): if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False return False
if not config.has_option('Parameter', 'tspRangeThreshold'): if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False return False
frameRate = config.getfloat('Parameter', 'frameRate') frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000: if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}") print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold') tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0: if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}") print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False return False
return True return True
def getDeviceCount(): def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists") print(f"Get device count failed. {deviceInfoPath} not exists")
return 0 return 0
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'") print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0 return 0
return len(data['devices']) return len(data['devices'])
def initProfileInfoDict(): def initProfileInfoDict():
global streamProfileDict global streamProfileDict
streamProfileDict = dict() streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt" profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath): if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists") print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False return False
with open(profilePath, 'r') as f: with open(profilePath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'streamProfiles' in data: if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'") print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False return False
for item in data['streamProfiles']: for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item: if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps']) profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo) streamProfileDict.setdefault(item['sensorType'], profileInfo)
else: else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}") print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0 return len(streamProfileDict.items()) > 0
def initResultDir(): def initResultDir():
global resultPath global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S") timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}" resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath): if not os.path.exists(resultPath):
os.makedirs(resultPath) os.makedirs(resultPath)
def isFrameFile(fileName): def isFrameFile(fileName):
fileExt = os.path.splitext(fileName) fileExt = os.path.splitext(fileName)
if len(fileExt) < 2: if len(fileExt) < 2:
return False return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"} frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts return fileExt[1] in frameExts
def extract_number(filename): def extract_number(filename):
"""Extract the numeric part after 'g' in the filename""" """Extract the numeric part after 'g' in the filename"""
match_g = re.search(r'g(\d+)', filename) match_g = re.search(r'g(\d+)', filename)
if match_g: if match_g:
return int(match_g.group(1)) return int(match_g.group(1))
"""Extract the numeric part after 'd' in the filename""" """Extract the numeric part after 'd' in the filename"""
match_d = re.search(r'd(\d+)', filename) match_d = re.search(r'd(\d+)', filename)
if match_d: if match_d:
return int(match_d.group(1)) return int(match_d.group(1))
return float('inf') return float('inf')
def initPictureInfoDictionary(rootFilePath, pictureInfoDict): def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0 frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath): for dirpath, dirnames, filenames in os.walk(rootFilePath):
filenames.sort(key=extract_number) # Sort filenames by the numeric part after 'd' and 'g' filenames.sort(key=extract_number) # Sort filenames by the numeric part after 'd' and 'g'
for fileName in filenames: for fileName in filenames:
filePath = os.path.join(dirpath, fileName) filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName): if not isFrameFile(fileName):
continue continue
frameFileCount += 1 frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0] fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_') pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:] deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_') sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:]) syncTimeStamp = int(pictureNameList[3][1:])
# Create struct # Create struct
pictureInfo = PictureInfo() pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1]) info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# Format dictionary: one key corresponds to one array # Format dictionary: one key corresponds to one array
pictureInfoDict.setdefault(deviceId, []).append(info) pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount: if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file") print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict): def matchFrame(pictureInfoDict):
global primaryId, frameRate global primaryId, frameRate
global streamProfileDict global streamProfileDict
# Get comparison image array, use the Color array of the first device for comparison # Get comparison image array, use the Color array of the first device for comparison
compareList = [] compareList = []
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color': if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info) compareList.append(info)
# Dynamically analyze based on stream opening status to reduce loop iterations # Dynamically analyze based on stream opening status to reduce loop iterations
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# Match adjacent frames # Match adjacent frames
resultDict = {} resultDict = {}
consumedList = [] consumedList = []
dictIndex = 0 dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5) frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList: for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic) resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
# Compare color # Compare color
if hasColorProfile: if hasColorProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare depth # Compare depth
if hasDepthProfile: if hasDepthProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare IR # Compare IR
if hasIRProfile: if hasIRProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare left IR # Compare left IR
if hasIRLeftProfile: if hasIRLeftProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare right IR # Compare right IR
if hasIRRightProfile: if hasIRRightProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Calculate deviation # Calculate deviation
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp) minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]: for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1 dictIndex += 1
return resultDict return resultDict
def safe_make_dir(path): def safe_make_dir(path):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
def handleSyncFrames(): def handleSyncFrames():
global resultPath global resultPath
global sourceFramesPath global sourceFramesPath
global streamProfileDict global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w") fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict(): if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit") print("Initialize stream profile dictionary failed. exit")
return return
print(f"Stream profiles: {streamProfileDict}") print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items()) streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {} pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict) initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0: if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0") print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return return
# # Dump pictureInfoDict # # Dump pictureInfoDict
# for key in pictureInfoDict: # for key in pictureInfoDict:
# print("===================key:%s" % key) # print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key]) # listTmp = list(pictureInfoDict[key])
# for info in listTmp: # for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath)) # print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount() deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount) print("=================device count:%d" % deviceCount)
resultDict = {} resultDict = {}
resultDict = matchFrame(pictureInfoDict) resultDict = matchFrame(pictureInfoDict)
for key in resultDict: for key in resultDict:
fp.write("===================result key:%s\n" % key) fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key) print("===================result key:%s" % key)
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
for info in listTmp: for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % ( fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % ( print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/" matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath) safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/" notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/" abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict: for key in resultDict:
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)): if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False haveAbnormalData = False
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
haveAbnormalData = True haveAbnormalData = True
else: else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
if haveAbnormalData: if haveAbnormalData:
safe_make_dir(abnormalPath) safe_make_dir(abnormalPath)
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
safe_make_dir(notMatchPath) safe_make_dir(notMatchPath)
for info in listTmp: for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath) shutil.copy(info.picturePath, newFilePath)
def main(): def main():
if not initPrimaryId(): if not initPrimaryId():
print("Initialize primaryId failed. exit.") print("Initialize primaryId failed. exit.")
return return
if not initSyncConfigParams(): if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.") print("Initialized Sync config parameter failed. exit.")
return return
initResultDir() initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}") print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames() handleSyncFrames()
return return
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'") parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'")
parser.add_argument('frames_dir', type=str, help="Frames directory") parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory") parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args() args = parser.parse_args()
sourceRootPath = args.frames_dir sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames" sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir mainPythonWorkPath = args.main_script_root_dir
print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}") print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}")
main() main()
print("Finish of CommonFrameMatch synchronize frame.") print("Finish of CommonFrameMatch synchronize frame.")
@@ -1,410 +1,410 @@
import os.path import os.path
import argparse import argparse
import sys import sys
import json import json
import shutil import shutil
import configparser import configparser
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
## Config.ini configuration ## Config.ini configuration
# Unit: FPS. Please fill in according to the actual dataset frame rate, # Unit: FPS. Please fill in according to the actual dataset frame rate,
# otherwise it will cause inaccurate matching. # otherwise it will cause inaccurate matching.
frameRate = -1 frameRate = -1
# Unit: ms. When the timestamp range difference of a matched frame group # Unit: ms. When the timestamp range difference of a matched frame group
# is greater than or equal to tspRangeThreshold, the file name will be marked. # is greater than or equal to tspRangeThreshold, the file name will be marked.
tspRangeThreshold = -1 tspRangeThreshold = -1
## Parsed automatically by Python script, DO NOT modify ## Parsed automatically by Python script, DO NOT modify
primaryId = '-1' # Host ID primaryId = '-1' # Host ID
mainPythonWorkPath = "" # Working directory of SyncFramesMain.py mainPythonWorkPath = "" # Working directory of SyncFramesMain.py
sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory sourceFramesPath = "" # DO NOT modify, fixed value, path to TotalMode directory
sourceRootPath = "" # Root directory containing TotalMode sourceRootPath = "" # Root directory containing TotalMode
resultPath = "" # Path to save sync analysis results resultPath = "" # Path to save sync analysis results
streamProfileDict = dict() # Store Python results streamProfileDict = dict() # Store Python results
class PictureInfo(object): class PictureInfo(object):
class Struct(object): class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId self.deviceId = deviceId
self.sensorType = sensorType self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath self.picturePath = picturePath
self.deviceIdFull = deviceIdFull self.deviceIdFull = deviceIdFull
self.fileExt = fileExt self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt): def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt) return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo: class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps): def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType self.sensorType = sensorType
self.width = width self.width = width
self.height = height self.height = height
self.format = format self.format = format
self.fps = fps self.fps = fps
def initPrimaryId(): def initPrimaryId():
global primaryId global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists") print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False return False
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'") print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False return False
for item in data['devices']: for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']: if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index']) primaryId = str(item['index'])
return True return True
return False return False
def initSyncConfigParams(): def initSyncConfigParams():
global frameRate, tspRangeThreshold global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini" configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath): if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.") print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False return False
# Create ConfigParser object # Create ConfigParser object
config = configparser.ConfigParser() config = configparser.ConfigParser()
# Read ini file # Read ini file
config.read(configPath, 'utf-8') config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'): if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False return False
if not config.has_option('Parameter', 'tspRangeThreshold'): if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False return False
frameRate = config.getfloat('Parameter', 'frameRate') frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000: if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}") print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold') tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0: if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}") print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False return False
return True return True
def getDeviceCount(): def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists") print(f"Get device count failed. {deviceInfoPath} not exists")
return 0 return 0
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'") print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0 return 0
return len(data['devices']) return len(data['devices'])
def initProfileInfoDict(): def initProfileInfoDict():
global streamProfileDict global streamProfileDict
streamProfileDict = dict() streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt" profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath): if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists") print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False return False
with open(profilePath, 'r') as f: with open(profilePath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'streamProfiles' in data: if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'") print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False return False
for item in data['streamProfiles']: for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item: if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps']) profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo) streamProfileDict.setdefault(item['sensorType'], profileInfo)
else: else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}") print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0 return len(streamProfileDict.items()) > 0
def initResultDir(): def initResultDir():
global resultPath global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S") timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}" resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath): if not os.path.exists(resultPath):
os.makedirs(resultPath) os.makedirs(resultPath)
def isFrameFile(fileName): def isFrameFile(fileName):
fileExt = os.path.splitext(fileName) fileExt = os.path.splitext(fileName)
if len(fileExt) < 2: if len(fileExt) < 2:
return False return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"} frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts return fileExt[1] in frameExts
def initPictureInfoDictionary(rootFilePath, pictureInfoDict): def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0 frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath): for dirpath, dirnames, filenames in os.walk(rootFilePath):
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath)) # print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
for fileName in filenames: for fileName in filenames:
filePath = os.path.join(dirpath, fileName) filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName): if not isFrameFile(fileName):
continue continue
frameFileCount += 1 frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0] fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_') pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:] deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_') sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:]) syncTimeStamp = int(pictureNameList[3][1:])
# Create structure # Create structure
pictureInfo = PictureInfo() pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1]) info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# Format dictionary: one key corresponds to one array # Format dictionary: one key corresponds to one array
pictureInfoDict.setdefault(deviceId, []).append(info) pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount: if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file") print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict): def matchFrame(pictureInfoDict):
global primaryId, frameRate global primaryId, frameRate
global streamProfileDict global streamProfileDict
# Get comparison image array, take the Color array of the first device for comparison # Get comparison image array, take the Color array of the first device for comparison
compareList = [] compareList = []
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color': if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info) compareList.append(info)
# Dynamically analyze based on stream status to reduce loop iterations # Dynamically analyze based on stream status to reduce loop iterations
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# Match adjacent frames # Match adjacent frames
resultDict = {} resultDict = {}
consumedList = [] consumedList = []
dictIndex = 0 dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5) frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList: for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic) resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
# Compare color # Compare color
if hasColorProfile: if hasColorProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare depth # Compare depth
if hasDepthProfile: if hasDepthProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare IR # Compare IR
if hasIRProfile: if hasIRProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare left IR # Compare left IR
if hasIRLeftProfile: if hasIRLeftProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare right IR # Compare right IR
if hasIRRightProfile: if hasIRRightProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Calculate deviation # Calculate deviation
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp) minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]: for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1 dictIndex += 1
return resultDict return resultDict
def safe_make_dir(path): def safe_make_dir(path):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
def handleSyncFrames(): def handleSyncFrames():
global resultPath global resultPath
global sourceFramesPath global sourceFramesPath
global streamProfileDict global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w") fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict(): if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit") print("Initialize stream profile dictionary failed. exit")
return return
print(f"Stream profiles: {streamProfileDict}") print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items()) streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {} pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict) initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0: if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0") print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return return
# # Dump pictureInfoDict # # Dump pictureInfoDict
# for key in pictureInfoDict: # for key in pictureInfoDict:
# print("===================key:%s" % key) # print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key]) # listTmp = list(pictureInfoDict[key])
# for info in listTmp: # for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath)) # print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount() deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount) print("=================device count:%d" % deviceCount)
resultDict = {} resultDict = {}
resultDict = matchFrame(pictureInfoDict) resultDict = matchFrame(pictureInfoDict)
for key in resultDict: for key in resultDict:
fp.write("===================result key:%s\n" % key) fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key) print("===================result key:%s" % key)
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
for info in listTmp: for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % ( fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % ( print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/" matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath) safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/" notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/" abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict: for key in resultDict:
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)): if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False haveAbnormalData = False
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
haveAbnormalData = True haveAbnormalData = True
else: else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
if haveAbnormalData: if haveAbnormalData:
safe_make_dir(abnormalPath) safe_make_dir(abnormalPath)
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
safe_make_dir(notMatchPath) safe_make_dir(notMatchPath)
for info in listTmp: for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath) shutil.copy(info.picturePath, newFilePath)
def main(): def main():
if not initPrimaryId(): if not initPrimaryId():
print("Initialize primaryId failed. exit.") print("Initialize primaryId failed. exit.")
return return
if not initSyncConfigParams(): if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.") print("Initialized Sync config parameter failed. exit.")
return return
initResultDir() initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}") print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames() handleSyncFrames()
return return
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2 VL'") parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2 VL'")
parser.add_argument('frames_dir', type=str, help="Frames directory") parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory") parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args() args = parser.parse_args()
sourceRootPath = args.frames_dir sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames" sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir mainPythonWorkPath = args.main_script_root_dir
print(f"Start of Gemini2VL synchronize frame. sourceRootPath={sourceRootPath}") print(f"Start of Gemini2VL synchronize frame. sourceRootPath={sourceRootPath}")
main() main()
print("Finish of Gemini2VL synchronize frame.") print("Finish of Gemini2VL synchronize frame.")
@@ -1,426 +1,426 @@
import os.path import os.path
import argparse import argparse
import sys import sys
import json import json
import shutil import shutil
import configparser import configparser
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
import os import os
import re import re
## Config.ini configuration ## Config.ini configuration
# Unit: FPS. Please fill in according to the actual frame rate of the dataset, otherwise the matching will be inaccurate. # Unit: FPS. Please fill in according to the actual frame rate of the dataset, otherwise the matching will be inaccurate.
frameRate = -1 frameRate = -1
# Unit: ms. When the timestamp range of a matched group of data frames is greater than or equal to tspRangeThreshold, the filename will be marked. # Unit: ms. When the timestamp range of a matched group of data frames is greater than or equal to tspRangeThreshold, the filename will be marked.
tspRangeThreshold = -1 tspRangeThreshold = -1
## Automatically parsed by Python script, do not modify ## Automatically parsed by Python script, do not modify
primaryId = '-1' # Primary device ID primaryId = '-1' # Primary device ID
mainPythonWorkPath = "" # Working directory of SyncFramesMain.py mainPythonWorkPath = "" # Working directory of SyncFramesMain.py
sourceFramesPath = "" # Do not modify, fixed value, path to TotalMode directory sourceFramesPath = "" # Do not modify, fixed value, path to TotalMode directory
sourceRootPath = "" # Root directory where TotalMode is stored sourceRootPath = "" # Root directory where TotalMode is stored
resultPath = "" # Save sync analysis results resultPath = "" # Save sync analysis results
streamProfileDict = dict() # Save Python results streamProfileDict = dict() # Save Python results
class PictureInfo(object): class PictureInfo(object):
class Struct(object): class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain): def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain):
self.deviceId = deviceId self.deviceId = deviceId
self.sensorType = sensorType self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath self.picturePath = picturePath
self.deviceIdFull = deviceIdFull self.deviceIdFull = deviceIdFull
self.fileExt = fileExt self.fileExt = fileExt
self.exp = exp self.exp = exp
self.gain = gain self.gain = gain
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain): def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain) return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain)
class StreamProfileInfo: class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps): def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType self.sensorType = sensorType
self.width = width self.width = width
self.height = height self.height = height
self.format = format self.format = format
self.fps = fps self.fps = fps
def initPrimaryId(): def initPrimaryId():
global primaryId global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists") print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False return False
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'") print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False return False
for item in data['devices']: for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']: if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index']) primaryId = str(item['index'])
return True return True
return False return False
def initSyncConfigParams(): def initSyncConfigParams():
global frameRate, tspRangeThreshold global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini" configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath): if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.") print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False return False
# Create ConfigParser object # Create ConfigParser object
config = configparser.ConfigParser() config = configparser.ConfigParser()
# Read ini file # Read ini file
config.read(configPath, 'utf-8') config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'): if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False return False
if not config.has_option('Parameter', 'tspRangeThreshold'): if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}") print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False return False
frameRate = config.getfloat('Parameter', 'frameRate') frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000: if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}") print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold') tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0: if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}") print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False return False
return True return True
def getDeviceCount(): def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt" deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath): if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists") print(f"Get device count failed. {deviceInfoPath} not exists")
return 0 return 0
with open(deviceInfoPath, 'r') as f: with open(deviceInfoPath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'devices' in data: if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'") print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0 return 0
return len(data['devices']) return len(data['devices'])
def initProfileInfoDict(): def initProfileInfoDict():
global streamProfileDict global streamProfileDict
streamProfileDict = dict() streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt" profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath): if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists") print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False return False
with open(profilePath, 'r') as f: with open(profilePath, 'r') as f:
data = json.load(f) data = json.load(f)
if not 'streamProfiles' in data: if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'") print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False return False
for item in data['streamProfiles']: for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item: if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps']) profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo) streamProfileDict.setdefault(item['sensorType'], profileInfo)
else: else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}") print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0 return len(streamProfileDict.items()) > 0
def initResultDir(): def initResultDir():
global resultPath global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S") timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}" resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath): if not os.path.exists(resultPath):
os.makedirs(resultPath) os.makedirs(resultPath)
def isFrameFile(fileName): def isFrameFile(fileName):
fileExt = os.path.splitext(fileName) fileExt = os.path.splitext(fileName)
if len(fileExt) < 2: if len(fileExt) < 2:
return False return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"} frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts return fileExt[1] in frameExts
def extract_number(filename): def extract_number(filename):
match_d = re.search(r'_d(\d+)', filename) match_d = re.search(r'_d(\d+)', filename)
if match_d: if match_d:
return int(match_d.group(1)) return int(match_d.group(1))
match_g = re.search(r'_g(\d+)', filename) match_g = re.search(r'_g(\d+)', filename)
if match_g: if match_g:
return int(match_g.group(1)) return int(match_g.group(1))
return float('inf') return float('inf')
def initPictureInfoDictionary(rootFilePath, pictureInfoDict): def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0 frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath): for dirpath, dirnames, filenames in os.walk(rootFilePath):
filenames.sort(key=extract_number) # Sort files by numbers following '_d' and '_g' in filename, ascending filenames.sort(key=extract_number) # Sort files by numbers following '_d' and '_g' in filename, ascending
for fileName in filenames: for fileName in filenames:
filePath = os.path.join(dirpath, fileName) filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName): if not isFrameFile(fileName):
continue continue
frameFileCount += 1 frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0] fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_') pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:] deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_') sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:]) syncTimeStamp = int(pictureNameList[3][1:])
exp = str(pictureNameList[6][0:]) exp = str(pictureNameList[6][0:])
gain = str(pictureNameList[7][0:]) gain = str(pictureNameList[7][0:])
# Create structure # Create structure
pictureInfo = PictureInfo() pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1],exp,gain) info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1],exp,gain)
# Format dictionary: one key corresponds to one array # Format dictionary: one key corresponds to one array
pictureInfoDict.setdefault(deviceId, []).append(info) pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount: if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file") print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict): def matchFrame(pictureInfoDict):
global primaryId, frameRate global primaryId, frameRate
global streamProfileDict global streamProfileDict
# Get comparison image array, use Color array of the first device for comparison # Get comparison image array, use Color array of the first device for comparison
compareList = [] compareList = []
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color': if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info) compareList.append(info)
# Dynamically analyze according to stream situation to reduce loop count # Dynamically analyze according to stream situation to reduce loop count
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# Match adjacent frames # Match adjacent frames
resultDict = {} resultDict = {}
consumedList = [] consumedList = []
dictIndex = 0 dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5) frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList: for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic) resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict: for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key]) listTmp = list(pictureInfoDict[key])
# Compare color # Compare color
if hasColorProfile: if hasColorProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare depth # Compare depth
if hasDepthProfile: if hasDepthProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare IR # Compare IR
if hasIRProfile: if hasIRProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare IR Left # Compare IR Left
if hasIRLeftProfile: if hasIRLeftProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Compare IR Right # Compare IR Right
if hasIRRightProfile: if hasIRRightProfile:
for info in listTmp: for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType: if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue continue
if info in consumedList: if info in consumedList:
continue continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap: if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info) resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info) consumedList.append(info)
break break
# Calculate deviation # Calculate deviation
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp) minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]: for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1 dictIndex += 1
return resultDict return resultDict
def safe_make_dir(path): def safe_make_dir(path):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
def handleSyncFrames(): def handleSyncFrames():
global resultPath global resultPath
global sourceFramesPath global sourceFramesPath
global streamProfileDict global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w") fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict(): if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit") print("Initialize stream profile dictionary failed. exit")
return return
print(f"Stream profiles: {streamProfileDict}") print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items()) streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {} pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict) initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0: if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0") print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return return
# # Dump pictureInfoDict # # Dump pictureInfoDict
# for key in pictureInfoDict: # for key in pictureInfoDict:
# print("===================key:%s" % key) # print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key]) # listTmp = list(pictureInfoDict[key])
# for info in listTmp: # for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath)) # print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount() deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount) print("=================device count:%d" % deviceCount)
resultDict = {} resultDict = {}
resultDict = matchFrame(pictureInfoDict) resultDict = matchFrame(pictureInfoDict)
for key in resultDict: for key in resultDict:
fp.write("===================result key:%s\n" % key) fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key) print("===================result key:%s" % key)
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
for info in listTmp: for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % ( fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % ( print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath)) info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/" matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath) safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/" notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/" abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict: for key in resultDict:
listTmp = list(resultDict[key]) listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)): if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False haveAbnormalData = False
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
haveAbnormalData = True haveAbnormalData = True
else: else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
if haveAbnormalData: if haveAbnormalData:
safe_make_dir(abnormalPath) safe_make_dir(abnormalPath)
for info in listTmp: for info in listTmp:
if info.tspDiff >= tspRangeThreshold: if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path) shutil.copy(info.picturePath, path)
else: else:
safe_make_dir(notMatchPath) safe_make_dir(notMatchPath)
for info in listTmp: for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str( newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, newFilePath) shutil.copy(info.picturePath, newFilePath)
def main(): def main():
if not initPrimaryId(): if not initPrimaryId():
print("Initialize primaryId failed. exit.") print("Initialize primaryId failed. exit.")
return return
if not initSyncConfigParams(): if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.") print("Initialized Sync config parameter failed. exit.")
return return
initResultDir() initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}") print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames() handleSyncFrames()
return return
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'") parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'")
parser.add_argument('frames_dir', type=str, help="Frames directory") parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory") parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args() args = parser.parse_args()
sourceRootPath = args.frames_dir sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames" sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir mainPythonWorkPath = args.main_script_root_dir
print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}") print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}")
main() main()
print("Finish of CommonFrameMatch synchronize frame.") print("Finish of CommonFrameMatch synchronize frame.")
+7 -7
View File
@@ -1,7 +1,7 @@
std_msgs/Header header std_msgs/Header header
string name string name
string serial_number string serial_number
string firmware_version string firmware_version
string supported_min_sdk_version string supported_min_sdk_version
string current_sdk_version string current_sdk_version
string hardware_version string hardware_version
+2 -2
View File
@@ -1,2 +1,2 @@
--- ---
sensor_msgs/CameraInfo info sensor_msgs/CameraInfo info
+3 -3
View File
@@ -1,4 +1,4 @@
--- ---
DeviceInfo info DeviceInfo info
bool success bool success
string message string message
+416 -416
View File
@@ -1,418 +1,418 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- This URDF was automatically created by SolidWorks to URDF Exporter! Originally created by Stephen Brawner ([email protected]) <!-- This URDF was automatically created by SolidWorks to URDF Exporter! Originally created by Stephen Brawner ([email protected])
Commit Version: 1.6.0-4-g7f85cfe Build Version: 1.6.7995.38578 Commit Version: 1.6.0-4-g7f85cfe Build Version: 1.6.7995.38578
For more information, please see http://wiki.ros.org/sw_urdf_exporter --> For more information, please see http://wiki.ros.org/sw_urdf_exporter -->
<robot xmlns:xacro="http://ros.org/wiki/xacro" name="astra_2"> <robot xmlns:xacro="http://ros.org/wiki/xacro" name="astra_2">
<xacro:property name="mesh_path" value="package://orbbec_description/meshes/astra2/" /> <xacro:property name="mesh_path" value="package://orbbec_description/meshes/astra2/" />
<link <link
name="camera_link"> name="camera_link">
<inertial> <inertial>
<origin <origin
xyz="-0.0160938085585399 -0.00100885505248632 0.0279919581775268" xyz="-0.0160938085585399 -0.00100885505248632 0.0279919581775268"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.0101524380243699" /> value="0.0101524380243699" />
<inertia <inertia
ixx="1.69184979927567E-05" ixx="1.69184979927567E-05"
ixy="-8.18066547859061E-09" ixy="-8.18066547859061E-09"
ixz="-1.27426373196599E-09" ixz="-1.27426373196599E-09"
iyy="7.7096717121366E-07" iyy="7.7096717121366E-07"
iyz="1.08838945581987E-08" iyz="1.08838945581987E-08"
izz="1.61801971464778E-05" /> izz="1.61801971464778E-05" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="-0.012323995999904 -0.0374999999985815 -0.0281503197112" xyz="-0.012323995999904 -0.0374999999985815 -0.0281503197112"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}base_link.STL" /> filename="${mesh_path}base_link.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.827450980392157 0.698039215686274 0.490196078431373 1" /> rgba="0.827450980392157 0.698039215686274 0.490196078431373 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="-0.012323995999904 -0.0374999999985815 -0.0281503197112" xyz="-0.012323995999904 -0.0374999999985815 -0.0281503197112"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}base_link.STL" /> filename="${mesh_path}base_link.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<link <link
name="camera_bottom_screw_frame"> name="camera_bottom_screw_frame">
<inertial> <inertial>
<origin <origin
xyz="-0.00635589512085941 1.59480886486103E-08 0.0118534361269284" xyz="-0.00635589512085941 1.59480886486103E-08 0.0118534361269284"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.00583601107155577" /> value="0.00583601107155577" />
<inertia <inertia
ixx="3.65092579901689E-06" ixx="3.65092579901689E-06"
ixy="-5.00479838523244E-12" ixy="-5.00479838523244E-12"
ixz="7.68810468643615E-07" ixz="7.68810468643615E-07"
iyy="2.03966087595316E-06" iyy="2.03966087595316E-06"
iyz="6.18841137398479E-13" iyz="6.18841137398479E-13"
izz="3.65126403038314E-06" /> izz="3.65126403038314E-06" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_bottom_screw_frame.STL" /> filename="${mesh_path}camera_bottom_screw_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.266666666666667 0.266666666666667 0.266666666666667 1" /> rgba="0.266666666666667 0.266666666666667 0.266666666666667 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_bottom_screw_frame.STL" /> filename="${mesh_path}camera_bottom_screw_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_bottom_screw_joint" name="camera_bottom_screw_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="-0.012323995999904 -0.0374999999985815 -0.0281503197112" xyz="-0.012323995999904 -0.0374999999985815 -0.0281503197112"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_link" /> link="camera_link" />
<child <child
link="camera_bottom_screw_frame" /> link="camera_bottom_screw_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_ir_frame"> name="camera_ir_frame">
<inertial> <inertial>
<origin <origin
xyz="-0.00168460453866946 5.33451517362171E-07 6.08796009742363E-07" xyz="-0.00168460453866946 5.33451517362171E-07 6.08796009742363E-07"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000608348661221201" /> value="0.000608348661221201" />
<inertia <inertia
ixx="6.79778952271057E-09" ixx="6.79778952271057E-09"
ixy="8.05787556448484E-13" ixy="8.05787556448484E-13"
ixz="9.13275008053385E-13" ixz="9.13275008053385E-13"
iyy="8.07147396496025E-09" iyy="8.07147396496025E-09"
iyz="1.28152628023355E-12" iyz="1.28152628023355E-12"
izz="8.07148645937271E-09" /> izz="8.07148645937271E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_frame.STL" /> filename="${mesh_path}camera_infra1_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_frame.STL" /> filename="${mesh_path}camera_infra1_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_ir_joint" name="camera_ir_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_depth_frame" /> link="camera_depth_frame" />
<child <child
link="camera_ir_frame" /> link="camera_ir_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_ir_optical_frame"> name="camera_ir_optical_frame">
<inertial> <inertial>
<origin <origin
xyz="-5.33451517362171E-07 -6.0879600974216E-07 -0.00168460453866946" xyz="-5.33451517362171E-07 -6.0879600974216E-07 -0.00168460453866946"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000608348661221202" /> value="0.000608348661221202" />
<inertia <inertia
ixx="8.07147396496025E-09" ixx="8.07147396496025E-09"
ixy="1.28152628023355E-12" ixy="1.28152628023355E-12"
ixz="-8.0578755644897E-13" ixz="-8.0578755644897E-13"
iyy="8.07148645937271E-09" iyy="8.07148645937271E-09"
iyz="-9.13275008052949E-13" iyz="-9.13275008052949E-13"
izz="6.79778952271057E-09" /> izz="6.79778952271057E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_optical_frame.STL" /> filename="${mesh_path}camera_infra1_optical_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_optical_frame.STL" /> filename="${mesh_path}camera_infra1_optical_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_ir_optical_joint" name="camera_ir_optical_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="-1.5707963267949 0 -1.5707963267949" /> rpy="-1.5707963267949 0 -1.5707963267949" />
<parent <parent
link="camera_ir_frame" /> link="camera_ir_frame" />
<child <child
link="camera_ir_optical_frame" /> link="camera_ir_optical_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_color_frame"> name="camera_color_frame">
<inertial> <inertial>
<origin <origin
xyz="-0.0010912488631539 -4.73353451897057E-07 7.19396296308746E-07" xyz="-0.0010912488631539 -4.73353451897057E-07 7.19396296308746E-07"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="7.68929599151334E-05" /> value="7.68929599151334E-05" />
<inertia <inertia
ixx="3.55949372682833E-10" ixx="3.55949372682833E-10"
ixy="-6.02255513042918E-14" ixy="-6.02255513042918E-14"
ixz="-1.32613402096388E-13" ixz="-1.32613402096388E-13"
iyy="2.2724816811302E-10" iyy="2.2724816811302E-10"
iyz="-4.27828382910552E-14" iyz="-4.27828382910552E-14"
izz="2.2743913744705E-10" /> izz="2.2743913744705E-10" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_colorl_frame.STL" /> filename="${mesh_path}camera_colorl_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_colorl_frame.STL" /> filename="${mesh_path}camera_colorl_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_color_joint" name="camera_color_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0.0025420000310987234 -0.01850000023841858 0" xyz="0.0025420000310987234 -0.01850000023841858 0"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_depth_frame" /> link="camera_depth_frame" />
<child <child
link="camera_color_frame" /> link="camera_color_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_color_optical_frame"> name="camera_color_optical_frame">
<inertial> <inertial>
<origin <origin
xyz="4.73353451897057E-07 -7.19396296308746E-07 -0.0010912488631539" xyz="4.73353451897057E-07 -7.19396296308746E-07 -0.0010912488631539"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="7.68929599151334E-05" /> value="7.68929599151334E-05" />
<inertia <inertia
ixx="2.2724816811302E-10" ixx="2.2724816811302E-10"
ixy="-4.27828382910548E-14" ixy="-4.27828382910548E-14"
ixz="6.02255513042915E-14" ixz="6.02255513042915E-14"
iyy="2.2743913744705E-10" iyy="2.2743913744705E-10"
iyz="1.32613402096386E-13" iyz="1.32613402096386E-13"
izz="3.55949372682833E-10" /> izz="3.55949372682833E-10" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_color_optical_frame.STL" /> filename="${mesh_path}camera_color_optical_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_color_optical_frame.STL" /> filename="${mesh_path}camera_color_optical_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_color_optical_joint" name="camera_color_optical_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="-1.5707963267949 0 -1.5707963267949" /> rpy="-1.5707963267949 0 -1.5707963267949" />
<parent <parent
link="camera_color_frame" /> link="camera_color_frame" />
<child <child
link="camera_color_optical_frame" /> link="camera_color_optical_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_depth_frame"> name="camera_depth_frame">
<inertial> <inertial>
<origin <origin
xyz="-0.00168460453866946 5.33451517362171E-07 6.08796009742395E-07" xyz="-0.00168460453866946 5.33451517362171E-07 6.08796009742395E-07"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000608348661221201" /> value="0.000608348661221201" />
<inertia <inertia
ixx="6.79778952271057E-09" ixx="6.79778952271057E-09"
ixy="8.05787556448476E-13" ixy="8.05787556448476E-13"
ixz="9.13275008053388E-13" ixz="9.13275008053388E-13"
iyy="8.07147396496025E-09" iyy="8.07147396496025E-09"
iyz="1.28152628023353E-12" iyz="1.28152628023353E-12"
izz="8.07148645937271E-09" /> izz="8.07148645937271E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_depth_frame.STL" /> filename="${mesh_path}camera_depth_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_depth_frame.STL" /> filename="${mesh_path}camera_depth_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_depth_joint" name="camera_depth_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_link" /> link="camera_link" />
<child <child
link="camera_depth_frame" /> link="camera_depth_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_depth_optical_frame"> name="camera_depth_optical_frame">
<inertial> <inertial>
<origin <origin
xyz="-5.33451517362171E-07 -6.08796009742075E-07 -0.00168460453866946" xyz="-5.33451517362171E-07 -6.08796009742075E-07 -0.00168460453866946"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000608348661221202" /> value="0.000608348661221202" />
<inertia <inertia
ixx="8.07147396496025E-09" ixx="8.07147396496025E-09"
ixy="1.28152628023353E-12" ixy="1.28152628023353E-12"
ixz="-8.05787556448886E-13" ixz="-8.05787556448886E-13"
iyy="8.07148645937271E-09" iyy="8.07148645937271E-09"
iyz="-9.13275008052966E-13" iyz="-9.13275008052966E-13"
izz="6.79778952271057E-09" /> izz="6.79778952271057E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_depth_optical_frame.STL" /> filename="${mesh_path}camera_depth_optical_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_depth_optical_frame.STL" /> filename="${mesh_path}camera_depth_optical_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_depth_optical_joint" name="camera_depth_optical_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="-1.5707963267949 0 -1.5707963267949" /> rpy="-1.5707963267949 0 -1.5707963267949" />
<parent <parent
link="camera_depth_frame" /> link="camera_depth_frame" />
<child <child
link="camera_depth_optical_frame" /> link="camera_depth_optical_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
</robot> </robot>
+469 -469
View File
@@ -1,471 +1,471 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- This URDF was automatically created by SolidWorks to URDF Exporter! Originally created by Stephen Brawner ([email protected]) <!-- This URDF was automatically created by SolidWorks to URDF Exporter! Originally created by Stephen Brawner ([email protected])
Commit Version: 1.6.0-4-g7f85cfe Build Version: 1.6.7995.38578 Commit Version: 1.6.0-4-g7f85cfe Build Version: 1.6.7995.38578
For more information, please see http://wiki.ros.org/sw_urdf_exporter --> For more information, please see http://wiki.ros.org/sw_urdf_exporter -->
<robot xmlns:xacro="http://ros.org/wiki/xacro" name="gemini_2"> <robot xmlns:xacro="http://ros.org/wiki/xacro" name="gemini_2">
<xacro:property name="mesh_path" value="package://orbbec_description/meshes/gemini2/" /> <xacro:property name="mesh_path" value="package://orbbec_description/meshes/gemini2/" />
<link <link
name="camera_link"> name="camera_link">
<inertial> <inertial>
<origin <origin
xyz="0.0027714370634978 -9.29434319373592E-06 0.0128014057830239" xyz="0.0027714370634978 -9.29434319373592E-06 0.0128014057830239"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.0186201988654731" /> value="0.0186201988654731" />
<inertia <inertia
ixx="1.38952652762223E-05" ixx="1.38952652762223E-05"
ixy="-4.88105621547421E-09" ixy="-4.88105621547421E-09"
ixz="1.98670223779875E-08" ixz="1.98670223779875E-08"
iyy="1.7072236178685E-06" iyy="1.7072236178685E-06"
iyz="-8.49860443053716E-09" iyz="-8.49860443053716E-09"
izz="1.31795237727551E-05" /> izz="1.31795237727551E-05" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="-0.01491 -0.025 -0.0125" xyz="-0.01491 -0.025 -0.0125"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}base_link.STL" /> filename="${mesh_path}base_link.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="-0.01491 -0.025 -0.0125" xyz="-0.01491 -0.025 -0.0125"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}base_link.STL" /> filename="${mesh_path}base_link.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<link <link
name="camera_botter_screw_frame"> name="camera_botter_screw_frame">
<inertial> <inertial>
<origin <origin
xyz="0.00255175353418556 -0.00123907270798541 0.0118889783016768" xyz="0.00255175353418556 -0.00123907270798541 0.0118889783016768"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.00564464822744556" /> value="0.00564464822744556" />
<inertia <inertia
ixx="3.07688942133184E-06" ixx="3.07688942133184E-06"
ixy="-1.84968755859098E-08" ixy="-1.84968755859098E-08"
ixz="-9.2787127214088E-09" ixz="-9.2787127214088E-09"
iyy="2.27625032346061E-07" iyy="2.27625032346061E-07"
iyz="1.65176768024159E-08" iyz="1.65176768024159E-08"
izz="2.93984854119768E-06" /> izz="2.93984854119768E-06" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_botter_screw_frame.STL" /> filename="${mesh_path}camera_botter_screw_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_botter_screw_frame.STL" /> filename="${mesh_path}camera_botter_screw_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_botter_screw_joint" name="camera_botter_screw_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="-0.01491 -0.025 -0.0125" xyz="-0.01491 -0.025 -0.0125"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_link" /> link="camera_link" />
<child <child
link="camera_botter_screw_frame" /> link="camera_botter_screw_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_right_ir_frame"> name="camera_right_ir_frame">
<inertial> <inertial>
<origin <origin
xyz="-0.000620201436687004 -1.61358465547434E-08 -1.50984520398332E-07" xyz="-0.000620201436687004 -1.61358465547434E-08 -1.50984520398332E-07"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000453168343720438" /> value="0.000453168343720438" />
<inertia <inertia
ixx="4.34107789199216E-09" ixx="4.34107789199216E-09"
ixy="-3.70099015891851E-13" ixy="-3.70099015891851E-13"
ixz="-1.46784711283119E-13" ixz="-1.46784711283119E-13"
iyy="4.84012228066652E-09" iyy="4.84012228066652E-09"
iyz="2.0076254107311E-14" iyz="2.0076254107311E-14"
izz="4.84044599033091E-09" /> izz="4.84044599033091E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_frame.STL" /> filename="${mesh_path}camera_infra1_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_frame.STL" /> filename="${mesh_path}camera_infra1_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_right_ir_joint" name="camera_right_ir_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 -0.05000000074505806 0" xyz="0 -0.05000000074505806 0"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_depth_frame" /> link="camera_depth_frame" />
<child <child
link="camera_right_ir_frame" /> link="camera_right_ir_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_right_ir_optical_frame"> name="camera_right_ir_optical_frame">
<inertial> <inertial>
<origin <origin
xyz="1.61358465547434E-08 1.50984520398324E-07 -0.000620201436687004" xyz="1.61358465547434E-08 1.50984520398324E-07 -0.000620201436687004"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000453168343720438" /> value="0.000453168343720438" />
<inertia <inertia
ixx="4.84012228066652E-09" ixx="4.84012228066652E-09"
ixy="2.00762541073067E-14" ixy="2.00762541073067E-14"
ixz="3.70099015891915E-13" ixz="3.70099015891915E-13"
iyy="4.84044599033091E-09" iyy="4.84044599033091E-09"
iyz="1.46784711283141E-13" iyz="1.46784711283141E-13"
izz="4.34107789199216E-09" /> izz="4.34107789199216E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_optical_frame.STL" /> filename="${mesh_path}camera_infra1_optical_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra1_optical_frame.STL" /> filename="${mesh_path}camera_infra1_optical_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_right_ir_optical_joint" name="camera_right_ir_optical_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="-1.5707963267949 0 -1.5707963267949" /> rpy="-1.5707963267949 0 -1.5707963267949" />
<parent <parent
link="camera_right_ir_frame" /> link="camera_right_ir_frame" />
<child <child
link="camera_right_ir_optical_frame" /> link="camera_right_ir_optical_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_color_frame"> name="camera_color_frame">
<inertial> <inertial>
<origin <origin
xyz="2.29364384785394E-07 -2.11520958501546E-07 -0.00105779474323097" xyz="2.29364384785394E-07 -2.11520958501546E-07 -0.00105779474323097"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000139779411914196" /> value="0.000139779411914196" />
<inertia <inertia
ixx="6.11273726117097E-10" ixx="6.11273726117097E-10"
ixy="3.95051289663905E-14" ixy="3.95051289663905E-14"
ixz="1.55845266007242E-13" ixz="1.55845266007242E-13"
iyy="6.11076547268852E-10" iyy="6.11076547268852E-10"
iyz="2.25440284353371E-14" iyz="2.25440284353371E-14"
izz="6.69129408711142E-10" /> izz="6.69129408711142E-10" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_color_frame.STL" /> filename="${mesh_path}camera_color_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_color_frame.STL" /> filename="${mesh_path}camera_color_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_color_joint" name="camera_color_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0.0019099999917671084 -0.01400499977171421 4.999999873689376e-06" xyz="0.0019099999917671084 -0.01400499977171421 4.999999873689376e-06"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_depth_frame" /> link="camera_depth_frame" />
<child <child
link="camera_color_frame" /> link="camera_color_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_color_optical_frame"> name="camera_color_optical_frame">
<inertial> <inertial>
<origin <origin
xyz="2.29364384785394E-07 -2.11520958501541E-07 -0.00105779474323097" xyz="2.29364384785394E-07 -2.11520958501541E-07 -0.00105779474323097"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000139779411914196" /> value="0.000139779411914196" />
<inertia <inertia
ixx="6.11273726117097E-10" ixx="6.11273726117097E-10"
ixy="3.95051289663901E-14" ixy="3.95051289663901E-14"
ixz="1.55845266007242E-13" ixz="1.55845266007242E-13"
iyy="6.11076547268852E-10" iyy="6.11076547268852E-10"
iyz="2.25440284353391E-14" iyz="2.25440284353391E-14"
izz="6.69129408711142E-10" /> izz="6.69129408711142E-10" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_color_optical_frame.STL" /> filename="${mesh_path}camera_color_optical_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_color_optical_frame.STL" /> filename="${mesh_path}camera_color_optical_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_color_optical_joint" name="camera_color_optical_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="-1.5707963267949 0 -1.5707963267949" /> rpy="-1.5707963267949 0 -1.5707963267949" />
<parent <parent
link="camera_color_frame" /> link="camera_color_frame" />
<child <child
link="camera_color_optical_frame" /> link="camera_color_optical_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_left_ir_frame"> name="camera_left_ir_frame">
<inertial> <inertial>
<origin <origin
xyz="-0.000620201436687001 -1.6135846551274E-08 -1.50984520410662E-07" xyz="-0.000620201436687001 -1.6135846551274E-08 -1.50984520410662E-07"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000453168343720439" /> value="0.000453168343720439" />
<inertia <inertia
ixx="4.34107789199216E-09" ixx="4.34107789199216E-09"
ixy="-3.70099015888916E-13" ixy="-3.70099015888916E-13"
ixz="-1.46784711288897E-13" ixz="-1.46784711288897E-13"
iyy="4.84012228066651E-09" iyy="4.84012228066651E-09"
iyz="2.0076254105875E-14" iyz="2.0076254105875E-14"
izz="4.8404459903309E-09" /> izz="4.8404459903309E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra2_frame.STL" /> filename="${mesh_path}camera_infra2_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra2_frame.STL" /> filename="${mesh_path}camera_infra2_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_left_ir_joint" name="camera_left_ir_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_link" /> link="camera_link" />
<child <child
link="camera_left_ir_frame" /> link="camera_left_ir_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_left_ir_optical_frame"> name="camera_left_ir_optical_frame">
<inertial> <inertial>
<origin <origin
xyz="1.6135846551274E-08 1.50984520410652E-07 -0.000620201436687001" xyz="1.6135846551274E-08 1.50984520410652E-07 -0.000620201436687001"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000453168343720439" /> value="0.000453168343720439" />
<inertia <inertia
ixx="4.84012228066652E-09" ixx="4.84012228066652E-09"
ixy="2.00762541058687E-14" ixy="2.00762541058687E-14"
ixz="3.70099015888909E-13" ixz="3.70099015888909E-13"
iyy="4.8404459903309E-09" iyy="4.8404459903309E-09"
iyz="1.46784711288896E-13" iyz="1.46784711288896E-13"
izz="4.34107789199216E-09" /> izz="4.34107789199216E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra2_optical_frame.STL" /> filename="${mesh_path}camera_infra2_optical_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_infra2_optical_frame.STL" /> filename="${mesh_path}camera_infra2_optical_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_left_ir_optical_joint" name="camera_left_ir_optical_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="-1.5707963267949 0 -1.5707963267949" /> rpy="-1.5707963267949 0 -1.5707963267949" />
<parent <parent
link="camera_left_ir_frame" /> link="camera_left_ir_frame" />
<child <child
link="camera_left_ir_optical_frame" /> link="camera_left_ir_optical_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
<link <link
name="camera_depth_frame"> name="camera_depth_frame">
<inertial> <inertial>
<origin <origin
xyz="-0.000620201436687001 -1.6135846551274E-08 -1.50984520410661E-07" xyz="-0.000620201436687001 -1.6135846551274E-08 -1.50984520410661E-07"
rpy="0 0 0" /> rpy="0 0 0" />
<mass <mass
value="0.000453168343720439" /> value="0.000453168343720439" />
<inertia <inertia
ixx="4.34107789199216E-09" ixx="4.34107789199216E-09"
ixy="-3.70099015888929E-13" ixy="-3.70099015888929E-13"
ixz="-1.46784711288886E-13" ixz="-1.46784711288886E-13"
iyy="4.84012228066651E-09" iyy="4.84012228066651E-09"
iyz="2.00762541058859E-14" iyz="2.00762541058859E-14"
izz="4.8404459903309E-09" /> izz="4.8404459903309E-09" />
</inertial> </inertial>
<visual> <visual>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_depth_frame.STL" /> filename="${mesh_path}camera_depth_frame.STL" />
</geometry> </geometry>
<material <material
name=""> name="">
<color <color
rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" /> rgba="0.792156862745098 0.819607843137255 0.933333333333333 1" />
</material> </material>
</visual> </visual>
<collision> <collision>
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<geometry> <geometry>
<mesh <mesh
filename="${mesh_path}camera_depth_frame.STL" /> filename="${mesh_path}camera_depth_frame.STL" />
</geometry> </geometry>
</collision> </collision>
</link> </link>
<joint <joint
name="camera_depth_joint" name="camera_depth_joint"
type="fixed"> type="fixed">
<origin <origin
xyz="0 0 0" xyz="0 0 0"
rpy="0 0 0" /> rpy="0 0 0" />
<parent <parent
link="camera_link" /> link="camera_link" />
<child <child
link="camera_depth_frame" /> link="camera_depth_frame" />
<axis <axis
xyz="0 0 0" /> xyz="0 0 0" />
</joint> </joint>
</robot> </robot>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff