ajustado core da camera multiespectral, criado ferramenta para revisar as mascaras, ajsutado ferramentas para calibragem do módulo multiespectral, ajustado scripts de separação de dataset, normalizacao, treinamento, teste, exportação e benchmark, para modelo agricola

This commit is contained in:
Diego Freitas 2026-08-31 09:35:05 -03:00
parent c9ad8d2aff
commit e8cdb27687
44 changed files with 66473 additions and 8345 deletions

View File

@ -0,0 +1 @@
Coloque seu labelmap.txt real ao lado do EXE.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
{
"group_root": "C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\dist\\MaskReviewer\\group",
"labelmap": "C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\dist\\MaskReviewer\\labelmap.txt"
}

View File

@ -0,0 +1,51 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_all
datas = []
binaries = []
hiddenimports = []
tmp_ret = collect_all('cv2')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
a = Analysis(
['MaskReviewer.py'],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='MaskReviewer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='MaskReviewer',
)

View File

@ -0,0 +1,110 @@
MASKREVIEWER - PACOTE WINDOWS
================================
OBJETIVO
--------
Revisar, comparar e eventualmente editar máscaras já separadas pelo auditor.
Não carrega modelo, PyTorch, CUDA ou checkpoint.
PACOTE PARA O ANOTADOR
----------------------
A estrutura recomendada é:
MaskReviewer\
MaskReviewer.exe
_internal\
labelmap.txt
group\
chao\
previews\
masks\
predictions\
final_masks\
review_order.csv
chao_cana\
chao_cana_erva\
chao_erva\
...
Ao abrir MaskReviewer.exe:
1. Se "group" estiver ao lado do EXE, ele usa automaticamente.
2. Se "labelmap.txt" estiver ao lado do EXE, ele usa automaticamente.
3. Se algum deles não existir, abre o seletor do Windows.
4. Os caminhos escolhidos ficam lembrados em MaskReviewer.settings.json.
5. O estado do lote fica em group\review_choices.csv.
6. As escolhas finais ficam em cada:
group\<GRUPO>\final_masks\
NOVO LOTE
---------
Para mandar uma nova revisão à mesma pessoa:
1. Ela mantém:
MaskReviewer.exe
_internal\
labelmap.txt
2. Substitui a pasta:
group\
3. Abre o mesmo EXE.
Como review_choices.csv fica DENTRO de group\, a sessão nova começa limpa junto
com a nova pasta group.
CONTROLES
---------
Painel 2x2:
1 = máscara humana
2 = máscara do modelo
3 = REMAP
E = editar
A/D = anterior/próxima
S = pular
X = apagar decisão
Q/ESC = sair
A barra "Overlay %" controla a opacidade dos dois overlays ao mesmo tempo.
Editor:
E e depois:
1 = humana como base
2 = modelo como base
3 = começar de chão
Mouse esquerdo = adiciona ponto
Mouse direito = remove último ponto
ENTER = preenche polígono
C = próxima classe
0..9 = ID de classe
Z = desfazer
R = restaurar base
BACKSPACE/DEL = limpar pontos abertos
S = salvar
ESC/Q = cancelar edição
COMO GERAR O EXE
----------------
Execute com duplo clique:
build_windows.bat
Recomendado:
Windows 10/11
Python 3.11 ou 3.12 instalado SOMENTE na máquina de build
O script cria:
dist\MaskReviewer\MaskReviewer.exe
Depois do build, a máquina do anotador NÃO precisa ter Python.
POR QUE --ONEDIR?
-----------------
O build usa PyInstaller --onedir porque OpenCV/Numpy ficam mais confiáveis,
iniciam mais rápido e são mais fáceis de diagnosticar do que um EXE --onefile.
IMPORTANTE
----------
O MaskReviewer nunca altera o dataset bruto.
Ele só escreve final_masks e review_choices.csv dentro do lote enviado.

View File

@ -0,0 +1,77 @@
('C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\MaskReviewer.exe',
False,
False,
True,
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-windowed.ico',
None,
False,
False,
b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<assembly xmlns='
b'"urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\n <trustInfo x'
b'mlns="urn:schemas-microsoft-com:asm.v3">\n <security>\n <requested'
b'Privileges>\n <requestedExecutionLevel level="asInvoker" uiAccess='
b'"false"/>\n </requestedPrivileges>\n </security>\n </trustInfo>\n '
b'<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">\n <'
b'application>\n <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f'
b'0}"/>\n <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>\n '
b' <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>\n <s'
b'upportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>\n <supporte'
b'dOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>\n </application>\n <'
b'/compatibility>\n <application xmlns="urn:schemas-microsoft-com:asm.v3">'
b'\n <windowsSettings>\n <longPathAware xmlns="http://schemas.micros'
b'oft.com/SMI/2016/WindowsSettings">true</longPathAware>\n </windowsSett'
b'ings>\n </application>\n <dependency>\n <dependentAssembly>\n <ass'
b'emblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version='
b'"6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" langua'
b'ge="*"/>\n </dependentAssembly>\n </dependency>\n</assembly>',
True,
False,
None,
None,
None,
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\MaskReviewer.pkg',
[('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\PYZ-00.pyz',
'PYZ'),
('struct',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\struct.pyc',
'PYMODULE'),
('pyimod01_archive',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'),
('pyimod02_importers',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'),
('pyimod03_ctypes',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'),
('pyimod04_pywin32',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'),
('pyiboot01_bootstrap',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'),
('pyi_rth__tkinter',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth__tkinter.py',
'PYSOURCE'),
('pyi_rth_inspect',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('MaskReviewer',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\MaskReviewer.py',
'PYSOURCE')],
[],
False,
False,
1787944409,
[('runw.exe',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe',
'EXECUTABLE')],
'C:\\Python312\\python312.dll')

View File

@ -0,0 +1,55 @@
('C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\MaskReviewer.pkg',
{'BINARY': True,
'DATA': True,
'EXECUTABLE': True,
'EXTENSION': True,
'PYMODULE': True,
'PYSOURCE': True,
'PYZ': False,
'SPLASH': True,
'SYMLINK': False},
[('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\PYZ-00.pyz',
'PYZ'),
('struct',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\struct.pyc',
'PYMODULE'),
('pyimod01_archive',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'),
('pyimod02_importers',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'),
('pyimod03_ctypes',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'),
('pyimod04_pywin32',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'),
('pyiboot01_bootstrap',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'),
('pyi_rth__tkinter',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth__tkinter.py',
'PYSOURCE'),
('pyi_rth_inspect',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('MaskReviewer',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\MaskReviewer.py',
'PYSOURCE')],
'python312.dll',
True,
False,
False,
[],
None,
None,
None)

View File

@ -0,0 +1,815 @@
('C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\build\\MaskReviewer\\PYZ-00.pyz',
[('__future__', 'C:\\Python312\\Lib\\__future__.py', 'PYMODULE'),
('_aix_support', 'C:\\Python312\\Lib\\_aix_support.py', 'PYMODULE'),
('_compat_pickle', 'C:\\Python312\\Lib\\_compat_pickle.py', 'PYMODULE'),
('_compression', 'C:\\Python312\\Lib\\_compression.py', 'PYMODULE'),
('_py_abc', 'C:\\Python312\\Lib\\_py_abc.py', 'PYMODULE'),
('_pydatetime', 'C:\\Python312\\Lib\\_pydatetime.py', 'PYMODULE'),
('_pydecimal', 'C:\\Python312\\Lib\\_pydecimal.py', 'PYMODULE'),
('_strptime', 'C:\\Python312\\Lib\\_strptime.py', 'PYMODULE'),
('_threading_local', 'C:\\Python312\\Lib\\_threading_local.py', 'PYMODULE'),
('argparse', 'C:\\Python312\\Lib\\argparse.py', 'PYMODULE'),
('ast', 'C:\\Python312\\Lib\\ast.py', 'PYMODULE'),
('asyncio', 'C:\\Python312\\Lib\\asyncio\\__init__.py', 'PYMODULE'),
('asyncio.base_events',
'C:\\Python312\\Lib\\asyncio\\base_events.py',
'PYMODULE'),
('asyncio.base_futures',
'C:\\Python312\\Lib\\asyncio\\base_futures.py',
'PYMODULE'),
('asyncio.base_subprocess',
'C:\\Python312\\Lib\\asyncio\\base_subprocess.py',
'PYMODULE'),
('asyncio.base_tasks',
'C:\\Python312\\Lib\\asyncio\\base_tasks.py',
'PYMODULE'),
('asyncio.constants',
'C:\\Python312\\Lib\\asyncio\\constants.py',
'PYMODULE'),
('asyncio.coroutines',
'C:\\Python312\\Lib\\asyncio\\coroutines.py',
'PYMODULE'),
('asyncio.events', 'C:\\Python312\\Lib\\asyncio\\events.py', 'PYMODULE'),
('asyncio.exceptions',
'C:\\Python312\\Lib\\asyncio\\exceptions.py',
'PYMODULE'),
('asyncio.format_helpers',
'C:\\Python312\\Lib\\asyncio\\format_helpers.py',
'PYMODULE'),
('asyncio.futures', 'C:\\Python312\\Lib\\asyncio\\futures.py', 'PYMODULE'),
('asyncio.locks', 'C:\\Python312\\Lib\\asyncio\\locks.py', 'PYMODULE'),
('asyncio.log', 'C:\\Python312\\Lib\\asyncio\\log.py', 'PYMODULE'),
('asyncio.mixins', 'C:\\Python312\\Lib\\asyncio\\mixins.py', 'PYMODULE'),
('asyncio.proactor_events',
'C:\\Python312\\Lib\\asyncio\\proactor_events.py',
'PYMODULE'),
('asyncio.protocols',
'C:\\Python312\\Lib\\asyncio\\protocols.py',
'PYMODULE'),
('asyncio.queues', 'C:\\Python312\\Lib\\asyncio\\queues.py', 'PYMODULE'),
('asyncio.runners', 'C:\\Python312\\Lib\\asyncio\\runners.py', 'PYMODULE'),
('asyncio.selector_events',
'C:\\Python312\\Lib\\asyncio\\selector_events.py',
'PYMODULE'),
('asyncio.sslproto', 'C:\\Python312\\Lib\\asyncio\\sslproto.py', 'PYMODULE'),
('asyncio.staggered',
'C:\\Python312\\Lib\\asyncio\\staggered.py',
'PYMODULE'),
('asyncio.streams', 'C:\\Python312\\Lib\\asyncio\\streams.py', 'PYMODULE'),
('asyncio.subprocess',
'C:\\Python312\\Lib\\asyncio\\subprocess.py',
'PYMODULE'),
('asyncio.taskgroups',
'C:\\Python312\\Lib\\asyncio\\taskgroups.py',
'PYMODULE'),
('asyncio.tasks', 'C:\\Python312\\Lib\\asyncio\\tasks.py', 'PYMODULE'),
('asyncio.threads', 'C:\\Python312\\Lib\\asyncio\\threads.py', 'PYMODULE'),
('asyncio.timeouts', 'C:\\Python312\\Lib\\asyncio\\timeouts.py', 'PYMODULE'),
('asyncio.transports',
'C:\\Python312\\Lib\\asyncio\\transports.py',
'PYMODULE'),
('asyncio.trsock', 'C:\\Python312\\Lib\\asyncio\\trsock.py', 'PYMODULE'),
('asyncio.unix_events',
'C:\\Python312\\Lib\\asyncio\\unix_events.py',
'PYMODULE'),
('asyncio.windows_events',
'C:\\Python312\\Lib\\asyncio\\windows_events.py',
'PYMODULE'),
('asyncio.windows_utils',
'C:\\Python312\\Lib\\asyncio\\windows_utils.py',
'PYMODULE'),
('base64', 'C:\\Python312\\Lib\\base64.py', 'PYMODULE'),
('bdb', 'C:\\Python312\\Lib\\bdb.py', 'PYMODULE'),
('bisect', 'C:\\Python312\\Lib\\bisect.py', 'PYMODULE'),
('bz2', 'C:\\Python312\\Lib\\bz2.py', 'PYMODULE'),
('calendar', 'C:\\Python312\\Lib\\calendar.py', 'PYMODULE'),
('cmd', 'C:\\Python312\\Lib\\cmd.py', 'PYMODULE'),
('code', 'C:\\Python312\\Lib\\code.py', 'PYMODULE'),
('codeop', 'C:\\Python312\\Lib\\codeop.py', 'PYMODULE'),
('concurrent', 'C:\\Python312\\Lib\\concurrent\\__init__.py', 'PYMODULE'),
('concurrent.futures',
'C:\\Python312\\Lib\\concurrent\\futures\\__init__.py',
'PYMODULE'),
('concurrent.futures._base',
'C:\\Python312\\Lib\\concurrent\\futures\\_base.py',
'PYMODULE'),
('concurrent.futures.process',
'C:\\Python312\\Lib\\concurrent\\futures\\process.py',
'PYMODULE'),
('concurrent.futures.thread',
'C:\\Python312\\Lib\\concurrent\\futures\\thread.py',
'PYMODULE'),
('contextlib', 'C:\\Python312\\Lib\\contextlib.py', 'PYMODULE'),
('contextvars', 'C:\\Python312\\Lib\\contextvars.py', 'PYMODULE'),
('copy', 'C:\\Python312\\Lib\\copy.py', 'PYMODULE'),
('csv', 'C:\\Python312\\Lib\\csv.py', 'PYMODULE'),
('ctypes', 'C:\\Python312\\Lib\\ctypes\\__init__.py', 'PYMODULE'),
('ctypes._endian', 'C:\\Python312\\Lib\\ctypes\\_endian.py', 'PYMODULE'),
('dataclasses', 'C:\\Python312\\Lib\\dataclasses.py', 'PYMODULE'),
('datetime', 'C:\\Python312\\Lib\\datetime.py', 'PYMODULE'),
('decimal', 'C:\\Python312\\Lib\\decimal.py', 'PYMODULE'),
('difflib', 'C:\\Python312\\Lib\\difflib.py', 'PYMODULE'),
('dis', 'C:\\Python312\\Lib\\dis.py', 'PYMODULE'),
('doctest', 'C:\\Python312\\Lib\\doctest.py', 'PYMODULE'),
('email', 'C:\\Python312\\Lib\\email\\__init__.py', 'PYMODULE'),
('email._encoded_words',
'C:\\Python312\\Lib\\email\\_encoded_words.py',
'PYMODULE'),
('email._header_value_parser',
'C:\\Python312\\Lib\\email\\_header_value_parser.py',
'PYMODULE'),
('email._parseaddr', 'C:\\Python312\\Lib\\email\\_parseaddr.py', 'PYMODULE'),
('email._policybase',
'C:\\Python312\\Lib\\email\\_policybase.py',
'PYMODULE'),
('email.base64mime', 'C:\\Python312\\Lib\\email\\base64mime.py', 'PYMODULE'),
('email.charset', 'C:\\Python312\\Lib\\email\\charset.py', 'PYMODULE'),
('email.contentmanager',
'C:\\Python312\\Lib\\email\\contentmanager.py',
'PYMODULE'),
('email.encoders', 'C:\\Python312\\Lib\\email\\encoders.py', 'PYMODULE'),
('email.errors', 'C:\\Python312\\Lib\\email\\errors.py', 'PYMODULE'),
('email.feedparser', 'C:\\Python312\\Lib\\email\\feedparser.py', 'PYMODULE'),
('email.generator', 'C:\\Python312\\Lib\\email\\generator.py', 'PYMODULE'),
('email.header', 'C:\\Python312\\Lib\\email\\header.py', 'PYMODULE'),
('email.headerregistry',
'C:\\Python312\\Lib\\email\\headerregistry.py',
'PYMODULE'),
('email.iterators', 'C:\\Python312\\Lib\\email\\iterators.py', 'PYMODULE'),
('email.message', 'C:\\Python312\\Lib\\email\\message.py', 'PYMODULE'),
('email.parser', 'C:\\Python312\\Lib\\email\\parser.py', 'PYMODULE'),
('email.policy', 'C:\\Python312\\Lib\\email\\policy.py', 'PYMODULE'),
('email.quoprimime', 'C:\\Python312\\Lib\\email\\quoprimime.py', 'PYMODULE'),
('email.utils', 'C:\\Python312\\Lib\\email\\utils.py', 'PYMODULE'),
('fileinput', 'C:\\Python312\\Lib\\fileinput.py', 'PYMODULE'),
('fnmatch', 'C:\\Python312\\Lib\\fnmatch.py', 'PYMODULE'),
('fractions', 'C:\\Python312\\Lib\\fractions.py', 'PYMODULE'),
('ftplib', 'C:\\Python312\\Lib\\ftplib.py', 'PYMODULE'),
('getopt', 'C:\\Python312\\Lib\\getopt.py', 'PYMODULE'),
('getpass', 'C:\\Python312\\Lib\\getpass.py', 'PYMODULE'),
('gettext', 'C:\\Python312\\Lib\\gettext.py', 'PYMODULE'),
('glob', 'C:\\Python312\\Lib\\glob.py', 'PYMODULE'),
('gzip', 'C:\\Python312\\Lib\\gzip.py', 'PYMODULE'),
('hashlib', 'C:\\Python312\\Lib\\hashlib.py', 'PYMODULE'),
('hmac', 'C:\\Python312\\Lib\\hmac.py', 'PYMODULE'),
('html', 'C:\\Python312\\Lib\\html\\__init__.py', 'PYMODULE'),
('html.entities', 'C:\\Python312\\Lib\\html\\entities.py', 'PYMODULE'),
('http', 'C:\\Python312\\Lib\\http\\__init__.py', 'PYMODULE'),
('http.client', 'C:\\Python312\\Lib\\http\\client.py', 'PYMODULE'),
('http.cookiejar', 'C:\\Python312\\Lib\\http\\cookiejar.py', 'PYMODULE'),
('http.server', 'C:\\Python312\\Lib\\http\\server.py', 'PYMODULE'),
('importlib', 'C:\\Python312\\Lib\\importlib\\__init__.py', 'PYMODULE'),
('importlib._abc', 'C:\\Python312\\Lib\\importlib\\_abc.py', 'PYMODULE'),
('importlib._bootstrap',
'C:\\Python312\\Lib\\importlib\\_bootstrap.py',
'PYMODULE'),
('importlib._bootstrap_external',
'C:\\Python312\\Lib\\importlib\\_bootstrap_external.py',
'PYMODULE'),
('importlib.abc', 'C:\\Python312\\Lib\\importlib\\abc.py', 'PYMODULE'),
('importlib.machinery',
'C:\\Python312\\Lib\\importlib\\machinery.py',
'PYMODULE'),
('importlib.metadata',
'C:\\Python312\\Lib\\importlib\\metadata\\__init__.py',
'PYMODULE'),
('importlib.metadata._adapters',
'C:\\Python312\\Lib\\importlib\\metadata\\_adapters.py',
'PYMODULE'),
('importlib.metadata._collections',
'C:\\Python312\\Lib\\importlib\\metadata\\_collections.py',
'PYMODULE'),
('importlib.metadata._functools',
'C:\\Python312\\Lib\\importlib\\metadata\\_functools.py',
'PYMODULE'),
('importlib.metadata._itertools',
'C:\\Python312\\Lib\\importlib\\metadata\\_itertools.py',
'PYMODULE'),
('importlib.metadata._meta',
'C:\\Python312\\Lib\\importlib\\metadata\\_meta.py',
'PYMODULE'),
('importlib.metadata._text',
'C:\\Python312\\Lib\\importlib\\metadata\\_text.py',
'PYMODULE'),
('importlib.readers',
'C:\\Python312\\Lib\\importlib\\readers.py',
'PYMODULE'),
('importlib.resources',
'C:\\Python312\\Lib\\importlib\\resources\\__init__.py',
'PYMODULE'),
('importlib.resources._adapters',
'C:\\Python312\\Lib\\importlib\\resources\\_adapters.py',
'PYMODULE'),
('importlib.resources._common',
'C:\\Python312\\Lib\\importlib\\resources\\_common.py',
'PYMODULE'),
('importlib.resources._itertools',
'C:\\Python312\\Lib\\importlib\\resources\\_itertools.py',
'PYMODULE'),
('importlib.resources._legacy',
'C:\\Python312\\Lib\\importlib\\resources\\_legacy.py',
'PYMODULE'),
('importlib.resources.abc',
'C:\\Python312\\Lib\\importlib\\resources\\abc.py',
'PYMODULE'),
('importlib.resources.readers',
'C:\\Python312\\Lib\\importlib\\resources\\readers.py',
'PYMODULE'),
('importlib.util', 'C:\\Python312\\Lib\\importlib\\util.py', 'PYMODULE'),
('inspect', 'C:\\Python312\\Lib\\inspect.py', 'PYMODULE'),
('ipaddress', 'C:\\Python312\\Lib\\ipaddress.py', 'PYMODULE'),
('json', 'C:\\Python312\\Lib\\json\\__init__.py', 'PYMODULE'),
('json.decoder', 'C:\\Python312\\Lib\\json\\decoder.py', 'PYMODULE'),
('json.encoder', 'C:\\Python312\\Lib\\json\\encoder.py', 'PYMODULE'),
('json.scanner', 'C:\\Python312\\Lib\\json\\scanner.py', 'PYMODULE'),
('logging', 'C:\\Python312\\Lib\\logging\\__init__.py', 'PYMODULE'),
('lzma', 'C:\\Python312\\Lib\\lzma.py', 'PYMODULE'),
('mimetypes', 'C:\\Python312\\Lib\\mimetypes.py', 'PYMODULE'),
('multiprocessing',
'C:\\Python312\\Lib\\multiprocessing\\__init__.py',
'PYMODULE'),
('multiprocessing.connection',
'C:\\Python312\\Lib\\multiprocessing\\connection.py',
'PYMODULE'),
('multiprocessing.context',
'C:\\Python312\\Lib\\multiprocessing\\context.py',
'PYMODULE'),
('multiprocessing.dummy',
'C:\\Python312\\Lib\\multiprocessing\\dummy\\__init__.py',
'PYMODULE'),
('multiprocessing.dummy.connection',
'C:\\Python312\\Lib\\multiprocessing\\dummy\\connection.py',
'PYMODULE'),
('multiprocessing.forkserver',
'C:\\Python312\\Lib\\multiprocessing\\forkserver.py',
'PYMODULE'),
('multiprocessing.heap',
'C:\\Python312\\Lib\\multiprocessing\\heap.py',
'PYMODULE'),
('multiprocessing.managers',
'C:\\Python312\\Lib\\multiprocessing\\managers.py',
'PYMODULE'),
('multiprocessing.pool',
'C:\\Python312\\Lib\\multiprocessing\\pool.py',
'PYMODULE'),
('multiprocessing.popen_fork',
'C:\\Python312\\Lib\\multiprocessing\\popen_fork.py',
'PYMODULE'),
('multiprocessing.popen_forkserver',
'C:\\Python312\\Lib\\multiprocessing\\popen_forkserver.py',
'PYMODULE'),
('multiprocessing.popen_spawn_posix',
'C:\\Python312\\Lib\\multiprocessing\\popen_spawn_posix.py',
'PYMODULE'),
('multiprocessing.popen_spawn_win32',
'C:\\Python312\\Lib\\multiprocessing\\popen_spawn_win32.py',
'PYMODULE'),
('multiprocessing.process',
'C:\\Python312\\Lib\\multiprocessing\\process.py',
'PYMODULE'),
('multiprocessing.queues',
'C:\\Python312\\Lib\\multiprocessing\\queues.py',
'PYMODULE'),
('multiprocessing.reduction',
'C:\\Python312\\Lib\\multiprocessing\\reduction.py',
'PYMODULE'),
('multiprocessing.resource_sharer',
'C:\\Python312\\Lib\\multiprocessing\\resource_sharer.py',
'PYMODULE'),
('multiprocessing.resource_tracker',
'C:\\Python312\\Lib\\multiprocessing\\resource_tracker.py',
'PYMODULE'),
('multiprocessing.shared_memory',
'C:\\Python312\\Lib\\multiprocessing\\shared_memory.py',
'PYMODULE'),
('multiprocessing.sharedctypes',
'C:\\Python312\\Lib\\multiprocessing\\sharedctypes.py',
'PYMODULE'),
('multiprocessing.spawn',
'C:\\Python312\\Lib\\multiprocessing\\spawn.py',
'PYMODULE'),
('multiprocessing.synchronize',
'C:\\Python312\\Lib\\multiprocessing\\synchronize.py',
'PYMODULE'),
('multiprocessing.util',
'C:\\Python312\\Lib\\multiprocessing\\util.py',
'PYMODULE'),
('netrc', 'C:\\Python312\\Lib\\netrc.py', 'PYMODULE'),
('nturl2path', 'C:\\Python312\\Lib\\nturl2path.py', 'PYMODULE'),
('numbers', 'C:\\Python312\\Lib\\numbers.py', 'PYMODULE'),
('numpy',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\__init__.py',
'PYMODULE'),
('numpy.__config__',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\__config__.py',
'PYMODULE'),
('numpy._array_api_info',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_array_api_info.py',
'PYMODULE'),
('numpy._core',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\__init__.py',
'PYMODULE'),
('numpy._core._add_newdocs',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_add_newdocs.py',
'PYMODULE'),
('numpy._core._add_newdocs_scalars',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_add_newdocs_scalars.py',
'PYMODULE'),
('numpy._core._asarray',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_asarray.py',
'PYMODULE'),
('numpy._core._dtype',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_dtype.py',
'PYMODULE'),
('numpy._core._dtype_ctypes',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_dtype_ctypes.py',
'PYMODULE'),
('numpy._core._exceptions',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_exceptions.py',
'PYMODULE'),
('numpy._core._internal',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_internal.py',
'PYMODULE'),
('numpy._core._methods',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_methods.py',
'PYMODULE'),
('numpy._core._string_helpers',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_string_helpers.py',
'PYMODULE'),
('numpy._core._type_aliases',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_type_aliases.py',
'PYMODULE'),
('numpy._core._ufunc_config',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\_ufunc_config.py',
'PYMODULE'),
('numpy._core.arrayprint',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\arrayprint.py',
'PYMODULE'),
('numpy._core.defchararray',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\defchararray.py',
'PYMODULE'),
('numpy._core.einsumfunc',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\einsumfunc.py',
'PYMODULE'),
('numpy._core.fromnumeric',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\fromnumeric.py',
'PYMODULE'),
('numpy._core.function_base',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\function_base.py',
'PYMODULE'),
('numpy._core.getlimits',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\getlimits.py',
'PYMODULE'),
('numpy._core.memmap',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\memmap.py',
'PYMODULE'),
('numpy._core.multiarray',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\multiarray.py',
'PYMODULE'),
('numpy._core.numeric',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\numeric.py',
'PYMODULE'),
('numpy._core.numerictypes',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\numerictypes.py',
'PYMODULE'),
('numpy._core.overrides',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\overrides.py',
'PYMODULE'),
('numpy._core.printoptions',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\printoptions.py',
'PYMODULE'),
('numpy._core.records',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\records.py',
'PYMODULE'),
('numpy._core.shape_base',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\shape_base.py',
'PYMODULE'),
('numpy._core.strings',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\strings.py',
'PYMODULE'),
('numpy._core.umath',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_core\\umath.py',
'PYMODULE'),
('numpy._distributor_init',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_distributor_init.py',
'PYMODULE'),
('numpy._expired_attrs_2_0',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_expired_attrs_2_0.py',
'PYMODULE'),
('numpy._globals',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_globals.py',
'PYMODULE'),
('numpy._pytesttester',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_pytesttester.py',
'PYMODULE'),
('numpy._typing',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\__init__.py',
'PYMODULE'),
('numpy._typing._add_docstring',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_add_docstring.py',
'PYMODULE'),
('numpy._typing._array_like',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_array_like.py',
'PYMODULE'),
('numpy._typing._char_codes',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_char_codes.py',
'PYMODULE'),
('numpy._typing._dtype_like',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_dtype_like.py',
'PYMODULE'),
('numpy._typing._nbit',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_nbit.py',
'PYMODULE'),
('numpy._typing._nbit_base',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_nbit_base.py',
'PYMODULE'),
('numpy._typing._nested_sequence',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_nested_sequence.py',
'PYMODULE'),
('numpy._typing._scalars',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_scalars.py',
'PYMODULE'),
('numpy._typing._shape',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_shape.py',
'PYMODULE'),
('numpy._typing._ufunc',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_typing\\_ufunc.py',
'PYMODULE'),
('numpy._utils',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_utils\\__init__.py',
'PYMODULE'),
('numpy._utils._conversions',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_utils\\_conversions.py',
'PYMODULE'),
('numpy._utils._inspect',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\_utils\\_inspect.py',
'PYMODULE'),
('numpy.char',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\char\\__init__.py',
'PYMODULE'),
('numpy.core',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\core\\__init__.py',
'PYMODULE'),
('numpy.core._utils',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\core\\_utils.py',
'PYMODULE'),
('numpy.core.multiarray',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\core\\multiarray.py',
'PYMODULE'),
('numpy.ctypeslib',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\ctypeslib\\__init__.py',
'PYMODULE'),
('numpy.ctypeslib._ctypeslib',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\ctypeslib\\_ctypeslib.py',
'PYMODULE'),
('numpy.dtypes',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\dtypes.py',
'PYMODULE'),
('numpy.exceptions',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\exceptions.py',
'PYMODULE'),
('numpy.f2py',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\__init__.py',
'PYMODULE'),
('numpy.f2py.__version__',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\__version__.py',
'PYMODULE'),
('numpy.f2py._backends',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\_backends\\__init__.py',
'PYMODULE'),
('numpy.f2py._backends._backend',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\_backends\\_backend.py',
'PYMODULE'),
('numpy.f2py._backends._meson',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\_backends\\_meson.py',
'PYMODULE'),
('numpy.f2py._isocbind',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\_isocbind.py',
'PYMODULE'),
('numpy.f2py.auxfuncs',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\auxfuncs.py',
'PYMODULE'),
('numpy.f2py.capi_maps',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\capi_maps.py',
'PYMODULE'),
('numpy.f2py.cb_rules',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\cb_rules.py',
'PYMODULE'),
('numpy.f2py.cfuncs',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\cfuncs.py',
'PYMODULE'),
('numpy.f2py.common_rules',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\common_rules.py',
'PYMODULE'),
('numpy.f2py.crackfortran',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\crackfortran.py',
'PYMODULE'),
('numpy.f2py.diagnose',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\diagnose.py',
'PYMODULE'),
('numpy.f2py.f2py2e',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\f2py2e.py',
'PYMODULE'),
('numpy.f2py.f90mod_rules',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\f90mod_rules.py',
'PYMODULE'),
('numpy.f2py.func2subr',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\func2subr.py',
'PYMODULE'),
('numpy.f2py.rules',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\rules.py',
'PYMODULE'),
('numpy.f2py.symbolic',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\symbolic.py',
'PYMODULE'),
('numpy.f2py.use_rules',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\f2py\\use_rules.py',
'PYMODULE'),
('numpy.fft',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\fft\\__init__.py',
'PYMODULE'),
('numpy.fft._helper',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\fft\\_helper.py',
'PYMODULE'),
('numpy.fft._pocketfft',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\fft\\_pocketfft.py',
'PYMODULE'),
('numpy.lib',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\__init__.py',
'PYMODULE'),
('numpy.lib._array_utils_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_array_utils_impl.py',
'PYMODULE'),
('numpy.lib._arraypad_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_arraypad_impl.py',
'PYMODULE'),
('numpy.lib._arraysetops_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_arraysetops_impl.py',
'PYMODULE'),
('numpy.lib._arrayterator_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_arrayterator_impl.py',
'PYMODULE'),
('numpy.lib._datasource',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_datasource.py',
'PYMODULE'),
('numpy.lib._format_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_format_impl.py',
'PYMODULE'),
('numpy.lib._function_base_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_function_base_impl.py',
'PYMODULE'),
('numpy.lib._histograms_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_histograms_impl.py',
'PYMODULE'),
('numpy.lib._index_tricks_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_index_tricks_impl.py',
'PYMODULE'),
('numpy.lib._iotools',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_iotools.py',
'PYMODULE'),
('numpy.lib._nanfunctions_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_nanfunctions_impl.py',
'PYMODULE'),
('numpy.lib._npyio_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_npyio_impl.py',
'PYMODULE'),
('numpy.lib._polynomial_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_polynomial_impl.py',
'PYMODULE'),
('numpy.lib._scimath_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_scimath_impl.py',
'PYMODULE'),
('numpy.lib._shape_base_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_shape_base_impl.py',
'PYMODULE'),
('numpy.lib._stride_tricks_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_stride_tricks_impl.py',
'PYMODULE'),
('numpy.lib._twodim_base_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_twodim_base_impl.py',
'PYMODULE'),
('numpy.lib._type_check_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_type_check_impl.py',
'PYMODULE'),
('numpy.lib._ufunclike_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_ufunclike_impl.py',
'PYMODULE'),
('numpy.lib._utils_impl',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_utils_impl.py',
'PYMODULE'),
('numpy.lib._version',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\_version.py',
'PYMODULE'),
('numpy.lib.array_utils',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\array_utils.py',
'PYMODULE'),
('numpy.lib.format',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\format.py',
'PYMODULE'),
('numpy.lib.introspect',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\introspect.py',
'PYMODULE'),
('numpy.lib.mixins',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\mixins.py',
'PYMODULE'),
('numpy.lib.npyio',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\npyio.py',
'PYMODULE'),
('numpy.lib.recfunctions',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\recfunctions.py',
'PYMODULE'),
('numpy.lib.scimath',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\scimath.py',
'PYMODULE'),
('numpy.lib.stride_tricks',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\lib\\stride_tricks.py',
'PYMODULE'),
('numpy.linalg',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\linalg\\__init__.py',
'PYMODULE'),
('numpy.linalg._linalg',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\linalg\\_linalg.py',
'PYMODULE'),
('numpy.ma',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\ma\\__init__.py',
'PYMODULE'),
('numpy.ma.core',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\ma\\core.py',
'PYMODULE'),
('numpy.ma.extras',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\ma\\extras.py',
'PYMODULE'),
('numpy.ma.mrecords',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\ma\\mrecords.py',
'PYMODULE'),
('numpy.matlib',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\matlib.py',
'PYMODULE'),
('numpy.matrixlib',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\matrixlib\\__init__.py',
'PYMODULE'),
('numpy.matrixlib.defmatrix',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\matrixlib\\defmatrix.py',
'PYMODULE'),
('numpy.polynomial',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\__init__.py',
'PYMODULE'),
('numpy.polynomial._polybase',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\_polybase.py',
'PYMODULE'),
('numpy.polynomial.chebyshev',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\chebyshev.py',
'PYMODULE'),
('numpy.polynomial.hermite',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\hermite.py',
'PYMODULE'),
('numpy.polynomial.hermite_e',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\hermite_e.py',
'PYMODULE'),
('numpy.polynomial.laguerre',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\laguerre.py',
'PYMODULE'),
('numpy.polynomial.legendre',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\legendre.py',
'PYMODULE'),
('numpy.polynomial.polynomial',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\polynomial.py',
'PYMODULE'),
('numpy.polynomial.polyutils',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\polynomial\\polyutils.py',
'PYMODULE'),
('numpy.random',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\random\\__init__.py',
'PYMODULE'),
('numpy.random._pickle',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\random\\_pickle.py',
'PYMODULE'),
('numpy.rec',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\rec\\__init__.py',
'PYMODULE'),
('numpy.strings',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\strings\\__init__.py',
'PYMODULE'),
('numpy.testing',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\testing\\__init__.py',
'PYMODULE'),
('numpy.testing._private',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\testing\\_private\\__init__.py',
'PYMODULE'),
('numpy.testing._private.extbuild',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\testing\\_private\\extbuild.py',
'PYMODULE'),
('numpy.testing._private.utils',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\testing\\_private\\utils.py',
'PYMODULE'),
('numpy.testing.overrides',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\testing\\overrides.py',
'PYMODULE'),
('numpy.typing',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\typing\\__init__.py',
'PYMODULE'),
('numpy.version',
'C:\\ZendionInc\\agrobot_base\\Python\\OAK\\datasets\\oak-fcc-3\\MaskReviewer_package\\.build_venv\\Lib\\site-packages\\numpy\\version.py',
'PYMODULE'),
('opcode', 'C:\\Python312\\Lib\\opcode.py', 'PYMODULE'),
('pathlib', 'C:\\Python312\\Lib\\pathlib.py', 'PYMODULE'),
('pdb', 'C:\\Python312\\Lib\\pdb.py', 'PYMODULE'),
('pickle', 'C:\\Python312\\Lib\\pickle.py', 'PYMODULE'),
('pkgutil', 'C:\\Python312\\Lib\\pkgutil.py', 'PYMODULE'),
('platform', 'C:\\Python312\\Lib\\platform.py', 'PYMODULE'),
('pprint', 'C:\\Python312\\Lib\\pprint.py', 'PYMODULE'),
('py_compile', 'C:\\Python312\\Lib\\py_compile.py', 'PYMODULE'),
('pydoc', 'C:\\Python312\\Lib\\pydoc.py', 'PYMODULE'),
('pydoc_data', 'C:\\Python312\\Lib\\pydoc_data\\__init__.py', 'PYMODULE'),
('pydoc_data.topics',
'C:\\Python312\\Lib\\pydoc_data\\topics.py',
'PYMODULE'),
('queue', 'C:\\Python312\\Lib\\queue.py', 'PYMODULE'),
('quopri', 'C:\\Python312\\Lib\\quopri.py', 'PYMODULE'),
('random', 'C:\\Python312\\Lib\\random.py', 'PYMODULE'),
('runpy', 'C:\\Python312\\Lib\\runpy.py', 'PYMODULE'),
('secrets', 'C:\\Python312\\Lib\\secrets.py', 'PYMODULE'),
('selectors', 'C:\\Python312\\Lib\\selectors.py', 'PYMODULE'),
('shlex', 'C:\\Python312\\Lib\\shlex.py', 'PYMODULE'),
('shutil', 'C:\\Python312\\Lib\\shutil.py', 'PYMODULE'),
('signal', 'C:\\Python312\\Lib\\signal.py', 'PYMODULE'),
('socket', 'C:\\Python312\\Lib\\socket.py', 'PYMODULE'),
('socketserver', 'C:\\Python312\\Lib\\socketserver.py', 'PYMODULE'),
('ssl', 'C:\\Python312\\Lib\\ssl.py', 'PYMODULE'),
('statistics', 'C:\\Python312\\Lib\\statistics.py', 'PYMODULE'),
('string', 'C:\\Python312\\Lib\\string.py', 'PYMODULE'),
('stringprep', 'C:\\Python312\\Lib\\stringprep.py', 'PYMODULE'),
('subprocess', 'C:\\Python312\\Lib\\subprocess.py', 'PYMODULE'),
('sysconfig', 'C:\\Python312\\Lib\\sysconfig.py', 'PYMODULE'),
('tarfile', 'C:\\Python312\\Lib\\tarfile.py', 'PYMODULE'),
('tempfile', 'C:\\Python312\\Lib\\tempfile.py', 'PYMODULE'),
('textwrap', 'C:\\Python312\\Lib\\textwrap.py', 'PYMODULE'),
('threading', 'C:\\Python312\\Lib\\threading.py', 'PYMODULE'),
('tkinter', 'C:\\Python312\\Lib\\tkinter\\__init__.py', 'PYMODULE'),
('tkinter.commondialog',
'C:\\Python312\\Lib\\tkinter\\commondialog.py',
'PYMODULE'),
('tkinter.constants',
'C:\\Python312\\Lib\\tkinter\\constants.py',
'PYMODULE'),
('tkinter.dialog', 'C:\\Python312\\Lib\\tkinter\\dialog.py', 'PYMODULE'),
('tkinter.filedialog',
'C:\\Python312\\Lib\\tkinter\\filedialog.py',
'PYMODULE'),
('tkinter.messagebox',
'C:\\Python312\\Lib\\tkinter\\messagebox.py',
'PYMODULE'),
('tkinter.simpledialog',
'C:\\Python312\\Lib\\tkinter\\simpledialog.py',
'PYMODULE'),
('token', 'C:\\Python312\\Lib\\token.py', 'PYMODULE'),
('tokenize', 'C:\\Python312\\Lib\\tokenize.py', 'PYMODULE'),
('tracemalloc', 'C:\\Python312\\Lib\\tracemalloc.py', 'PYMODULE'),
('tty', 'C:\\Python312\\Lib\\tty.py', 'PYMODULE'),
('typing', 'C:\\Python312\\Lib\\typing.py', 'PYMODULE'),
('unittest', 'C:\\Python312\\Lib\\unittest\\__init__.py', 'PYMODULE'),
('unittest._log', 'C:\\Python312\\Lib\\unittest\\_log.py', 'PYMODULE'),
('unittest.async_case',
'C:\\Python312\\Lib\\unittest\\async_case.py',
'PYMODULE'),
('unittest.case', 'C:\\Python312\\Lib\\unittest\\case.py', 'PYMODULE'),
('unittest.loader', 'C:\\Python312\\Lib\\unittest\\loader.py', 'PYMODULE'),
('unittest.main', 'C:\\Python312\\Lib\\unittest\\main.py', 'PYMODULE'),
('unittest.result', 'C:\\Python312\\Lib\\unittest\\result.py', 'PYMODULE'),
('unittest.runner', 'C:\\Python312\\Lib\\unittest\\runner.py', 'PYMODULE'),
('unittest.signals', 'C:\\Python312\\Lib\\unittest\\signals.py', 'PYMODULE'),
('unittest.suite', 'C:\\Python312\\Lib\\unittest\\suite.py', 'PYMODULE'),
('unittest.util', 'C:\\Python312\\Lib\\unittest\\util.py', 'PYMODULE'),
('urllib', 'C:\\Python312\\Lib\\urllib\\__init__.py', 'PYMODULE'),
('urllib.error', 'C:\\Python312\\Lib\\urllib\\error.py', 'PYMODULE'),
('urllib.parse', 'C:\\Python312\\Lib\\urllib\\parse.py', 'PYMODULE'),
('urllib.request', 'C:\\Python312\\Lib\\urllib\\request.py', 'PYMODULE'),
('urllib.response', 'C:\\Python312\\Lib\\urllib\\response.py', 'PYMODULE'),
('webbrowser', 'C:\\Python312\\Lib\\webbrowser.py', 'PYMODULE'),
('xml', 'C:\\Python312\\Lib\\xml\\__init__.py', 'PYMODULE'),
('xml.parsers', 'C:\\Python312\\Lib\\xml\\parsers\\__init__.py', 'PYMODULE'),
('xml.parsers.expat',
'C:\\Python312\\Lib\\xml\\parsers\\expat.py',
'PYMODULE'),
('xml.sax', 'C:\\Python312\\Lib\\xml\\sax\\__init__.py', 'PYMODULE'),
('xml.sax._exceptions',
'C:\\Python312\\Lib\\xml\\sax\\_exceptions.py',
'PYMODULE'),
('xml.sax.expatreader',
'C:\\Python312\\Lib\\xml\\sax\\expatreader.py',
'PYMODULE'),
('xml.sax.handler', 'C:\\Python312\\Lib\\xml\\sax\\handler.py', 'PYMODULE'),
('xml.sax.saxutils', 'C:\\Python312\\Lib\\xml\\sax\\saxutils.py', 'PYMODULE'),
('xml.sax.xmlreader',
'C:\\Python312\\Lib\\xml\\sax\\xmlreader.py',
'PYMODULE'),
('xmlrpc', 'C:\\Python312\\Lib\\xmlrpc\\__init__.py', 'PYMODULE'),
('xmlrpc.client', 'C:\\Python312\\Lib\\xmlrpc\\client.py', 'PYMODULE'),
('zipfile', 'C:\\Python312\\Lib\\zipfile\\__init__.py', 'PYMODULE'),
('zipfile._path',
'C:\\Python312\\Lib\\zipfile\\_path\\__init__.py',
'PYMODULE'),
('zipfile._path.glob',
'C:\\Python312\\Lib\\zipfile\\_path\\glob.py',
'PYMODULE'),
('zipimport', 'C:\\Python312\\Lib\\zipimport.py', 'PYMODULE')])

View File

@ -0,0 +1,223 @@
This file lists modules PyInstaller was not able to find. This does not
necessarily mean these modules are required for running your program. Both
Python's standard library and 3rd-party Python packages often conditionally
import optional modules, some of which may be available only on certain
platforms.
Types of import:
* top-level: imported at the top-level - look at these first
* conditional: imported within an if-statement
* delayed: imported within a function
* optional: imported within a try-except-statement
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks!
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), http.server (delayed, optional), netrc (delayed, conditional), getpass (delayed)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional)
missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed)
missing module named fcntl - imported by subprocess (optional)
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named termios - imported by tty (top-level), getpass (optional)
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional)
missing module named resource - imported by posix (top-level)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named pyimod02_importers - imported by C:\ZendionInc\agrobot_base\Python\OAK\datasets\oak-fcc-3\MaskReviewer_package\.build_venv\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named _dummy_thread - imported by numpy._core.arrayprint (optional)
missing module named typing_extensions - imported by numpy._typing._nested_sequence (conditional), numpy.random.bit_generator (top-level)
missing module named charset_normalizer - imported by numpy.f2py.crackfortran (optional)
missing module named vms_lib - imported by platform (delayed, optional)
missing module named 'java.lang' - imported by platform (delayed, optional)
missing module named java - imported by platform (delayed)
missing module named _winreg - imported by platform (delayed, optional)
missing module named psutil - imported by numpy.testing._private.utils (delayed, optional)
missing module named readline - imported by cmd (delayed, conditional, optional), code (delayed, conditional, optional), pdb (delayed, optional)
missing module named win32pdh - imported by numpy.testing._private.utils (delayed, conditional)
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named _typeshed - imported by numpy.random._common (top-level), numpy.random.bit_generator (top-level)
missing module named threadpoolctl - imported by numpy.lib._utils_impl (delayed, optional)
missing module named numpy._core.zeros - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.void - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.vecmat - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.vecdot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.ushort - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.unsignedinteger - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ulonglong - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ulong - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uintp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uintc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uint64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ubyte - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.trunc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.true_divide - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.transpose - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._function_base_impl (top-level), numpy (conditional)
missing module named numpy._core.trace - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.timedelta64 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.tensordot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.tanh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.tan - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.swapaxes - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.sum - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.subtract - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.str_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.square - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.sqrt - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.spacing - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.sort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.sinh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.single - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.signedinteger - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.signbit - imported by numpy._core (conditional), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.sign - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.short - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.rint - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.right_shift - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.remainder - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.reciprocal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.radians - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.rad2deg - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.prod - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.power - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.positive - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.pi - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.outer - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.ones - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.object_ - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.number - imported by numpy._core (conditional), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.not_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.nextafter - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.newaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.negative - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ndarray - imported by numpy._core (top-level), numpy.lib._utils_impl (top-level), numpy (conditional), numpy.testing._private.utils (top-level)
missing module named numpy._core.multiply - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.moveaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.modf - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.mod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.minimum - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.maximum - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.matvec - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.matrix_transpose - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.matmul - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.longlong - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.longdouble - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.long - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_xor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_or - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_not - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_and - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logaddexp2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logaddexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log10 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log1p - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.linspace - imported by numpy._core (top-level), numpy.lib._index_tricks_impl (top-level), numpy (conditional)
missing module named numpy._core.less_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.less - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.left_shift - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ldexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.lcm - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.isscalar - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.isnan - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.isfinite - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.intp - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy._array_api_info (top-level), numpy.testing._private.utils (top-level)
missing module named numpy._core.integer - imported by numpy._core (conditional), numpy (conditional), numpy.fft._helper (top-level)
missing module named numpy._core.intc - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.int64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int8 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.inf - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.inexact - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.iinfo - imported by numpy._core (top-level), numpy.lib._twodim_base_impl (top-level), numpy (conditional)
missing module named numpy._core.hypot - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.hstack - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.heaviside - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.half - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.greater_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.greater - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.gcd - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.frompyfunc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.frexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmin - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmax - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floor_divide - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floating - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.float_power - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.float32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level), numpy.testing._private.utils (top-level)
missing module named numpy._core.float16 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.finfo - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.fabs - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.expm1 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.exp2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.exp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.euler_gamma - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.errstate - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.empty_like - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.empty - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._helper (top-level), numpy.testing._private.utils (top-level)
missing module named numpy._core.e - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.double - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.dot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.divmod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.divide - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.diagonal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.degrees - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.deg2rad - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.datetime64 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.csingle - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.cross - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.count_nonzero - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.cosh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cos - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.copysign - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.conjugate - imported by numpy._core (conditional), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.conj - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.complexfloating - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.complex64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.clongdouble - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.character - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ceil - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cdouble - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.cbrt - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bytes_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.byte - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bool_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_xor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_or - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_count - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_and - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.atleast_3d - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
missing module named numpy._core.atleast_2d - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.atleast_1d - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.asarray - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._array_utils_impl (top-level), numpy (conditional), numpy.fft._helper (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy._core.asanyarray - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.array - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional), numpy.testing._private.utils (top-level)
missing module named numpy._core.argsort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.arctanh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctan2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctan - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arcsinh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arcsin - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arccosh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arccos - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.amin - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.amax - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.all - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.testing._private.utils (delayed)
missing module named numpy._core.add - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named yaml - imported by numpy.__config__ (delayed)
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional)

View File

@ -0,0 +1,81 @@
@echo off
setlocal
cd /d "%~dp0"
echo ============================================================
echo BUILD MASKREVIEWER WINDOWS
echo ============================================================
echo.
where py >nul 2>nul
if %errorlevel%==0 (
set PY=py
) else (
set PY=python
)
%PY% --version
if errorlevel 1 (
echo.
echo [ERRO] Python nao encontrado.
echo Instale Python 3.11 ou 3.12 para Windows e tente novamente.
pause
exit /b 1
)
if not exist ".build_venv\Scripts\python.exe" (
echo [1/4] Criando ambiente de build...
%PY% -m venv .build_venv
if errorlevel 1 goto :erro
)
call ".build_venv\Scripts\activate.bat"
echo [2/4] Instalando dependencias de build...
python -m pip install --upgrade pip
python -m pip install pyinstaller numpy opencv-python
if errorlevel 1 goto :erro
echo [3/4] Limpando builds antigos...
if exist build rmdir /s /q build
if exist dist rmdir /s /q dist
if exist MaskReviewer.spec del /q MaskReviewer.spec
echo [4/4] Gerando EXE...
python -m PyInstaller ^
--noconfirm ^
--clean ^
--onedir ^
--windowed ^
--name MaskReviewer ^
--collect-all cv2 ^
MaskReviewer.py
if errorlevel 1 goto :erro
echo.
echo ============================================================
echo BUILD CONCLUIDO
echo ============================================================
echo Pasta gerada:
echo %CD%\dist\MaskReviewer
echo.
echo Coloque ao lado do MaskReviewer.exe:
echo labelmap.txt
echo group\
echo.
echo Estrutura final recomendada:
echo MaskReviewer\
echo MaskReviewer.exe
echo _internal\
echo labelmap.txt
echo group\
echo.
pause
exit /b 0
:erro
echo.
echo [ERRO] Falha no build.
pause
exit /b 1

View File

@ -0,0 +1,19 @@
@echo off
setlocal
cd /d "%~dp0"
where py >nul 2>nul
if %errorlevel%==0 (
set PY=py
) else (
set PY=python
)
%PY% -c "import cv2, numpy" >nul 2>nul
if errorlevel 1 (
echo Instalando numpy e opencv-python...
%PY% -m pip install numpy opencv-python
)
%PY% MaskReviewer.py
if errorlevel 1 pause

View File

@ -8,7 +8,8 @@ Exporta o checkpoint PyTorch do SegFormer Multi-Head OAK-FCC-3 para ONNX.
Exemplo: Exemplo:
python _10_export_onnx.py --config config.json --checkpoint backup/segformer_b1/2026_08_17/stacked_raw5_ndvi_ndre/best_score.pt --out backup/segformer_b1/2026_08_17/stacked_raw5_ndvi_ndre/best_score.onnx --include-norm --postprocess argmax_fullres python .\_10_export_onnx.py --config config.json --checkpoint backup/segformer_b1/2026_08_17/stacked_raw5_ndvi_ndre/best_score.pt --out backup/segformer_b1/2026_08_17/stacked_raw5_ndvi_ndre/best_score.onnx --include-norm --postprocess argmax_fullres
python .\_10_export_onnx.py --config .\config.json --train-script .\_8_train_multihead_v2.py --opset 17 --device cuda --include-norm --postprocess argmax_fullres
""" """
from __future__ import annotations from __future__ import annotations

View File

@ -13,6 +13,7 @@ para o SegFormer OAK-FCC-3 Multi-Head.
Exemplo: Exemplo:
python _11_validate_onnx.py --config config.json --max_samples 20 --device cuda --onnx_provider cuda --torch_no_amp python _11_validate_onnx.py --config config.json --max_samples 20 --device cuda --onnx_provider cuda --torch_no_amp
python .\_11_validate_onnx.py --config .\config.json --train-script .\_8_train_multihead_v2.py --max_samples 50 --device cuda --onnx_provider cuda --torch_no_amp
Para validar o ONNX com saída já redimensionada: Para validar o ONNX com saída já redimensionada:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -25,10 +25,7 @@ Mostra:
Exemplo: Exemplo:
python .\\_9_test_infer_multihead.py ^ python .\\_9_test_multihead.py --config config.json --split_folder val --ckpt backup\segformer_b1\test_multi\stacked_raw5_multihead\best_score.pt
--config config.json ^
--split_folder val ^
--ckpt backup\segformer_b1\test_multi\stacked_raw5_multihead\best_score.pt
Controles: Controles:
D / seta direita : próxima amostra D / seta direita : próxima amostra
@ -634,7 +631,20 @@ def collect_samples(root: Path, heads_config: Dict[str, dict], require_masks: bo
missing = [] missing = []
for head_name, hcfg in heads_config.items(): for head_name, hcfg in heads_config.items():
mask_dir = group_dir / str(hcfg.get("mask_dir", "masks")) mask_dir_name = str(hcfg.get("mask_dir", "masks"))
is_derived = (
bool(hcfg.get("derived", False))
or mask_dir_name == "__derived_target__"
)
# Heads derivadas não possuem máscara física.
# O GT target será construído posteriormente a partir de:
# vegetation == 1 AND cana == 0.
if is_derived:
masks[head_name] = None
continue
mask_dir = group_dir / mask_dir_name
mask_path = mask_dir / f"{base}.npy" mask_path = mask_dir / f"{base}.npy"
if mask_path.exists(): if mask_path.exists():
@ -2002,14 +2012,25 @@ def main():
sample_metrics[head_name] = {"iou": iou, "miou": miou, "acc": acc} sample_metrics[head_name] = {"iou": iou, "miou": miou, "acc": acc}
metric_lines.append(f"{head_name}: mIoU={miou:.3f} acc={acc:.3f}") metric_lines.append(f"{head_name}: mIoU={miou:.3f} acc={acc:.3f}")
if gt_target is not None: if gt_target is not None and pred_target_op is not None:
cm_t = confusion_matrix_np(pred_target, gt_target, 2, ignore_id) cm_t = confusion_matrix_np(pred_target_op, gt_target, 2, ignore_id)
iou_t, miou_t, acc_t = metrics_from_cm(cm_t) iou_t, miou_t, acc_t = metrics_from_cm(cm_t)
sample_metrics["target_op"] = {"iou": iou_t, "miou": miou_t, "acc": acc_t}
metric_lines.append(f"target_op: IoU_alvo={iou_t[1]:.3f} acc={acc_t:.3f}") sample_metrics["target_op"] = {
"iou": iou_t,
"miou": miou_t,
"acc": acc_t,
}
metric_lines.append(
f"target_op: IoU_alvo={iou_t[1]:.3f} acc={acc_t:.3f}"
)
if "target" in sample_metrics: if "target" in sample_metrics:
iou_head = sample_metrics["target"]["iou"] iou_head = sample_metrics["target"]["iou"]
metric_lines.append(f"target_head: IoU_alvo={iou_head[1]:.3f}") metric_lines.append(
f"target_head: IoU_alvo={iou_head[1]:.3f}"
)
if idx not in visited: if idx not in visited:
for head_name, pred in preds.items(): for head_name, pred in preds.items():

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,18 @@
{ {
"camera": "oak-fcc-3", "camera": "oak-fcc-3",
"modelo": "segformer_b1", "modelo": "segformer_b1",
"model_name": "2026_08_18", "model_name": "2026_08_28_linear_demosaic",
"main_class_name": "erva", "main_class_name": "erva",
"es_classes": "", "es_classes": "",
"model_to_use": "geral", "model_to_use": "geral",
"raw_size": [1280, 800], "raw_size": [1280, 800],
"resolucao": [640, 400], "resolucao": [960, 600],
"roi_inicio": 0.0, "roi_inicio": 0.0,
"roi_tamanho": 1.0, "roi_tamanho": 1.0,
"shaves": 3, "shaves": 3,
"source_channels": ["R", "G", "B", "RE", "NIR"], "source_channels": ["R", "G", "B", "RE", "NIR"],
"channels": 7, "channels": 5,
"input_channels": ["R", "G", "B", "RE", "NIR", "NDVI", "NDRE"], "input_channels": ["R", "G", "B", "RE", "NIR"],
"derived_channels": { "derived_channels": {
"epsilon": 1e-6, "epsilon": 1e-6,
"clip_min": -1.0, "clip_min": -1.0,
@ -20,7 +20,7 @@
}, },
"backbone": "nvidia/mit-b1", "backbone": "nvidia/mit-b1",
"fusion_mode": "stacked", "fusion_mode": "stacked",
"stats_source_tag": "stacked_raw5_ndvi_ndre", "stats_source_tag": "stacked_raw5",
"module_params_json": "calibration/module_params.json", "module_params_json": "calibration/module_params.json",
"ckpt_test": "best_score", "ckpt_test": "best_score",
"multi_head": true, "multi_head": true,
@ -41,7 +41,7 @@
"mask_dir": "masks_vegetation", "mask_dir": "masks_vegetation",
"classes": {"background": 0, "vegetation": 1}, "classes": {"background": 0, "vegetation": 1},
"ignore_index": 255, "ignore_index": 255,
"loss_weight": 0.20 "loss_weight": 0.15
}, },
"cana": { "cana": {
"enabled": true, "enabled": true,
@ -50,7 +50,7 @@
"mask_dir": "masks_cana", "mask_dir": "masks_cana",
"classes": {"not_cana": 0, "cana": 1}, "classes": {"not_cana": 0, "cana": 1},
"ignore_index": 255, "ignore_index": 255,
"loss_weight": 0.25 "loss_weight": 0.35
}, },
"target": { "target": {
"enabled": true, "enabled": true,
@ -59,23 +59,158 @@
"mask_dir": "__derived_target__", "mask_dir": "__derived_target__",
"classes": {"background": 0, "target": 1}, "classes": {"background": 0, "target": 1},
"ignore_index": 255, "ignore_index": 255,
"loss_weight": 0.35, "loss_weight": 0.30,
"derived": true "derived": true
} }
}, },
"target_distillation": { "training_v2": {
"enabled": true, "model": {
"hard_weight": 0.85, "decoder_mode": "shared_light",
"distill_weight": 0.15, "spectral_input_init": "zero_extra"
"rampup_enabled": true, },
"start_epoch": 8, "augmentation": {
"rampup_epochs": 12, "enabled": true,
"w_sem_erva": 0.45, "horizontal_flip_p": 0.5,
"w_veg_not_cana": 0.35, "vertical_flip_p": 0.0,
"w_veg_suppressed": 0.20, "affine_p": 0.7,
"cana_suppression_power": 1.5, "rotate_deg": 5.0,
"teacher_min": 0.0, "scale_min": 0.9,
"teacher_max": 1.0, "scale_max": 1.1,
"detach_teacher": true "translate_frac": 0.04,
"crop_p": 0.45,
"crop_scale_min": 0.7,
"crop_scale_max": 1.0,
"crop_focus_target_p": 0.55,
"crop_focus_cana_p": 0.25,
"global_gain_p": 0.35,
"global_gain_min": 0.92,
"global_gain_max": 1.08,
"band_gain_p": 0.25,
"band_gain_min": 0.96,
"band_gain_max": 1.04,
"rgb_gamma_p": 0.2,
"rgb_gamma_min": 0.94,
"rgb_gamma_max": 1.06,
"noise_p": 0.2,
"noise_sigma_min": 0.001,
"noise_sigma_max": 0.008,
"blur_p": 0.12,
"blur_kernel": 3,
"sensor_channel_dropout_p": 0.0,
"clip_physical": true
},
"sampler": {
"mode": "diverse",
"samples_per_epoch": 0,
"tiny_target_pct": 0.005,
"small_target_pct": 0.02,
"medium_target_pct": 0.1
},
"class_weighting": {
"method": "log_inverse",
"log_offset": 1.02,
"power": 0.5,
"min_weight": 0.25,
"max_weight": 4.0
},
"loss": {
"dice_reduction": "per_image",
"dice_smooth": 1.0,
"boundary_weight": 0.0,
"ohem_ratio": 0.0,
"safety": {
"enabled": true,
"weight": 0.08,
"cana_weight": 1.0,
"ground_weight": 0.2
}
},
"optimizer": {
"encoder_lr": null,
"patch_lr_mult": 2.0,
"heads_lr_mult": 5.0,
"weight_decay": null,
"no_decay_bias": true,
"no_decay_norm": true,
"betas": [
0.9,
0.999
],
"eps": 1e-08
},
"scheduler": {
"mode": "poly",
"warmup_ratio": 0.05,
"warmup_start_factor": 0.1,
"poly_power": 1.0,
"min_lr_ratio": 0.02
},
"optimization": {
"grad_clip_norm": 1.0,
"matmul_precision": "high",
"cudnn_benchmark": true,
"persistent_workers": true,
"prefetch_factor": 2
},
"target_distillation": {
"enabled": false,
"mode": "cross_head",
"start_epoch": 8,
"rampup_epochs": 12,
"hard_weight": 0.75,
"distill_weight": 0.25,
"teacher_confidence_min": 0.6,
"detach_teacher": true,
"w_sem_erva": 0.45,
"w_veg_not_cana": 0.35,
"w_veg_suppressed": 0.2,
"cana_suppression_power": 1.5
},
"metrics": {
"target_thresholds": [
0.3,
0.4,
0.5,
0.6,
0.7,
0.8,
0.9
],
"ece_bins": 15,
"scenario_metrics": true,
"group_metrics": true,
"rich_train_metrics": false,
"operational_threshold": {
"max_cana_spray_rate": 0.02,
"max_ground_spray_rate": 0.03,
"max_weed_miss_rate": 0.2,
"score_weights": {
"target_iou": 0.35,
"target_f1": 0.2,
"cana_safety": 0.25,
"ground_safety": 0.1,
"weed_recall": 0.1
}
}
},
"selection_score": {
"target_iou": 0.4,
"cana_iou": 0.2,
"target_f1": 0.1,
"vegetation_miou": 0.1,
"semantic_miou": 0.05,
"cana_safety": 0.15
},
"checkpoint": {
"early_stop_min_delta": 0.0005,
"save_best_safety": true,
"save_best_legacy": true,
"save_best_operational": true
},
"data": {
"validate_npy_content": true,
"skip_corrupt_samples": true,
"max_corrupt_fraction": 0.005
}
} }
} }

View File

@ -1,620 +0,0 @@
import argparse
import json
import sys
import time
from pathlib import Path
import cv2
import numpy as np
try:
import torch
except Exception:
torch = None
# ============================================================
# Ajuste de import local
# ============================================================
THIS_FILE = Path(__file__).resolve()
# Esperado:
# .../Python/Scripts/workers/camera_worker/oak_fcc3_core/benchmark_raw_bruto_scientific.py
WORKERS_DIR = THIS_FILE.parents[2]
if str(WORKERS_DIR) not in sys.path:
sys.path.insert(0, str(WORKERS_DIR))
from camera_worker.oak_fcc3_core.oak_fcc3_client import OakFcc3Client
try:
from camera_worker.oak_fcc3_core.segformer_service import MultiSpecSegformerService
except Exception:
MultiSpecSegformerService = None
# ============================================================
# Utils
# ============================================================
def now_ms():
return time.perf_counter() * 1000.0
def mean(xs):
return float(np.mean(xs)) if xs else 0.0
def p95(xs):
return float(np.percentile(xs, 95)) if xs else 0.0
def maxv(xs):
return float(np.max(xs)) if xs else 0.0
def last_mean(xs, n=30):
return float(np.mean(xs[-n:])) if xs else 0.0
def last_max(xs, n=30):
return float(np.max(xs[-n:])) if xs else 0.0
def load_json_if_exists(path):
if not path:
return None
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def parse_float_list(s, expected=5, default=None):
if s is None:
return default
vals = [float(x.strip()) for x in str(s).split(",") if x.strip() != ""]
if len(vals) != expected:
raise ValueError(f"Esperado {expected} valores, veio {len(vals)}: {s}")
return np.array(vals, dtype=np.float32)
def extract_mean_std_from_model_config(cfg):
if not isinstance(cfg, dict):
return None, None
mean_cfg = cfg.get("mean") or cfg.get("channel_mean") or cfg.get("norm_mean")
std_cfg = cfg.get("std") or cfg.get("channel_std") or cfg.get("norm_std")
norm = cfg.get("normalization") or cfg.get("norm") or {}
if mean_cfg is None and isinstance(norm, dict):
mean_cfg = norm.get("mean") or norm.get("channel_mean")
if std_cfg is None and isinstance(norm, dict):
std_cfg = norm.get("std") or norm.get("channel_std")
if mean_cfg is None or std_cfg is None:
return None, None
mean_arr = np.array(mean_cfg, dtype=np.float32)
std_arr = np.array(std_cfg, dtype=np.float32)
if mean_arr.size != 5 or std_arr.size != 5:
return None, None
return mean_arr, std_arr
def tensor_stats(tensor):
names = ["R", "G", "B", "RE", "NIR"]
out = {}
for i, name in enumerate(names):
ch = tensor[i].astype(np.float32)
out[name] = {
"min": float(np.min(ch)),
"p01": float(np.percentile(ch, 1)),
"p50": float(np.percentile(ch, 50)),
"p99": float(np.percentile(ch, 99)),
"max": float(np.max(ch)),
"mean": float(np.mean(ch)),
"std": float(np.std(ch)),
}
return out
def print_tensor_stats(label, tensor):
print("============================================")
print(f"[{label}] TENSOR")
print(f"shape={tensor.shape} dtype={tensor.dtype}")
stats = tensor_stats(tensor)
for ch, s in stats.items():
print(
f"{ch:>3} | "
f"min={s['min']:.4f} "
f"p01={s['p01']:.4f} "
f"p50={s['p50']:.4f} "
f"p99={s['p99']:.4f} "
f"max={s['max']:.4f} "
f"mean={s['mean']:.4f} "
f"std={s['std']:.4f}"
)
print("============================================")
def to_u8_01(arr):
arr = np.asarray(arr, dtype=np.float32)
arr = np.nan_to_num(arr, nan=0.0, posinf=1.0, neginf=0.0)
arr = np.clip(arr, 0.0, 1.0)
return (arr * 255.0).astype(np.uint8)
def make_panel(tensor):
rgb = np.stack([tensor[0], tensor[1], tensor[2]], axis=2)
rgb_bgr = cv2.cvtColor(to_u8_01(rgb), cv2.COLOR_RGB2BGR)
re_bgr = cv2.cvtColor(to_u8_01(tensor[3]), cv2.COLOR_GRAY2BGR)
nir_bgr = cv2.cvtColor(to_u8_01(tensor[4]), cv2.COLOR_GRAY2BGR)
false_rgb = np.stack(
[
tensor[4], # visual R = NIR
tensor[3], # visual G = RE
tensor[0], # visual B = R real
],
axis=2,
)
false_bgr = cv2.cvtColor(to_u8_01(false_rgb), cv2.COLOR_RGB2BGR)
def title(img, text):
h, w = img.shape[:2]
bar_h = 34
bar = np.zeros((bar_h, w, 3), dtype=np.uint8)
cv2.putText(
bar,
text,
(10, 24),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
(255, 255, 255),
2,
cv2.LINE_AA,
)
return np.vstack([bar, img])
rgb_bgr = title(rgb_bgr, "RGB")
re_bgr = title(re_bgr, "RE")
nir_bgr = title(nir_bgr, "NIR")
false_bgr = title(false_bgr, "Falso color NIR/RE/R")
top = np.hstack([rgb_bgr, re_bgr])
bottom = np.hstack([nir_bgr, false_bgr])
return np.vstack([top, bottom])
# ============================================================
# Benchmark processor
# ============================================================
class ScientificBenchmark:
def __init__(self, args):
self.args = args
self.target_size = (int(args.width), int(args.height))
self.model_cfg = load_json_if_exists(args.model_config_json)
mean_cfg, std_cfg = extract_mean_std_from_model_config(self.model_cfg)
if args.model_mean is not None:
self.mean = parse_float_list(args.model_mean, expected=5)
elif mean_cfg is not None:
self.mean = mean_cfg
else:
self.mean = np.array([0.5, 0.5, 0.5, 0.5, 0.5], dtype=np.float32)
if args.model_std is not None:
self.std = parse_float_list(args.model_std, expected=5)
elif std_cfg is not None:
self.std = std_cfg
else:
self.std = np.array([0.25, 0.25, 0.25, 0.25, 0.25], dtype=np.float32)
self.mean_chw = self.mean[:, None, None].astype(np.float32)
self.std_chw = self.std[:, None, None].astype(np.float32)
self.device = None
if torch is not None and torch.cuda.is_available():
self.device = torch.device("cuda")
elif torch is not None:
self.device = torch.device("cpu")
self.model_svc = None
if args.run_model:
if MultiSpecSegformerService is None:
raise RuntimeError("MultiSpecSegformerService não pôde ser importado.")
if not isinstance(self.model_cfg, dict):
raise RuntimeError("--run_model requer --model_config_json válido.")
self.model_svc = MultiSpecSegformerService(
model_config=self.model_cfg,
mostrar_log=print,
)
dummy = np.zeros((5, args.height, args.width), dtype=np.float32)
for _ in range(max(0, int(args.warmup_model))):
self.model_svc.infer_tensor_fast(dummy, keep_probs=False)
print(f"[BENCH] Warmup modelo concluído: {args.warmup_model}x")
def process_once(self, client):
"""
Mede uma iteração completa do fluxo científico.
"""
times = {
"capture_ms": 0.0,
"decode_ms": 0.0,
"controller_ms": 0.0,
"fuse_total_ms": 0.0,
"fuse_dark_ms": 0.0,
"fuse_radnorm_ms": 0.0,
"fuse_flat_ms": 0.0,
"fuse_prepare_ms": 0.0,
"fuse_warp_ms": 0.0,
"fuse_crop_resize_ms": 0.0,
"fuse_concat_ms": 0.0,
"model_norm_ms": 0.0,
"torch_ms": 0.0,
"infer_ms": 0.0,
"total_ms": 0.0,
"sync_dt_ms": 0.0,
}
t_total0 = now_ms()
# ========================================================
# 1. Captura RAW_BRUTO
# ========================================================
t0 = now_ms()
raw_frame, raw_meta = client.get_next_raw_frame(timeout=self.args.timeout)
times["capture_ms"] = now_ms() - t0
#cp = raw_meta.get("capture_perf", {})
#print(
# "[CAP_ASYNC] "
# f"get_wait={cp.get('async_get_wait_ms',0):.2f}ms "
# f"age={cp.get('async_packet_age_ms',0):.2f}ms "
# f"seq={cp.get('async_packet_seq')} "
# f"thread_wait={cp.get('wait_total_ms',0):.2f}ms "
# f"sleep={cp.get('sleep_ms',0):.2f}ms/{cp.get('sleep_count',0)} "
# f"drain={cp.get('drain_total_ms',0):.2f}ms "
# f"copy={cp.get('drain_frombuffer_copy_ms',0):.2f}ms "
# f"status={cp.get('async_status',{})}"
#)
times["sync_dt_ms"] = float(raw_meta.get("sync_dt_ms", 0.0) or 0.0)
# ========================================================
# 2. Decode RAW10 packed -> float científico por câmera
# ========================================================
t0 = now_ms()
decoded = client.decode_stream_cameras(raw_frame, raw_meta)
times["decode_ms"] = now_ms() - t0
# ========================================================
# 3. RadiometricController update, se ativo
# Isso NÃO é a radiometric_normalization do tensor.
# É o controlador de exposição/ganho.
# ========================================================
t0 = now_ms()
client.update_radiometry(decoded, raw_meta)
times["controller_ms"] = now_ms() - t0
# ========================================================
# 4. Fusão científica no RawProcessorCore
# dark + radnorm + flat + homografia + crop/resize + concat
# ========================================================
t0 = now_ms()
tensor = client.build_infer_tensor_from_decoded(
decoded=decoded,
meta=raw_meta,
channels_expected=5,
target_size=self.target_size,
)
times["fuse_total_ms"] = now_ms() - t0
# Pega detalhamento interno do core
try:
perf = (client.core.last_fusion_result or {}).get("perf", {}) or {}
times["fuse_dark_ms"] = float(perf.get("dark_ms", 0.0) or 0.0)
times["fuse_radnorm_ms"] = float(perf.get("radnorm_ms", 0.0) or 0.0)
times["fuse_flat_ms"] = float(perf.get("flat_ms", 0.0) or 0.0)
times["fuse_prepare_ms"] = float(perf.get("prepare_ms", 0.0) or 0.0)
times["fuse_warp_ms"] = float(perf.get("warp_total_ms", 0.0) or 0.0)
times["fuse_crop_resize_ms"] = float(perf.get("crop_resize_ms", 0.0) or 0.0)
times["fuse_concat_ms"] = float(perf.get("concat_ms", 0.0) or 0.0)
except Exception:
pass
# ========================================================
# 5. Normalização do modelo, opcional
# ========================================================
if self.args.simulate_model_norm:
t0 = now_ms()
tensor = (tensor - self.mean_chw) / np.maximum(self.std_chw, 1e-6)
tensor = np.ascontiguousarray(tensor, dtype=np.float32)
times["model_norm_ms"] = now_ms() - t0
# ========================================================
# 6. Transferência para torch/cuda, opcional
# ========================================================
if self.args.to_torch:
if torch is None:
raise RuntimeError("--to_torch requer torch instalado.")
t0 = now_ms()
x = torch.from_numpy(tensor).unsqueeze(0).to(self.device, non_blocking=True)
if self.device is not None and self.device.type == "cuda":
torch.cuda.synchronize()
times["torch_ms"] = now_ms() - t0
# ========================================================
# 7. Inferência real, opcional
# ========================================================
pred = None
if self.args.run_model:
t0 = now_ms()
pred = self.model_svc.infer_tensor_fast(tensor, keep_probs=False)
times["infer_ms"] = now_ms() - t0
times["total_ms"] = now_ms() - t_total0
return tensor, pred, raw_frame, raw_meta, decoded, times
# ============================================================
# Main
# ============================================================
def main():
ap = argparse.ArgumentParser(
description="Benchmark científico OAK-FCC-3 RAW_BRUTO -> tensor multiespectral final."
)
ap.add_argument(
"--module_params",
default=r"C:\ZendionInc\agrobot_base\Python\OAK\datasets\oak-fcc-3\calibration\module_params.json",
help="Caminho do module_params.json.",
)
ap.add_argument("--width", type=int, default=1024, help="Largura final do tensor.")
ap.add_argument("--height", type=int, default=640, help="Altura final do tensor.")
ap.add_argument("--fps", type=float, default=30.0)
ap.add_argument("--seconds", type=float, default=20.0)
ap.add_argument("--timeout", type=float, default=3.0)
ap.add_argument("--warmup", type=int, default=5)
ap.add_argument("--mx_id", default=None)
ap.add_argument("--sync_tolerance_ms", type=float, default=25.0)
ap.add_argument("--buffer_size", type=int, default=8)
ap.add_argument("--simulate_model_norm", action="store_true")
ap.add_argument("--model_mean", default=None)
ap.add_argument("--model_std", default=None)
ap.add_argument("--to_torch", action="store_true")
ap.add_argument("--run_model", action="store_true")
ap.add_argument("--model_config_json", default=None)
ap.add_argument("--warmup_model", type=int, default=3)
ap.add_argument("--save_debug", action="store_true")
ap.add_argument("--debug_dir", default="raw_bruto_scientific_benchmark")
ap.add_argument("--show", action="store_true")
ap.add_argument("--display_scale", type=float, default=0.65)
args = ap.parse_args()
bench = ScientificBenchmark(args)
client = OakFcc3Client(
width=args.width,
height=args.height,
fps=args.fps,
frame_type="RAW_BRUTO",
output_dtype="uint8",
capture_mode="TRIPLE",
raw_policy="require_triple",
module_calibration_json=args.module_params,
sync_tolerance_ms=args.sync_tolerance_ms,
buffer_size=args.buffer_size,
mx_id=args.mx_id,
)
client.core.warmup_numba_raw10_decode()
samples = {
"capture_ms": [],
"decode_ms": [],
"controller_ms": [],
"fuse_total_ms": [],
"fuse_dark_ms": [],
"fuse_radnorm_ms": [],
"fuse_flat_ms": [],
"fuse_prepare_ms": [],
"fuse_warp_ms": [],
"fuse_crop_resize_ms": [],
"fuse_concat_ms": [],
"model_norm_ms": [],
"torch_ms": [],
"infer_ms": [],
"total_ms": [],
"sync_dt_ms": [],
}
n_frames = 0
t_start = time.perf_counter()
t_last_log = t_start
last_tensor = None
last_meta = None
try:
client.start(print_debug=True)
# Warmup de câmera/controlador/filas
print(f"[BENCH] Warmup frames: {args.warmup}")
for _ in range(max(0, int(args.warmup))):
try:
bench.process_once(client)
except Exception as e:
print(f"[WARN] warmup falhou: {type(e).__name__}: {e}")
time.sleep(0.02)
print("============================================")
print("[BENCH] Iniciando benchmark científico RAW_BRUTO")
print(f"target tensor : (5,{args.height},{args.width})")
print(f"duration : {args.seconds}s")
print("============================================")
while True:
now = time.perf_counter()
elapsed = now - t_start
if elapsed >= args.seconds:
break
try:
tensor, pred, raw_frame, raw_meta, decoded, times = bench.process_once(client)
except TimeoutError as e:
print(f"[TIMEOUT] {e}")
continue
n_frames += 1
last_tensor = tensor
last_meta = raw_meta
for k in samples:
samples[k].append(float(times.get(k, 0.0) or 0.0))
if now - t_last_log >= 1.0:
elapsed = now - t_start
fps = n_frames / max(elapsed, 1e-6)
print(
"[RAW_SCI_PERF] "
f"elapsed={elapsed:.1f}s "
f"frames={n_frames} "
f"fps={fps:.2f} "
f"sync={last_mean(samples['sync_dt_ms']):.2f}ms "
f"capture={last_mean(samples['capture_ms']):.2f}ms "
f"decode={last_mean(samples['decode_ms']):.2f}ms "
f"controller={last_mean(samples['controller_ms']):.2f}ms "
f"fuse={last_mean(samples['fuse_total_ms']):.2f}ms "
f"radnorm={last_mean(samples['fuse_radnorm_ms']):.2f}ms "
f"flat={last_mean(samples['fuse_flat_ms']):.2f}ms "
f"warp={last_mean(samples['fuse_warp_ms']):.2f}ms "
f"crop_resize={last_mean(samples['fuse_crop_resize_ms']):.2f}ms "
f"concat={last_mean(samples['fuse_concat_ms']):.2f}ms "
f"model_norm={last_mean(samples['model_norm_ms']):.2f}ms "
f"torch={last_mean(samples['torch_ms']):.2f}ms "
f"infer={last_mean(samples['infer_ms']):.2f}ms "
f"total={last_mean(samples['total_ms']):.2f}ms "
f"tensor_shape={tuple(tensor.shape)}"
)
t_last_log = now
elapsed_total = time.perf_counter() - t_start
fps_total = n_frames / max(elapsed_total, 1e-6)
print("============================================")
print("RESULTADO FINAL RAW_BRUTO CIENTÍFICO")
print(f"elapsed : {elapsed_total:.2f}s")
print(f"frames : {n_frames}")
print(f"fps : {fps_total:.2f}")
print("--------------------------------------------")
def print_metric(name):
xs = samples[name]
print(
f"{name:18s} "
f"mean={mean(xs):8.2f}ms "
f"p95={p95(xs):8.2f}ms "
f"max={maxv(xs):8.2f}ms"
)
for name in [
"sync_dt_ms",
"capture_ms",
"decode_ms",
"controller_ms",
"fuse_total_ms",
"fuse_dark_ms",
"fuse_radnorm_ms",
"fuse_flat_ms",
"fuse_prepare_ms",
"fuse_warp_ms",
"fuse_crop_resize_ms",
"fuse_concat_ms",
"model_norm_ms",
"torch_ms",
"infer_ms",
"total_ms",
]:
print_metric(name)
print("============================================")
if last_tensor is not None:
print_tensor_stats("LAST RAW_BRUTO SCI", last_tensor)
if args.save_debug:
debug_dir = Path(args.debug_dir)
debug_dir.mkdir(parents=True, exist_ok=True)
np.save(str(debug_dir / "last_tensor.npy"), last_tensor)
stats_path = debug_dir / "last_tensor_stats.json"
with open(stats_path, "w", encoding="utf-8") as f:
json.dump(tensor_stats(last_tensor), f, indent=2, ensure_ascii=False)
panel = make_panel(last_tensor)
cv2.imwrite(str(debug_dir / "last_tensor_panel.png"), panel)
if last_meta is not None:
with open(debug_dir / "last_meta.json", "w", encoding="utf-8") as f:
json.dump(last_meta, f, indent=2, ensure_ascii=False)
print(f"[SAVE] Debug salvo em: {debug_dir}")
if args.show:
panel = make_panel(last_tensor)
if args.display_scale and abs(args.display_scale - 1.0) > 1e-6:
new_w = max(1, int(panel.shape[1] * args.display_scale))
new_h = max(1, int(panel.shape[0] * args.display_scale))
panel = cv2.resize(panel, (new_w, new_h), interpolation=cv2.INTER_AREA)
cv2.imshow("RAW_BRUTO scientific tensor", panel)
print("[INFO] Pressione qualquer tecla para fechar.")
cv2.waitKey(0)
cv2.destroyAllWindows()
finally:
try:
client.stop()
except Exception:
pass
if __name__ == "__main__":
main()

View File

@ -1,645 +0,0 @@
import argparse
import json
import sys
import time
import threading
from pathlib import Path
import cv2
import numpy as np
# ============================================================
# Ajuste de import local
# ============================================================
THIS_FILE = Path(__file__).resolve()
WORKERS_DIR = THIS_FILE.parents[2]
if str(WORKERS_DIR) not in sys.path:
sys.path.insert(0, str(WORKERS_DIR))
from camera_worker.oak_fcc3_core.oak_fcc3_client import OakFcc3Client
try:
from camera_worker.oak_fcc3_core.segformer_service import MultiSpecSegformerService
except Exception:
MultiSpecSegformerService = None
# ============================================================
# FPS / utilidades
# ============================================================
class FpsMeter:
def __init__(self, alpha=0.15):
self.alpha = float(alpha)
self.last_ts = None
self.fps = 0.0
def tick(self):
now = time.perf_counter()
if self.last_ts is not None:
dt = now - self.last_ts
if dt > 1e-9:
inst = 1.0 / dt
self.fps = inst if self.fps <= 0 else (1.0 - self.alpha) * self.fps + self.alpha * inst
self.last_ts = now
return self.fps
def load_json_if_exists(path):
if not path:
return None
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def init_model_service(args):
"""
Mesmo contrato do benchmark científico:
--run_model exige --model_config_json
MultiSpecSegformerService(model_config=cfg).infer_tensor_fast(tensor, keep_probs=False)
"""
if not args.run_model:
return None
if MultiSpecSegformerService is None:
raise RuntimeError("MultiSpecSegformerService não pôde ser importado.")
model_cfg = load_json_if_exists(args.model_config_json)
if not isinstance(model_cfg, dict):
raise RuntimeError("--run_model requer --model_config_json válido.")
svc = MultiSpecSegformerService(
model_config=model_cfg,
mostrar_log=print,
)
dummy = np.zeros((5, int(args.height), int(args.width)), dtype=np.float32)
for _ in range(max(0, int(args.warmup_model))):
svc.infer_tensor_fast(dummy, keep_probs=False)
print(f"[MODEL] Warmup concluído: {args.warmup_model}x")
return svc
def to_u8_01(arr, auto_level=False):
arr = np.asarray(arr, dtype=np.float32)
arr = np.nan_to_num(arr, nan=0.0, posinf=1.0, neginf=0.0)
if auto_level:
p1 = float(np.percentile(arr, 1))
p99 = float(np.percentile(arr, 99))
den = max(p99 - p1, 1e-6)
arr = (arr - p1) / den
arr = np.clip(arr, 0.0, 1.0)
return (arr * 255.0).astype(np.uint8)
def ensure_bgr(img, auto_level=False):
img = np.asarray(img)
if img.ndim == 2:
g = to_u8_01(img, auto_level=auto_level)
return cv2.cvtColor(g, cv2.COLOR_GRAY2BGR)
if img.ndim == 3 and img.shape[2] == 3:
u8 = to_u8_01(img, auto_level=auto_level)
# decoded/tensor RGB vem em RGB; OpenCV mostra BGR
return cv2.cvtColor(u8, cv2.COLOR_RGB2BGR)
raise RuntimeError(f"Imagem inválida para visualização: shape={img.shape}")
def tensor_rgb_to_bgr(tensor, auto_level=False):
rgb = np.stack([tensor[0], tensor[1], tensor[2]], axis=2)
return ensure_bgr(rgb, auto_level=auto_level)
def tensor_channel_to_bgr(tensor, idx, auto_level=False):
return ensure_bgr(tensor[idx], auto_level=auto_level)
def add_title(img, title, color=(255, 255, 255)):
out = img.copy()
h, w = out.shape[:2]
bar_h = 34
bar = np.zeros((bar_h, w, 3), dtype=np.uint8)
cv2.putText(bar, str(title), (10, 23), cv2.FONT_HERSHEY_SIMPLEX, 0.62, color, 2, cv2.LINE_AA)
return np.vstack([bar, out])
def add_hud(panel, lines):
out = panel.copy()
x, y = 12, 45
for line in lines:
cv2.putText(out, line, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.62, (0, 255, 255), 2, cv2.LINE_AA)
y += 24
return out
def resize_tile(img, tile_w, tile_h):
return cv2.resize(img, (int(tile_w), int(tile_h)), interpolation=cv2.INTER_AREA)
def get_cam_by_role(decoded, role):
role = str(role).lower()
for cam_id, item in decoded.items():
r = str(item.get("role") or item.get("meta", {}).get("role") or "").lower()
if r == role:
return cam_id
return None
def decoded_raw_tiles(decoded, tile_w, tile_h, auto_level=False):
"""
Retorna tiles RAW/decoded para RGB, RE, NIR antes da fusão final.
Aqui 'RAW_BRUTO' significa o conteúdo decodificado vindo das câmeras, ainda no espaço nativo.
"""
tiles = {}
rgb_id = get_cam_by_role(decoded, "rgb")
re_id = get_cam_by_role(decoded, "re")
nir_id = get_cam_by_role(decoded, "nir")
if rgb_id is not None:
img = decoded[rgb_id]["image"]
tiles["rgb"] = resize_tile(ensure_bgr(img, auto_level=auto_level), tile_w, tile_h)
else:
tiles["rgb"] = np.zeros((tile_h, tile_w, 3), dtype=np.uint8)
if re_id is not None:
img = decoded[re_id]["image"]
tiles["re"] = resize_tile(ensure_bgr(img, auto_level=auto_level), tile_w, tile_h)
else:
tiles["re"] = np.zeros((tile_h, tile_w, 3), dtype=np.uint8)
if nir_id is not None:
img = decoded[nir_id]["image"]
tiles["nir"] = resize_tile(ensure_bgr(img, auto_level=auto_level), tile_w, tile_h)
else:
tiles["nir"] = np.zeros((tile_h, tile_w, 3), dtype=np.uint8)
return tiles
def tensor_tiles(tensor, tile_w, tile_h, auto_level=False):
return {
"rgb": resize_tile(tensor_rgb_to_bgr(tensor, auto_level=auto_level), tile_w, tile_h),
"re": resize_tile(tensor_channel_to_bgr(tensor, 3, auto_level=auto_level), tile_w, tile_h),
"nir": resize_tile(tensor_channel_to_bgr(tensor, 4, auto_level=auto_level), tile_w, tile_h),
}
def colorize_label_map(label_map, num_classes=None):
label = np.asarray(label_map)
if label.ndim == 3:
label = np.argmax(label, axis=0)
label = label.astype(np.int32)
if num_classes is None:
num_classes = int(max(1, label.max() + 1))
# Paleta simples e estável. BGR.
palette = np.array([
[40, 40, 40],
[60, 180, 60],
[60, 60, 220],
[220, 180, 60],
[180, 60, 180],
[180, 180, 60],
[60, 180, 180],
[220, 220, 220],
], dtype=np.uint8)
out = palette[label % len(palette)]
return out
def try_extract_prediction_tiles(pred, target_w, target_h):
"""
Tentativa genérica. Adapte aqui se o benchmark tiver nomes específicos das cabeças.
Retorna até 3 tiles BGR: semântica/head0/head1.
"""
if pred is None:
blank = np.zeros((target_h, target_w, 3), dtype=np.uint8)
return [blank, blank.copy(), blank.copy()], ["Pred vazio", "Head 1", "Head 2"]
candidates = []
names = []
if isinstance(pred, dict):
# nomes comuns
for key in ("mask", "pred_mask", "class_map", "semantic", "semantic_mask", "segmentation"):
if key in pred:
candidates.append(pred[key])
names.append(key)
heads = pred.get("heads") or pred.get("head_outputs") or pred.get("predictions")
if isinstance(heads, dict):
for k, v in heads.items():
candidates.append(v)
names.append(str(k))
elif isinstance(heads, (list, tuple)):
for i, v in enumerate(heads):
candidates.append(v)
names.append(f"head_{i}")
else:
candidates.append(pred)
names.append("prediction")
tiles = []
out_names = []
for name, arr in zip(names, candidates):
arr = np.asarray(arr)
# remove batch se existir
if arr.ndim == 4 and arr.shape[0] == 1:
arr = arr[0]
if arr.ndim == 3:
# CHW logits/probs ou HWC RGB/probs
if arr.shape[0] <= 32:
vis = colorize_label_map(np.argmax(arr, axis=0), num_classes=arr.shape[0])
elif arr.shape[2] in (1, 3):
vis = ensure_bgr(arr[:, :, 0] if arr.shape[2] == 1 else arr, auto_level=True)
else:
vis = ensure_bgr(np.max(arr, axis=2), auto_level=True)
elif arr.ndim == 2:
# se parecer label map, colore; se parecer float, cinza auto-level
if np.issubdtype(arr.dtype, np.integer) and int(np.max(arr)) <= 64:
vis = colorize_label_map(arr)
else:
vis = ensure_bgr(arr, auto_level=True)
elif arr.ndim == 1:
# vetor de classe/score: desenha texto
vis = np.zeros((target_h, target_w, 3), dtype=np.uint8)
txt = np.array2string(arr[:8], precision=2, separator=", ")
cv2.putText(vis, txt[:80], (10, target_h // 2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA)
else:
continue
vis = resize_tile(vis, target_w, target_h)
tiles.append(vis)
out_names.append(name)
if len(tiles) >= 3:
break
while len(tiles) < 3:
tiles.append(np.zeros((target_h, target_w, 3), dtype=np.uint8))
out_names.append(f"pred_{len(tiles)}")
return tiles[:3], out_names[:3]
def try_run_model(model_svc, tensor):
"""
Inferência real, igual ao benchmark científico.
Mantém esta função isolada para adaptar fácil caso o retorno do modelo mude.
"""
if model_svc is None:
return None, "model_svc_none"
if hasattr(model_svc, "infer_tensor_fast"):
return model_svc.infer_tensor_fast(tensor, keep_probs=False), None
if hasattr(model_svc, "infer"):
return model_svc.infer(tensor), None
return None, "model_svc_sem_infer"
def build_grid(raw_tiles, final_tiles, pred_tiles=None, pred_names=None, tile_w=420, tile_h=260):
rows = []
row_defs = [
("RGB", "rgb"),
("RE", "re"),
("NIR", "nir"),
]
for i, (label, key) in enumerate(row_defs):
left = add_title(raw_tiles[key], f"RAW_BRUTO decoded {label}")
mid = add_title(final_tiles[key], f"Tensor final {label}")
cells = [left, mid]
if pred_tiles is not None:
name = pred_names[i] if pred_names and i < len(pred_names) else f"Pred {i}"
cells.append(add_title(pred_tiles[i], name))
# iguala altura após título
h_min = min(c.shape[0] for c in cells)
norm = [cv2.resize(c, (tile_w, h_min), interpolation=cv2.INTER_AREA) if c.shape[1] != tile_w or c.shape[0] != h_min else c for c in cells]
rows.append(np.hstack(norm))
return np.vstack(rows)
def get_core_perf(client):
for attr in ("raw_processor", "processor", "core", "raw_processor_core"):
obj = getattr(client, attr, None)
if obj is not None and getattr(obj, "last_fusion_result", None) is not None:
return obj.last_fusion_result.get("perf", {}) or {}
return {}
# ============================================================
# Estado compartilhado / workers assíncronos
# ============================================================
class SharedState:
def __init__(self):
self.lock = threading.Lock()
self.running = True
self.latest_decoded = None
self.latest_meta = None
self.latest_tensor = None
self.latest_perf = {}
self.latest_pred = None
self.latest_model_warn = None
self.latest_model_ms = 0.0
self.latest_error = None
self.tensor_fps = FpsMeter()
self.model_fps = FpsMeter()
self.preview_fps = FpsMeter()
self.tensor_seq = 0
self.model_seq = 0
def stop(self):
with self.lock:
self.running = False
def is_running(self):
with self.lock:
return bool(self.running)
def tensor_worker(client, state, args):
"""
Roda no talo: captura RAW_BRUTO, monta tensor final e atualiza cache.
Não depende do FPS da janela.
"""
while state.is_running():
try:
frame, meta, decoded = client.get_next_decoded(timeout=args.timeout)
if not isinstance(frame, dict):
raise RuntimeError(f"RAW_BRUTO esperado como dict. Veio {type(frame)}")
tensor = client.build_infer_tensor_from_decoded(
decoded=decoded,
meta=meta,
channels_expected=5,
target_size=(args.width, args.height),
)
tensor = np.ascontiguousarray(tensor.astype(np.float32, copy=False))
perf = get_core_perf(client)
fps = state.tensor_fps.tick()
with state.lock:
state.latest_decoded = decoded
state.latest_meta = meta
state.latest_tensor = tensor
state.latest_perf = dict(perf or {})
state.tensor_seq += 1
state.latest_error = None
except Exception as e:
with state.lock:
state.latest_error = f"tensor_worker: {type(e).__name__}: {e}"
time.sleep(0.02)
def model_worker(model_svc, state, args):
"""
Opcional: roda inferência no último tensor disponível.
Não bloqueia o worker de tensor nem a janela.
"""
last_seq = -1
while state.is_running():
with state.lock:
tensor = None if state.latest_tensor is None else state.latest_tensor.copy()
seq = state.tensor_seq
if tensor is None or seq == last_seq:
time.sleep(0.005)
continue
last_seq = seq
try:
t0_model = time.perf_counter()
pred, warn = try_run_model(model_svc, tensor)
model_ms = (time.perf_counter() - t0_model) * 1000.0
if warn is None:
state.model_fps.tick()
with state.lock:
state.latest_pred = pred
state.latest_model_warn = warn
state.latest_model_ms = float(model_ms)
state.model_seq += 1
except Exception as e:
with state.lock:
state.latest_model_warn = f"model_worker: {type(e).__name__}: {e}"
time.sleep(0.02)
def snapshot_state(state):
"""
Copia referências do cache para desenhar. A janela só roda na cadência do preview.
"""
with state.lock:
return {
"decoded": state.latest_decoded,
"meta": state.latest_meta,
"tensor": state.latest_tensor,
"perf": dict(state.latest_perf or {}),
"pred": state.latest_pred,
"model_warn": state.latest_model_warn,
"model_ms": state.latest_model_ms,
"error": state.latest_error,
"tensor_fps": state.tensor_fps.fps,
"model_fps": state.model_fps.fps,
"tensor_seq": state.tensor_seq,
"model_seq": state.model_seq,
}
# ============================================================
# Main loop assíncrono
# ============================================================
def main():
ap = argparse.ArgumentParser(description="Preview assíncrono RAW_BRUTO decoded vs tensor final multispectral.")
ap.add_argument("--module_params", default=r"C:\ZendionInc\agrobot_base\Python\OAK\datasets\oak-fcc-3\calibration\module_params.json")
ap.add_argument("--width", type=int, default=1024, help="Largura final do tensor.")
ap.add_argument("--height", type=int, default=640, help="Altura final do tensor.")
ap.add_argument("--fps", type=float, default=40.0, help="FPS alvo da câmera.")
ap.add_argument("--preview_fps", type=float, default=5.0, help="FPS da janela OpenCV apenas.")
ap.add_argument("--timeout", type=float, default=2.0)
ap.add_argument("--warmup", type=int, default=5)
ap.add_argument("--mx_id", default=None)
ap.add_argument("--display_scale", type=float, default=0.75)
ap.add_argument("--tile_w", type=int, default=420)
ap.add_argument("--tile_h", type=int, default=260)
ap.add_argument("--auto_level_raw", action="store_true")
ap.add_argument("--auto_level_tensor", action="store_true")
ap.add_argument("--run_model", action="store_true", help="Roda inferência em thread separada usando o último tensor cacheado.")
ap.add_argument("--model_config_json", default=None, help="JSON de configuração do modelo SegFormer, igual ao benchmark.")
ap.add_argument("--warmup_model", type=int, default=3, help="Número de inferências dummy para aquecer o modelo.")
ap.add_argument("--save_last", default=None)
args = ap.parse_args()
client = OakFcc3Client(
width=args.width,
height=args.height,
fps=args.fps,
frame_type="RAW_BRUTO",
capture_mode="TRIPLE",
raw_policy="require_triple",
module_calibration_json=args.module_params,
mx_id=args.mx_id,
)
model_svc = init_model_service(args) if args.run_model else None
state = SharedState()
last_panel = None
last_warn_ts = 0.0
last_seq_drawn = -1
try:
client.start(print_debug=True)
for _ in range(max(0, int(args.warmup))):
try:
client.get_next_decoded(timeout=args.timeout)
except Exception:
pass
time.sleep(0.03)
tw = threading.Thread(target=tensor_worker, args=(client, state, args), daemon=True)
tw.start()
mw = None
if args.run_model:
mw = threading.Thread(target=model_worker, args=(model_svc, state, args), daemon=True)
mw.start()
min_period = 1.0 / max(float(args.preview_fps), 0.1)
next_draw_ts = 0.0
print("[INFO] Preview assíncrono iniciado. Pressione Q ou ESC para sair.")
print("[INFO] Tensor FPS = geração real do tensor. Preview FPS = janela. Model FPS = inferência, se habilitada.")
while True:
now = time.perf_counter()
if now < next_draw_ts:
time.sleep(min(0.005, next_draw_ts - now))
key = cv2.waitKey(1) & 0xFF
if key in (27, ord('q'), ord('Q')):
break
continue
next_draw_ts = now + min_period
snap = snapshot_state(state)
if snap["tensor"] is None or snap["decoded"] is None:
blank = np.zeros((360, 900, 3), dtype=np.uint8)
cv2.putText(blank, "Aguardando primeiro tensor...", (30, 180), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,255,255), 2, cv2.LINE_AA)
cv2.imshow("OAK RAW_BRUTO vs Tensor Final", blank)
key = cv2.waitKey(1) & 0xFF
if key in (27, ord('q'), ord('Q')):
break
continue
# Desenha só usando o cache. Não captura nem monta tensor aqui.
t_draw0 = time.perf_counter()
raw_tiles = decoded_raw_tiles(
snap["decoded"],
tile_w=args.tile_w,
tile_h=args.tile_h,
auto_level=args.auto_level_raw,
)
final_tiles = tensor_tiles(
snap["tensor"],
tile_w=args.tile_w,
tile_h=args.tile_h,
auto_level=args.auto_level_tensor,
)
pred_tiles = None
pred_names = None
if args.run_model:
pred_tiles, pred_names = try_extract_prediction_tiles(
snap["pred"],
target_w=args.tile_w,
target_h=args.tile_h,
)
if snap["model_warn"]:
tnow = time.time()
if tnow - last_warn_ts > 2.0:
print(f"[WARN][MODEL] {snap['model_warn']}")
last_warn_ts = tnow
panel = build_grid(
raw_tiles=raw_tiles,
final_tiles=final_tiles,
pred_tiles=pred_tiles,
pred_names=pred_names,
tile_w=args.tile_w,
tile_h=args.tile_h,
)
preview_fps = state.preview_fps.tick()
draw_ms = (time.perf_counter() - t_draw0) * 1000.0
perf = snap["perf"]
meta = snap["meta"] or {}
tensor_seq = int(snap["tensor_seq"])
dropped_for_preview = max(0, tensor_seq - last_seq_drawn - 1) if last_seq_drawn >= 0 else 0
last_seq_drawn = tensor_seq
hud = [
f"Preview FPS: {preview_fps:.1f} | Tensor FPS: {snap['tensor_fps']:.1f} | Model FPS: {snap['model_fps']:.1f} | infer={snap.get('model_ms', 0.0):.1f}ms",
f"draw={draw_ms:.1f}ms flat={perf.get('flat_ms', 0):.1f} warp={perf.get('warp_total_ms', 0):.1f} crop={perf.get('crop_resize_ms', 0):.1f} fuse={perf.get('total_ms', 0):.1f}",
f"seq={tensor_seq} skipped_preview={dropped_for_preview} frame_type={meta.get('frame_type')} run_model={args.run_model}",
]
if snap["error"]:
hud.append(str(snap["error"])[:120])
panel = add_hud(panel, hud)
last_panel = panel
disp = panel
if args.display_scale and abs(args.display_scale - 1.0) > 1e-6:
disp = cv2.resize(
disp,
(max(1, int(disp.shape[1] * args.display_scale)), max(1, int(disp.shape[0] * args.display_scale))),
interpolation=cv2.INTER_AREA,
)
cv2.imshow("OAK RAW_BRUTO vs Tensor Final", disp)
key = cv2.waitKey(1) & 0xFF
if key in (27, ord('q'), ord('Q')):
break
finally:
state.stop()
time.sleep(0.05)
if args.save_last and last_panel is not None:
out_path = Path(args.save_last)
out_path.parent.mkdir(parents=True, exist_ok=True)
cv2.imwrite(str(out_path), last_panel)
print(f"[SAVE] {out_path}")
try:
client.stop()
except Exception:
pass
cv2.destroyAllWindows()
if __name__ == "__main__":
main()

View File

@ -46,6 +46,7 @@ class OakFcc3Client:
mx_id=None, mx_id=None,
imu_modo="rotation_vector", imu_modo="rotation_vector",
imu_freq_hz=200, imu_freq_hz=200,
evaluate_quality=True,
**kwargs, **kwargs,
): ):
self.width = width self.width = width
@ -63,6 +64,16 @@ class OakFcc3Client:
self.imu_modo = str(imu_modo).strip().lower() self.imu_modo = str(imu_modo).strip().lower()
self.imu_freq_hz = int(imu_freq_hz) self.imu_freq_hz = int(imu_freq_hz)
# Auditoria radiométrica completa do Raw5.
#
# True mantém o comportamento histórico e é útil para captura científica,
# normalize/auditoria e ferramentas offline.
#
# No runtime em tempo real deve ficar False: evaluate_frame_quality()
# calcula estatísticas/percentis pesados e não faz parte da montagem
# necessária para a inferência.
self.evaluate_quality = bool(evaluate_quality)
self.mx_id = str(mx_id) if mx_id else None self.mx_id = str(mx_id) if mx_id else None
self.svc = OakFcc3Service( self.svc = OakFcc3Service(
@ -327,8 +338,33 @@ class OakFcc3Client:
target_size=target_size, target_size=target_size,
) )
def build_infer_tensor_from_decoded(self, decoded, meta, channels_expected, target_size=None): def build_infer_tensor_from_decoded(
self,
decoded,
meta,
channels_expected,
target_size=None,
evaluate_quality=None,
):
"""
Monta o Raw5 físico a partir das câmeras já decodificadas.
evaluate_quality:
- None -> usa self.evaluate_quality
- True -> executa evaluate_frame_quality() e atualiza
core.last_frame_quality_result
- False -> não executa a auditoria pesada e limpa
core.last_frame_quality_result
A flag altera somente a auditoria de qualidade. Não altera decode,
radiometria, flat-field, homografia, crop/resize ou patch normalization.
"""
channels_expected = self._validate_physical_channel_count(channels_expected) channels_expected = self._validate_physical_channel_count(channels_expected)
if evaluate_quality is None:
evaluate_quality = self.evaluate_quality
evaluate_quality = bool(evaluate_quality)
tensor = self.core.fuse_multispec_cameras(decoded, meta, channels_expected) tensor = self.core.fuse_multispec_cameras(decoded, meta, channels_expected)
tensor = self.core.resize_tensor_chw(tensor, target_size=target_size) tensor = self.core.resize_tensor_chw(tensor, target_size=target_size)
@ -338,7 +374,12 @@ class OakFcc3Client:
if bool(patch_cfg.get("enabled", False)): if bool(patch_cfg.get("enabled", False)):
tensor = self.core.apply_patch_normalization_to_tensor(tensor) tensor = self.core.apply_patch_normalization_to_tensor(tensor)
self.core.last_frame_quality_result = self.core.evaluate_frame_quality(tensor) if evaluate_quality:
self.core.last_frame_quality_result = self.core.evaluate_frame_quality(tensor)
else:
# Evita deixar um resultado antigo parecer referente ao frame atual.
self.core.last_frame_quality_result = None
return tensor return tensor
def decode_stream_cameras(self, frame, meta): def decode_stream_cameras(self, frame, meta):
@ -382,12 +423,19 @@ class OakFcc3Client:
tensor = np.transpose(rgb01.astype(np.float32), (2, 0, 1)) tensor = np.transpose(rgb01.astype(np.float32), (2, 0, 1))
return np.ascontiguousarray(tensor.astype(np.float32, copy=False)) return np.ascontiguousarray(tensor.astype(np.float32, copy=False))
def build_multispec_tensor(self, decoded, meta=None, target_size=None): def build_multispec_tensor(
self,
decoded,
meta=None,
target_size=None,
evaluate_quality=None,
):
tensor = self.build_infer_tensor_from_decoded( tensor = self.build_infer_tensor_from_decoded(
decoded=decoded, decoded=decoded,
meta=meta, meta=meta,
channels_expected=5, channels_expected=5,
target_size=target_size, target_size=target_size,
evaluate_quality=evaluate_quality,
) )
return np.ascontiguousarray(tensor.astype(np.float32, copy=False)) return np.ascontiguousarray(tensor.astype(np.float32, copy=False))

View File

@ -9,6 +9,8 @@ import cv2
import depthai as dai import depthai as dai
import numpy as np import numpy as np
from itertools import product
class OakFcc3Manager: class OakFcc3Manager:
""" """
@ -43,6 +45,8 @@ class OakFcc3Manager:
raw_policy="allow_single", raw_policy="allow_single",
roles=None, roles=None,
sync_mode="best", sync_mode="best",
hardware_sync_enabled=True,
frame_sync_master="CAM_A",
sync_tolerance_ms=12.0, sync_tolerance_ms=12.0,
buffer_size=8, buffer_size=8,
only_camera=None, only_camera=None,
@ -52,6 +56,9 @@ class OakFcc3Manager:
imu_modo="rotation_vector", imu_modo="rotation_vector",
imu_freq_hz=200, imu_freq_hz=200,
): ):
self.hardware_sync_enabled = hardware_sync_enabled
self.frame_sync_master = frame_sync_master
self.fps = fps self.fps = fps
# Para compatibilidade, mantemos width/height. # Para compatibilidade, mantemos width/height.
@ -432,6 +439,49 @@ class OakFcc3Manager:
# Pipeline creation, MULTISPEC aligned on OAK # Pipeline creation, MULTISPEC aligned on OAK
# ============================================================ # ============================================================
def _aplicar_frame_sync(self, cam, cam_id):
cam_id_normalizado = str(cam_id).strip().upper()
master = str(self.frame_sync_master).strip().upper()
if not self.hardware_sync_enabled:
print(
f"[OAK FSYNC] cam={cam_id_normalizado} "
f"habilitado=False modo=DISABLED"
)
return
cameras_validas = {"CAM_A", "CAM_B", "CAM_C"}
if master not in cameras_validas:
raise ValueError(
f"frame_sync_master inválido: {self.frame_sync_master}. "
f"Esperado: CAM_A, CAM_B ou CAM_C."
)
if cam_id_normalizado not in cameras_validas:
print(
f"[OAK FSYNC] Câmera ignorada: "
f"cam_id={cam_id_normalizado}"
)
return
if cam_id_normalizado == master:
modo = dai.CameraControl.FrameSyncMode.OUTPUT
nome_modo = "OUTPUT"
else:
modo = dai.CameraControl.FrameSyncMode.INPUT
nome_modo = "INPUT"
cam.initialControl.setFrameSyncMode(modo)
print(
f"[OAK FSYNC] "
f"cam={cam_id_normalizado} "
f"master={master} "
f"habilitado=True "
f"modo={nome_modo}"
)
def _create_color_camera_multispec(self, socket): def _create_color_camera_multispec(self, socket):
cam = self.pipeline.create(dai.node.ColorCamera) cam = self.pipeline.create(dai.node.ColorCamera)
cam.setBoardSocket(socket) cam.setBoardSocket(socket)
@ -591,6 +641,7 @@ class OakFcc3Manager:
raise RuntimeError(f"Role não suportada no MULTISPEC: cam_id={cam_id}, role={role}") raise RuntimeError(f"Role não suportada no MULTISPEC: cam_id={cam_id}, role={role}")
self.apply_initial_camera_controls_to_node(cam, cam_id) self.apply_initial_camera_controls_to_node(cam, cam_id)
self._aplicar_frame_sync(cam, cam_id)
xin_ctrl = self.pipeline.create(dai.node.XLinkIn) xin_ctrl = self.pipeline.create(dai.node.XLinkIn)
xin_ctrl.setStreamName(f"{cam_id}_ctrl") xin_ctrl.setStreamName(f"{cam_id}_ctrl")
@ -871,6 +922,7 @@ class OakFcc3Manager:
) )
self.apply_initial_camera_controls_to_node(cam, socket_name) self.apply_initial_camera_controls_to_node(cam, socket_name)
self._aplicar_frame_sync(cam, socket_name)
xin_ctrl = self.pipeline.create(dai.node.XLinkIn) xin_ctrl = self.pipeline.create(dai.node.XLinkIn)
xin_ctrl.setStreamName(f"{socket_name}_ctrl") xin_ctrl.setStreamName(f"{socket_name}_ctrl")
@ -1241,6 +1293,41 @@ class OakFcc3Manager:
f"Tolerância atual={self.sync_tolerance_ms} ms." f"Tolerância atual={self.sync_tolerance_ms} ms."
) )
def _encontrar_melhor_tripleta(self, required_cam_ids):
listas = [
list(self.buffers[cam_id])
for cam_id in required_cam_ids
]
melhor_selecao = None
melhor_score = None
for combinacao in product(*listas):
timestamps = [
item["timestamp"]
for item in combinacao
]
menor_ts = min(timestamps)
maior_ts = max(timestamps)
spread_ms = (maior_ts - menor_ts) * 1000.0
# Primeiro prioriza menor dispersão.
# Em empate, prefere o pacote mais recente.
score = (
spread_ms,
-menor_ts,
)
if melhor_score is None or score < melhor_score:
melhor_score = score
melhor_selecao = {
cam_id: item
for cam_id, item in zip(required_cam_ids, combinacao)
}
return melhor_selecao
def _extract_frame_controls(self, msg): def _extract_frame_controls(self, msg):
controls = { controls = {
@ -1330,9 +1417,19 @@ class OakFcc3Manager:
t0_ts = self._cap_now_ms() t0_ts = self._cap_now_ms()
try: try:
ts = msg.getTimestamp().total_seconds() ts_start = msg.getTimestampDevice(
dai.CameraExposureOffset.START
).total_seconds()
ts_end = msg.getTimestampDevice(
dai.CameraExposureOffset.END
).total_seconds()
except Exception: except Exception:
ts = time.time() ts_start = msg.getTimestamp().total_seconds()
ts_end = ts_start
ts = ts_start
if perf is not None: if perf is not None:
perf["drain_get_timestamp_ms"] += self._cap_now_ms() - t0_ts perf["drain_get_timestamp_ms"] += self._cap_now_ms() - t0_ts
@ -1395,7 +1492,9 @@ class OakFcc3Manager:
self.buffers[cam_id].append({ self.buffers[cam_id].append({
"frame": frame, "frame": frame,
"timestamp": ts, "timestamp": ts_start,
"timestamp_start": ts_start,
"timestamp_end": ts_end,
"controls": frame_controls, "controls": frame_controls,
}) })
@ -1420,34 +1519,25 @@ class OakFcc3Manager:
def _try_get_synced_packet(self, perf=None): def _try_get_synced_packet(self, perf=None):
required_cam_ids = self._get_required_cam_ids() required_cam_ids = self._get_required_cam_ids()
if not required_cam_ids:
if perf is not None:
perf["wait_reason"] = "no_required_cameras"
return None
# Todas as câmeras precisam ter pelo menos um frame disponível.
for cam_id in required_cam_ids: for cam_id in required_cam_ids:
if cam_id not in self.buffers or len(self.buffers[cam_id]) == 0: if cam_id not in self.buffers or len(self.buffers[cam_id]) == 0:
if perf is not None: if perf is not None:
perf["wait_reason"] = f"empty_buffer:{cam_id}" perf["wait_reason"] = f"empty_buffer:{cam_id}"
return None return None
ref_cam_id = min(required_cam_ids, key=lambda cid: len(self.buffers[cid])) # Procura a melhor combinação entre todos os frames disponíveis.
ref_item = self.buffers[ref_cam_id][0] selected = self._encontrar_melhor_tripleta(required_cam_ids)
ref_ts = ref_item["timestamp"]
selected = {} if not selected or len(selected) != len(required_cam_ids):
if perf is not None:
for cam_id in required_cam_ids: perf["wait_reason"] = "no_valid_selection"
best_item = None return None
best_dt = None
for item in self.buffers[cam_id]:
dt = abs(item["timestamp"] - ref_ts)
if best_dt is None or dt < best_dt:
best_dt = dt
best_item = item
if best_item is None:
if perf is not None:
perf["wait_reason"] = f"no_best_item:{cam_id}"
return None
selected[cam_id] = best_item
timestamps = { timestamps = {
cam_id: item["timestamp"] cam_id: item["timestamp"]
@ -1459,44 +1549,80 @@ class OakFcc3Manager:
for cam_id, item in selected.items() for cam_id, item in selected.items()
} }
ts_values = list(timestamps.values())
sync_dt_ms = (
(max(ts_values) - min(ts_values)) * 1000.0
if len(ts_values) >= 2
else 0.0
)
sync_ok = sync_dt_ms <= self.sync_tolerance_ms
if perf is not None: if perf is not None:
perf["selected_ts_by_cam"] = {cam_id: float(ts) for cam_id, ts in timestamps.items()} perf["selected_ts_by_cam"] = {
cam_id: float(ts)
for cam_id, ts in timestamps.items()
}
perf["selected_seq_by_cam"] = { perf["selected_seq_by_cam"] = {
cam_id: item.get("controls", {}).get("sequence_num") cam_id: item.get("controls", {}).get("sequence_num")
for cam_id, item in selected.items() for cam_id, item in selected.items()
} }
ts_values = list(timestamps.values()) perf["sync_dt_ms"] = float(sync_dt_ms)
sync_dt_ms = (max(ts_values) - min(ts_values)) * 1000.0 if len(ts_values) >= 2 else 0.0 perf["sync_ok"] = bool(sync_ok)
sync_ok = sync_dt_ms <= self.sync_tolerance_ms
modo_sync = str(self.sync_mode).strip().lower()
# No modo estrito, nunca entrega uma tripleta fora da tolerância.
if not sync_ok and modo_sync == "strict":
#Remove o frame globalmente mais antigo entre as cabeças
#dos buffers. Frames futuros somente estarão mais distantes
#desse frame, então ele não conseguirá formar uma combinação
#melhor posteriormente.
oldest_cam_id = min(
required_cam_ids,
key=lambda cam_id: self.buffers[cam_id][0]["timestamp"]
)
dropped_item = self.buffers[oldest_cam_id].popleft()
if not sync_ok and self.sync_mode == "strict":
oldest_cam_id = min(timestamps, key=timestamps.get)
if len(self.buffers[oldest_cam_id]) > 0:
self.buffers[oldest_cam_id].popleft()
if perf is not None: if perf is not None:
perf["wait_reason"] = f"strict_drop_oldest:{oldest_cam_id}" perf["wait_reason"] = f"strict_drop_oldest:{oldest_cam_id}"
perf["sync_dt_ms"] = float(sync_dt_ms) perf["dropped_timestamp"] = float(dropped_item["timestamp"])
perf["sync_ok"] = False
return None return None
# Em best/best_effort, entrega a melhor combinação disponível,
# mesmo quando estiver fora da tolerância.
frames = { frames = {
cam_id: item["frame"] cam_id: item["frame"]
for cam_id, item in selected.items() for cam_id, item in selected.items()
} }
# Consome todos os frames anteriores e o próprio frame selecionado.
for cam_id, used_item in selected.items(): for cam_id, used_item in selected.items():
while len(self.buffers[cam_id]) > 0: while self.buffers[cam_id]:
item = self.buffers[cam_id].popleft() item = self.buffers[cam_id].popleft()
if item is used_item: if item is used_item:
break break
if perf is not None: if perf is not None:
perf["wait_reason"] = "synced_selected" perf["wait_reason"] = (
perf["sync_dt_ms"] = float(sync_dt_ms) "synced_selected"
perf["sync_ok"] = bool(sync_ok) if sync_ok
else "best_effort_selected_outside_tolerance"
)
return frames, timestamps, sync_dt_ms, sync_ok, frame_controls return (
frames,
timestamps,
sync_dt_ms,
sync_ok,
frame_controls,
)
def _get_available_cam_ids_ordered(self): def _get_available_cam_ids_ordered(self):
role_order = ["rgb", "re", "nir"] role_order = ["rgb", "re", "nir"]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,761 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
camera_startup_profile_production.py
====================================
Gerador/auditor FINAL de produção para o bloco `camera_settings`
do módulo multiespectral.
IMPORTANTE
----------
Isto NÃO é uma calibração óptica/radiométrica nova.
O script antigo "Sensor Calibration Tool" misturava:
- camera_settings;
- ajustes manuais AE/AWB/exposure/gain;
- ROIs de cana/erva/solo;
- guidance heurístico;
- rgb_calibration manual;
- snapshots/offline samples.
Na arquitetura final de produto essas responsabilidades já foram separadas.
Esta ferramenta preserva SOMENTE o que ainda é necessário ao runtime:
camera_settings
e força explicitamente:
rgb_calibration.enabled = false
gains = 1,1,1
para impedir que ganhos RGB históricos/experimentais vazem para o tensor.
Dependência
-----------
A ferramenta lê o artefato homologado da etapa radiométrica:
calibration/radiometry_calibration_v5.json
e reaproveita:
radiometric_normalization.reference_controls
como valores de fallback do startup.
Política normal de produto
--------------------------
RGB:
AE = ON
AWB = ON
exposure_time_us = referência radiométrica (fallback)
analogue_gain = ISO_ref / 100
colour_gains = [1.0, 1.0]
RE:
AE = ON
AWB = OFF
exposure_time_us = referência radiométrica (fallback)
analogue_gain = ISO_ref / 100
NIR:
AE = ON
AWB = OFF
exposure_time_us = referência radiométrica (fallback)
analogue_gain = ISO_ref / 100
Por que manter os valores manuais se AE está ON?
-------------------------------------------------
No manager atual, com AE ligado, exposure/gain do JSON não são forçados
fisicamente. Eles ficam armazenados como estado/fallback para uma eventual
transição futura para manual.
Assim:
- o campo continua adaptando exposição automaticamente;
- a normalização radiométrica usa os controles reais de cada frame;
- se AE for desabilitado em algum fluxo futuro, o fallback já é coerente
com a calibração radiométrica do módulo.
Saída
-----
calibration/camera_startup_profile_v3.json
Contém:
module_params_fragment.camera_settings
module_params_fragment.rgb_calibration
O assembler final do module_params continua sendo a autoridade de merge.
Fail-closed
-----------
- exige CAM_A = OV9782 ou AR0234;
- exige CAM_B/C = OV9282;
- exige artefato radiométrico promovido;
- exige hardware_signature compatível;
- exige reference_controls válidos para rgb/re/nir;
- não aceita ISO inválido;
- não ativa rgb_calibration;
- preserva backup do ativo anterior.
Uso
---
python camera_startup_profile_production.py
Com rastreabilidade:
python camera_startup_profile_production.py ^
--module-id MOD_MS_001 ^
--operator Diego
Somente auditoria, sem salvar:
python camera_startup_profile_production.py --check-only
"""
from __future__ import annotations
import argparse
import json
import os
import shutil
from dataclasses import dataclass, asdict
from datetime import datetime
from pathlib import Path
from typing import Dict, Optional, List
import depthai as dai
SCHEMA = "multispec_camera_startup_profile_v3"
ISO_BASE = 100.0
ROLES = ("rgb", "re", "nir")
PRODUCT_TOPOLOGY = {
"rgb": {
"socket": "CAM_A",
"allowed_sensors": ("OV9782", "AR0234"),
},
"re": {
"socket": "CAM_B",
"allowed_sensors": ("OV9282",),
},
"nir": {
"socket": "CAM_C",
"allowed_sensors": ("OV9282",),
},
}
SENSOR_NATIVE_SIZE = {
"OV9782": (1280, 800),
"AR0234": (1920, 1200),
"OV9282": (1280, 800),
}
def now_str() -> str:
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def stamp() -> str:
return datetime.now().strftime("%Y%m%d_%H%M%S_%f")
def ensure_dir(path: str | Path):
Path(path).mkdir(parents=True, exist_ok=True)
def load_json(path: str | Path) -> dict:
p = Path(path)
if not p.is_file():
raise FileNotFoundError(f"Arquivo não encontrado: {p}")
with p.open("r", encoding="utf-8") as f:
return json.load(f)
def save_json_atomic(path: str | Path, data: dict):
p = Path(path)
ensure_dir(p.parent)
tmp = p.with_suffix(p.suffix + ".tmp")
with tmp.open("w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
f.flush()
os.fsync(f.fileno())
os.replace(tmp, p)
def socket_name(socket) -> str:
name = getattr(socket, "name", None)
if name:
return str(name)
text = str(socket)
for candidate in ("CAM_A", "CAM_B", "CAM_C", "CAM_D"):
if candidate in text:
return candidate
return text
def supported_types(feature) -> List[str]:
return [
str(v).upper()
for v in (getattr(feature, "supportedTypes", []) or [])
]
def feature_is_color(feature) -> bool:
types = supported_types(feature)
sensor = str(getattr(feature, "sensorName", "") or "").upper()
if any("COLOR" in x for x in types):
return True
if any("MONO" in x for x in types):
return False
return sensor in {"OV9782", "AR0234"}
def feature_is_mono(feature) -> bool:
types = supported_types(feature)
if any("MONO" in x for x in types):
return True
if any("COLOR" in x for x in types):
return False
return not feature_is_color(feature)
@dataclass
class CameraInfo:
role: str
socket: str
sensor: str
width: int
height: int
is_color: bool
is_mono: bool
def discover_hardware(mx_id: Optional[str]):
info = dai.DeviceInfo(mx_id) if mx_id else None
ctx = dai.Device(info) if info is not None else dai.Device()
with ctx as device:
actual_mx = None
for method_name in ("getMxId", "getDeviceId"):
fn = getattr(device, method_name, None)
if callable(fn):
try:
value = fn()
if value:
actual_mx = str(value)
break
except Exception:
pass
usb_speed = None
try:
usb_speed = str(device.getUsbSpeed())
except Exception:
pass
rows = []
for f in device.getConnectedCameraFeatures():
rows.append({
"socket": socket_name(f.socket),
"sensor": str(getattr(f, "sensorName", "") or "").upper(),
"width": int(getattr(f, "width", 0) or 0),
"height": int(getattr(f, "height", 0) or 0),
"is_color": feature_is_color(f),
"is_mono": feature_is_mono(f),
"supported_types": supported_types(f),
})
return rows, actual_mx, usb_speed
def validate_and_resolve_hardware(rows: list[dict]) -> Dict[str, CameraInfo]:
by_socket = {row["socket"]: row for row in rows}
resolved = {}
errors = []
for role in ROLES:
contract = PRODUCT_TOPOLOGY[role]
sock = contract["socket"]
row = by_socket.get(sock)
if row is None:
errors.append(f"{role.upper()}: {sock} ausente")
continue
sensor = row["sensor"]
if sensor not in contract["allowed_sensors"]:
errors.append(
f"{role.upper()}: {sock} sensor={sensor}, "
f"permitidos={contract['allowed_sensors']}"
)
continue
expected_w, expected_h = SENSOR_NATIVE_SIZE[sensor]
if row["width"] and row["height"]:
if (row["width"], row["height"]) != (expected_w, expected_h):
errors.append(
f"{role.upper()}: {sensor} anunciou "
f"{row['width']}x{row['height']}, esperado "
f"{expected_w}x{expected_h}"
)
if role == "rgb" and not row["is_color"]:
errors.append(f"RGB: {sock}/{sensor} não anunciado COLOR")
if role in ("re", "nir") and not row["is_mono"]:
errors.append(f"{role.upper()}: {sock}/{sensor} não anunciado MONO")
resolved[role] = CameraInfo(
role=role,
socket=sock,
sensor=sensor,
width=expected_w,
height=expected_h,
is_color=bool(row["is_color"]),
is_mono=bool(row["is_mono"]),
)
if errors:
raise RuntimeError(
"Topologia de produto inválida:\n - "
+ "\n - ".join(errors)
)
return resolved
def hardware_signature_from_resolved(resolved: Dict[str, CameraInfo]) -> dict:
return {
role: {
"socket": resolved[role].socket,
"sensor": resolved[role].sensor,
"size": [resolved[role].width, resolved[role].height],
}
for role in ROLES
}
def normalize_signature(sig: dict) -> dict:
"""
Aceita assinatura no formato dos calibradores de produção.
Retorna apenas role/socket/sensor/size para comparação robusta.
"""
out = {}
for role in ROLES:
item = (sig or {}).get(role, {}) or {}
socket = (
item.get("socket")
or item.get("socket_name")
)
sensor = (
item.get("sensor")
or item.get("sensor_name")
)
size = item.get("size")
if size is None:
width = item.get("width")
height = item.get("height")
if width is not None and height is not None:
size = [int(width), int(height)]
if socket is None or sensor is None or size is None:
raise RuntimeError(
f"hardware_signature incompleta para {role}: {item}"
)
out[role] = {
"socket": str(socket),
"sensor": str(sensor).upper(),
"size": [int(size[0]), int(size[1])],
}
return out
def extract_radiometric_fragment(data: dict) -> dict:
"""
O artefato radiométrico de produção guarda:
module_params_fragment.radiometric_normalization
"""
fragment = data.get("module_params_fragment", {}) or {}
rn = fragment.get("radiometric_normalization")
if not isinstance(rn, dict):
raise RuntimeError(
"Artefato radiométrico sem "
"module_params_fragment.radiometric_normalization"
)
if not rn.get("enabled", False):
raise RuntimeError(
"radiometric_normalization do artefato não está enabled=true"
)
return rn
def validate_radiometry_artifact(
data: dict,
current_signature: dict,
):
schema = str(data.get("schema", ""))
if schema != "multispec_radiometric_calibration_v5":
raise RuntimeError(
f"Schema radiométrico inesperado: {schema!r}. "
"Esperado 'multispec_radiometric_calibration_v5'."
)
status = str(data.get("status", "")).lower()
if status not in ("good", "warning"):
raise RuntimeError(
f"Radiometria não homologada para consumo: status={status!r}"
)
if not bool(data.get("promoted", False)):
raise RuntimeError(
"Artefato radiométrico não foi promovido."
)
artifact_sig = normalize_signature(
data.get("hardware_signature", {})
)
if artifact_sig != current_signature:
raise RuntimeError(
"Hardware conectado não corresponde ao hardware da radiometria.\n"
f"Atual : {current_signature}\n"
f"Rad : {artifact_sig}"
)
rn = extract_radiometric_fragment(data)
refs = rn.get("reference_controls", {}) or {}
for role in ROLES:
ref = refs.get(role)
if not isinstance(ref, dict):
raise RuntimeError(
f"reference_controls ausente para {role}"
)
exp = ref.get("exposure_time_us")
iso = ref.get("sensitivity_iso")
if exp is None or float(exp) <= 0:
raise RuntimeError(
f"reference exposure inválida para {role}: {exp}"
)
if iso is None or float(iso) < 50:
raise RuntimeError(
f"reference ISO inválido para {role}: {iso}"
)
return rn
def iso_to_analogue_gain(iso: float) -> float:
"""
O manager atual representa analogue_gain aproximadamente como ISO/100.
"""
gain = float(iso) / ISO_BASE
return max(1.0, gain)
def build_camera_settings(reference_controls: dict) -> dict:
settings = {}
for role in ROLES:
ref = reference_controls[role]
exp = int(round(float(ref["exposure_time_us"])))
iso = float(ref["sensitivity_iso"])
gain = iso_to_analogue_gain(iso)
if role == "rgb":
settings[role] = {
"ae_enable": True,
"awb_enable": True,
"exposure_time_us": exp,
"analogue_gain": gain,
"colour_gains": [1.0, 1.0],
}
else:
settings[role] = {
"ae_enable": True,
"awb_enable": False,
"exposure_time_us": exp,
"analogue_gain": gain,
"colour_gains": None,
}
return settings
def build_rgb_calibration_policy() -> dict:
"""
Produto final mantém correção multiplicativa RGB desligada.
Se um dia ela for homologada, deve existir um calibrador separado e
dataset/runtime precisam usar o mesmo contrato.
"""
return {
"enabled": False,
"gains": {
"R": 1.0,
"G": 1.0,
"B": 1.0,
},
"policy": "disabled_product_default",
"reason": (
"No validated RGB tensor-gain calibration is currently active. "
"Historical manual gains are intentionally discarded."
),
}
def compare_existing_module_params(
module_params_path: Optional[str],
generated_fragment: dict,
) -> dict:
if not module_params_path:
return {
"available": False,
"path": None,
}
p = Path(module_params_path)
if not p.is_file():
return {
"available": False,
"path": str(p),
}
current = load_json(p)
current_camera = current.get("camera_settings")
current_rgb = current.get("rgb_calibration")
generated_camera = generated_fragment["camera_settings"]
generated_rgb = generated_fragment["rgb_calibration"]
return {
"available": True,
"path": str(p),
"schema": current.get("schema"),
"camera_settings_equal": current_camera == generated_camera,
"rgb_calibration_equal": current_rgb == generated_rgb,
"current_camera_settings": current_camera,
"generated_camera_settings": generated_camera,
"current_rgb_calibration": current_rgb,
"generated_rgb_calibration": generated_rgb,
}
def main():
parser = argparse.ArgumentParser(
description=(
"Gerador/auditor de produção para camera_settings e "
"política rgb_calibration do módulo multiespectral."
),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"--mx-id",
default=None,
help="MX ID da OAK. Vazio usa o primeiro dispositivo disponível.",
)
parser.add_argument(
"--radiometry-json",
default="calibration/radiometry_calibration_v5.json",
help="Artefato radiométrico homologado.",
)
parser.add_argument(
"--out-json",
default="calibration/camera_startup_profile_v3.json",
help="Artefato ativo desta etapa.",
)
parser.add_argument(
"--compare-module-params",
default="calibration/module_params.json",
help="Opcional: audita diferenças contra module_params existente.",
)
parser.add_argument(
"--module-id",
default="",
)
parser.add_argument(
"--operator",
default="",
)
parser.add_argument(
"--notes",
default="",
)
parser.add_argument(
"--check-only",
action="store_true",
help="Valida e imprime o fragmento, mas não salva.",
)
args = parser.parse_args()
rows, actual_mx, usb_speed = discover_hardware(args.mx_id)
resolved = validate_and_resolve_hardware(rows)
current_signature = hardware_signature_from_resolved(resolved)
radiometry = load_json(args.radiometry_json)
rn = validate_radiometry_artifact(
radiometry,
current_signature,
)
reference_controls = rn["reference_controls"]
camera_settings = build_camera_settings(reference_controls)
rgb_calibration = build_rgb_calibration_policy()
module_params_fragment = {
"camera_settings": camera_settings,
"rgb_calibration": {
"enabled": False,
"gains": {
"R": 1.0,
"G": 1.0,
"B": 1.0,
},
},
}
audit = compare_existing_module_params(
args.compare_module_params,
module_params_fragment,
)
payload = {
"schema": SCHEMA,
"created_at": now_str(),
"status": "good",
"promoted": not args.check_only,
"runtime_effect": (
"startup_camera_policy_and_explicit_rgb_gain_disable"
),
"device_mx_id": actual_mx,
"usb_speed": usb_speed,
"hardware_inventory": rows,
"hardware_signature": current_signature,
"source_radiometry": {
"path": str(args.radiometry_json),
"schema": radiometry.get("schema"),
"session_id": radiometry.get("session_id"),
"status": radiometry.get("status"),
"promoted": radiometry.get("promoted"),
"reference_controls": reference_controls,
},
"startup_policy": {
"mode": "ae_with_calibrated_manual_fallback",
"rgb_awb": "auto_for_isp_preview_only",
"spectral_awb": "off_not_applicable",
"manual_fallback_source": (
"radiometric_normalization.reference_controls"
),
"note": (
"With AE enabled, manager stores exposure/gain as fallback "
"and does not force them manually at startup."
),
},
"rgb_calibration_policy": rgb_calibration,
"module_params_fragment": module_params_fragment,
"existing_module_params_audit": audit,
"traceability": {
"module_id": args.module_id or None,
"operator": args.operator or None,
},
"notes": args.notes or "",
}
print("=" * 82)
print("CAMERA STARTUP PROFILE - PRODUCTION")
print(f"MX ID : {actual_mx}")
print(f"USB : {usb_speed}")
print("-" * 82)
for role in ROLES:
c = resolved[role]
s = camera_settings[role]
print(
f"{c.socket} -> {role.upper():3s} | "
f"{c.sensor:8s} | {c.width}x{c.height} | "
f"AE={s['ae_enable']} AWB={s['awb_enable']} | "
f"fallback={s['exposure_time_us']}us gain={s['analogue_gain']:.3f}"
)
print("-" * 82)
print("rgb_calibration: DISABLED | gains = 1.0 / 1.0 / 1.0")
if audit.get("available"):
print(
"module_params audit: "
f"camera_settings_equal={audit['camera_settings_equal']} | "
f"rgb_calibration_equal={audit['rgb_calibration_equal']}"
)
else:
print("module_params audit: arquivo não disponível")
if args.check_only:
print("[CHECK-ONLY] Nenhum arquivo alterado.")
print("=" * 82)
return
out = Path(args.out_json)
ensure_dir(out.parent)
if out.exists():
backup = out.with_name(
out.stem
+ f".backup_{stamp()}"
+ out.suffix
)
shutil.copy2(out, backup)
save_json_atomic(out, payload)
print(f"[PASS] Artefato salvo: {out}")
print("[ASSEMBLER] Consumir module_params_fragment deste JSON.")
print("=" * 82)
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,654 +0,0 @@
import json
import argparse
import os
from copy import deepcopy
from datetime import datetime
# ============================================================
# Helpers
# ============================================================
def now_str():
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def load_json(path, required=True):
if not path or not os.path.isfile(path):
if required:
raise FileNotFoundError(f"Arquivo não encontrado: {path}")
return {}
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def save_json(path, data):
out_dir = os.path.dirname(os.path.abspath(path))
if out_dir:
os.makedirs(out_dir, exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
f.write("\n")
def rel_or_abs(path):
"""
Mantém o caminho como veio, mas normaliza separadores.
Isso evita quebrar projetos Windows/Linux e deixa o module_params legível.
"""
if path is None:
return None
return str(path).replace("\\", "/")
def deep_merge(base, update, *, skip_none=True):
"""
Merge recursivo seguro.
- dict + dict: combina recursivamente.
- listas/escalares: valor novo substitui o antigo.
- None: por padrão NÃO apaga valor antigo, para evitar perder calibração quando
um arquivo fonte não conhece determinada chave.
"""
if not isinstance(base, dict):
base = {}
out = deepcopy(base)
if not isinstance(update, dict):
return out
for key, value in update.items():
if value is None and skip_none:
continue
if isinstance(value, dict) and isinstance(out.get(key), dict):
out[key] = deep_merge(out[key], value, skip_none=skip_none)
else:
out[key] = deepcopy(value)
return out
def first_dict(*values):
for value in values:
if isinstance(value, dict):
return value
return None
# ============================================================
# Defaults coerentes com RawProcessorCore + module_params atual
# ============================================================
DEFAULT_RGB_PROCESSING = {
"mode": "bayer_planes",
}
DEFAULT_PATCH_NORMALIZATION = {
"enabled": True,
"apply_when_metering_mode": "reference_patches",
"apply_stage": "after_fusion",
"method": "gray_scale_with_white_guard",
"space": "multispec_tensor",
"targets_by_patch_channel": {
"black": {
"R": 0.06,
"G": 0.06,
"B": 0.06,
"RE": 0.06,
"NIR": 0.06,
},
"gray": {
"R": 0.34,
"G": 0.34,
"B": 0.34,
"RE": 0.24,
"NIR": 0.30,
},
"white": {
"R": 0.78,
"G": 0.78,
"B": 0.78,
"RE": 0.78,
"NIR": 0.78,
},
},
"white_guard_max": 0.92,
"white_guard_max_by_channel": {
"R": 0.92,
"G": 0.92,
"B": 0.92,
"RE": 0.88,
"NIR": 0.88,
},
"scale_min": 0.35,
"scale_max": 2.5,
"clip_output": True,
"require_valid_gray": True,
"use_black_for_offset": False,
"save_patch_stats": True,
"rgb_saturation_guard_enabled": True,
"rgb_saturation_guard_mode": "fade_strength",
"rgb_saturation_soft_start": 0.88,
"rgb_saturation_hard": 0.97,
"rgb_saturation_threshold": 0.97,
}
DEFAULT_FLATFIELD_RUNTIME = {
"strength": 0.35,
"strength_by_channel": {
"R": 0.9,
"G": 0.9,
"B": 0.9,
"RE": 0.25,
"NIR": 0.25,
},
"gain_min_runtime": 0.75,
"gain_max_runtime": 1.35,
"runtime_smooth_ksize": 81,
"saturation_guard_enabled": True,
"saturation_guard_mode": "fade_strength",
"saturation_guard_threshold": 0.97,
"saturation_guard_soft_start": 0.88,
"saturation_guard_hard": 0.97,
}
DEFAULT_RADIOMETRIC_NORMALIZATION = {
"enabled": False,
"method": "exposure_gain_reference",
"apply_stage": "after_dark_before_flat_gain",
"reference_controls": {
"rgb": {"exposure_time_us": 3000, "analogue_gain": 1.0},
"re": {"exposure_time_us": 7000, "analogue_gain": 1.0},
"nir": {"exposure_time_us": 7000, "analogue_gain": 1.0},
},
"clip_output": True,
}
DEFAULT_RADIOMETRIC_CONFIG = {
"enabled": True,
"interval_s": 0.25,
"verbose": True,
"metering_mode": "reference_patches",
"spectral_control_mode": "shared",
"control_metric": "p50",
"target_value": 0.5,
"deadband": 0.055,
"p95_limit": 0.975,
"saturation_limit_pct": 5.0,
"alpha": 0.18,
"exp_step_gain": 0.55,
"prefer_exposure": True,
"exp_min_us": 100,
"exp_max_us": 80000,
"gain_min": 1.0,
"gain_max": 4.0,
"exp_apply_threshold_us": 15,
"gain_apply_threshold": 0.05,
"apply_same_spectral_to_both": True,
"spectral_roles": ["re", "nir"],
"dark_limit_pct": 35.0,
"control_strategy": "ratio",
"ratio_alpha": 0.42,
"ratio_min": 0.72,
"ratio_max": 1.38,
"reduce_fast_factor": 0.8,
"factor_min": 0.62,
"factor_max": 1.42,
"gain_return_enabled": True,
"gain_reduce_on_saturation": True,
"gain_increase_required_cycles": 3,
"gain_decrease_required_cycles": 1,
"gain_step_up": 0.3,
"gain_step_down": 0.5,
"gain_hard_reset_on_saturation": False,
"exp_high_ratio_for_gain": 0.95,
"exp_low_ratio_for_gain_return": 0.75,
"role_limits": {
"rgb": {"exp_min_us": 100, "exp_max_us": 80000, "gain_min": 1.0, "gain_max": 2.0},
"re": {"exp_min_us": 100, "exp_max_us": 3500, "gain_min": 1.0, "gain_max": 2.0},
"nir": {"exp_min_us": 100, "exp_max_us": 3500, "gain_min": 1.0, "gain_max": 2.0},
},
"ready_required_cycles": 3,
"patch_control_mode": "gray_primary",
"patch_require_order": True,
"patch_min_separation": 0.08,
"patch_white_sat_limit_pct": 5.0,
"patch_white_p95_limit": 0.985,
"patch_black_dark_limit_pct": 80.0,
"patch_black_max_p50": 0.2,
"patch_gray_min_p50": 0.08,
"patch_gray_max_p50": 0.85,
"patch_roi_contract": "multi_roi_by_role_v1",
"patch_roi_reduce_method": "median_valid_rois",
"patch_roi_outlier_reject": True,
"patch_roi_max_p50_delta": 0.12,
"global_saturation_guard_enabled": True,
"global_guard_roi_pct": {"x0": 0.05, "y0": 0.05, "x1": 0.95, "y1": 0.76},
"global_guard_sat_threshold": 0.985,
"global_guard_near_sat_threshold": 0.94,
"global_guard_sat_pct_soft": 0.50,
"global_guard_sat_pct_hard": 1.5,
"global_guard_sat_pct_extreme": 5.0,
"global_guard_blob_pct_soft": 0.20,
"global_guard_blob_pct_hard": 0.80,
"global_guard_blob_pct_extreme": 2.2,
"global_guard_min_blob_px": 48,
"global_guard_downsample_max_side": 320,
"global_guard_reduce_factor_soft": 0.96,
"global_guard_reduce_factor_hard": 0.82,
"global_guard_reduce_factor_extreme": 0.62,
"sun_guard_enabled": True,
"sun_guard_p99_threshold": 0.96,
"sun_guard_near_sat_pct_threshold": 2.0,
"sun_guard_freeze_increase_cycles": 1,
"sun_guard_allow_decrease": True,
"guard_force_apply_enabled": True,
"guard_force_apply_soft": False,
"guard_force_apply_hard": True,
"guard_force_apply_extreme": True,
"guard_force_apply_on_patch_saturation": True,
"guard_freeze_cycles_soft": 1,
"guard_freeze_cycles_hard": 2,
"guard_freeze_cycles_extreme": 3,
"guard_reapply_min_exp_on_emergency": True,
"guard_min_exp_margin_us": 80,
"patch_two_roi_soften_risk": True,
"patch_two_roi_white_risk_percentile": 75,
"patch_two_roi_other_risk_percentile": 50,
"patch_white_single_roi_saturation_reject": True,
"patch_white_roi_reject_sat_pct": 5.0,
"patch_white_roi_reject_p95": 0.995,
}
def default_module_template():
return {
"schema": "multispec_module_params_v3",
"saved_at": now_str(),
"frame_type": "RAW_BRUTO",
"capture_mode_requested": "AUTO",
"capture_mode_effective": "AUTO",
"raw_policy": "allow_single",
"sensor_width": 1280,
"sensor_height": 800,
"bayer_pattern": "BGGR",
"rgb_processing": deepcopy(DEFAULT_RGB_PROCESSING),
"camera_settings": {},
"fusion_config": {
"alignment_mode": "manual_affine",
"baseline_mm": 75.0,
"reference_camera": "rgb",
"manual_offsets": {
"re": {"dx": 0, "dy": 0, "theta_deg": 0.0},
"nir": {"dx": 0, "dy": 0, "theta_deg": 0.0},
},
"homographies": {
"re_to_rgb": None,
"nir_to_rgb": None,
},
"crop_valid_common": True,
"resize_after_crop": True,
"target_size": None,
},
"radiometric_config": deepcopy(DEFAULT_RADIOMETRIC_CONFIG),
"radiometric_normalization": deepcopy(DEFAULT_RADIOMETRIC_NORMALIZATION),
"patch_normalization": deepcopy(DEFAULT_PATCH_NORMALIZATION),
"rgb_calibration": {
"enabled": False,
"gains": {"R": 1.0, "G": 1.0, "B": 1.0},
},
"flatfield_config": deep_merge(
{
"enabled": False,
"reason": "flatfield não informado ou arquivo inexistente",
"subtract_dark": True,
"apply_before_fusion": True,
"apply_after_decode": True,
"apply_space": "native_camera_space",
"map_type": "gain",
"channels": ["R", "G", "B", "RE", "NIR"],
"channel_maps": {},
"clip_output": True,
},
DEFAULT_FLATFIELD_RUNTIME,
),
}
# ============================================================
# Builders / normalizers
# ============================================================
def build_flatfield_config(flatfield_json_path, flatfield_data, previous_flatfield_config=None):
"""
Espera o JSON gerado pelo flatfield_calibration_tool_v2.py.
Importante: usa previous_flatfield_config como base para preservar knobs runtime
que não existem no arquivo de calibração do flat-field, como strength,
gain_min_runtime, runtime_smooth_ksize e saturation_guard_*.
"""
base = deep_merge(
deep_merge({}, previous_flatfield_config or {}),
DEFAULT_FLATFIELD_RUNTIME,
)
if not isinstance(flatfield_data, dict):
return deep_merge(base, {
"enabled": False,
"reason": "flatfield_json ausente ou inválido",
})
outputs = flatfield_data.get("outputs", {}) or {}
maps = flatfield_data.get("maps", {}) or {}
npz_path = outputs.get("npz")
if not npz_path:
base_name, _ = os.path.splitext(flatfield_json_path)
npz_path = base_name + ".npz"
channels = flatfield_data.get("channels") or base.get("channels") or ["R", "G", "B", "RE", "NIR"]
channel_maps = {}
previous_channel_maps = base.get("channel_maps", {}) or {}
for ch in channels:
m = maps.get(ch, {}) or {}
prev = previous_channel_maps.get(ch, {}) or {}
channel_maps[ch] = deep_merge(prev, {
"gain_key": m.get("gain_key", f"gain_{ch}"),
"flat_norm_key": m.get("flat_norm_key", f"flat_norm_{ch}"),
"white_median_key": m.get("white_median_key", f"white_median_{ch}"),
"dark_median_key": m.get("dark_median_key", f"dark_median_{ch}"),
"shape": m.get("shape"),
"gain_min": m.get("gain_min"),
"gain_max": m.get("gain_max"),
"gain_mean": m.get("gain_mean"),
"gain_std": m.get("gain_std"),
})
generated = {
"enabled": True,
"subtract_dark": True,
"schema": flatfield_data.get("schema", "multispec_flatfield_v1"),
"created_at": flatfield_data.get("created_at"),
"json_file": rel_or_abs(flatfield_json_path),
"npz_file": rel_or_abs(npz_path),
"apply_before_fusion": True,
"apply_after_decode": True,
"apply_space": "native_camera_space",
"map_type": "gain",
"formula": "channel_corrected = max(channel_linear - dark, 0) * gain_map",
"channels": channels,
"channel_maps": channel_maps,
"exp_gain_correct_during_flat_capture": bool(flatfield_data.get("exp_gain_correct", False)),
"smooth_ksize": flatfield_data.get("smooth_ksize"),
"min_gain": flatfield_data.get("min_gain"),
"max_gain": flatfield_data.get("max_gain"),
"notes": flatfield_data.get("notes", ""),
}
return deep_merge(base, generated)
def pick_radiometric_config(radiometric_data: dict, selected_profile: str | None = None):
if not isinstance(radiometric_data, dict):
return None
root_cfg = radiometric_data.get("radiometric_config")
if isinstance(root_cfg, dict):
return root_cfg
active_profile = radiometric_data.get("active_profile")
if active_profile in ("global_scene_mode", "three_reference_patches_mode"):
cfg = radiometric_data.get(active_profile, {}).get("radiometric_config")
if isinstance(cfg, dict):
return cfg
if selected_profile:
cfg = radiometric_data.get(selected_profile, {}).get("radiometric_config")
if isinstance(cfg, dict):
return cfg
return None
def normalize_patch_normalization_contract(base_patch_config, incoming_patch_config=None):
"""
Garante o contrato atual do RawProcessorCore.
- Sempre tem targets_by_patch_channel.
- Preserva white_guard_max_by_channel.
- Preserva rgb_saturation_guard_*.
- Remove a chave legada targets, porque ela não é usada pelo core atual.
"""
cfg = deep_merge(DEFAULT_PATCH_NORMALIZATION, base_patch_config or {})
cfg = deep_merge(cfg, incoming_patch_config or {})
legacy_targets = cfg.pop("targets", None)
if isinstance(legacy_targets, dict) and "targets_by_patch_channel" not in cfg:
# Fallback conservador. Na prática, com DEFAULT_PATCH_NORMALIZATION acima,
# normalmente não entra aqui. Mantido só para arquivos muito antigos.
t = deepcopy(DEFAULT_PATCH_NORMALIZATION["targets_by_patch_channel"])
for patch_type in ("black", "gray", "white"):
if patch_type in legacy_targets:
scalar = legacy_targets.get(patch_type)
try:
scalar = float(scalar)
for ch in ("R", "G", "B", "RE", "NIR"):
t[patch_type][ch] = scalar
except Exception:
pass
cfg["targets_by_patch_channel"] = t
cfg = deep_merge(DEFAULT_PATCH_NORMALIZATION, cfg)
return cfg
def normalize_radiometric_config(base_rad_config, incoming_rad_config=None):
cfg = deep_merge(DEFAULT_RADIOMETRIC_CONFIG, base_rad_config or {})
cfg = deep_merge(cfg, incoming_rad_config or {})
return cfg
def normalize_radiometric_normalization(base_config, incoming_config=None):
cfg = deep_merge(DEFAULT_RADIOMETRIC_NORMALIZATION, base_config or {})
cfg = deep_merge(cfg, incoming_config or {})
cfg["enabled"] = bool(cfg.get("enabled", False))
return cfg
def build_fusion_config(fusion_data, previous_fusion_config=None):
base = previous_fusion_config or {}
generated = {
"alignment_mode": fusion_data.get("alignment_mode"),
"baseline_mm": fusion_data.get("baseline_mm"),
"reference_camera": fusion_data.get("reference_camera"),
"manual_offsets": fusion_data.get("manual_offsets"),
"homographies": fusion_data.get("homographies"),
"crop_valid_common": fusion_data.get("crop_valid_common"),
"resize_after_crop": fusion_data.get("resize_after_crop"),
"target_size": fusion_data.get("target_size"),
}
cfg = deep_merge(default_module_template()["fusion_config"], base)
cfg = deep_merge(cfg, generated)
return cfg
def load_base_module(args):
"""
Carrega defaults do module_params atual.
Prioridade:
1. --base_module_json, se informado.
2. --out, se já existir.
3. template interno coerente com o contrato atual.
"""
candidates = []
if args.base_module_json:
candidates.append(args.base_module_json)
if args.out:
candidates.append(args.out)
for path in candidates:
if path and os.path.isfile(path):
print(f"[INFO] Usando module_params base: {path}")
return deep_merge(default_module_template(), load_json(path, required=True))
print("[WARN] Nenhum module_params base encontrado. Usando defaults internos.")
return default_module_template()
# ============================================================
# Main
# ============================================================
def main():
parser = argparse.ArgumentParser(
description="Monta o module_params.json preservando o contrato atual do RawProcessorCore.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument("--camera_json", default="calibration/sensor_calibration.json")
parser.add_argument("--fusion_json", default="calibration/manual_offsets.json")
parser.add_argument("--radiometric_json", default="calibration/radiometric_config.json")
parser.add_argument(
"--radiometric_profile",
default="global_scene_mode",
choices=["global_scene_mode", "three_reference_patches_mode"],
)
parser.add_argument("--flatfield_json", default="calibration/flatfield_maps_v1.json")
parser.add_argument("--disable_flatfield", action="store_true")
parser.add_argument("--base_module_json", default=None, help="module_params atual usado como defaults antes de sobrescrever")
parser.add_argument("--out", default="calibration/module_params.json")
args = parser.parse_args()
module_base = load_base_module(args)
cam_data = load_json(args.camera_json, required=True)
fusion_data = load_json(args.fusion_json, required=True)
radiometric_data = load_json(args.radiometric_json, required=False) if args.radiometric_json else {}
# =========================
# MODULE PARAMS FINAL
# =========================
module_params = deepcopy(module_base)
module_params["schema"] = "multispec_module_params_v3"
module_params["saved_at"] = now_str()
# =========================
# ROOT / CAMERA
# =========================
root_updates = {
"frame_type": cam_data.get("frame_type", fusion_data.get("frame_type")),
"capture_mode_requested": cam_data.get("capture_mode_requested"),
"capture_mode_effective": cam_data.get("capture_mode_effective"),
"raw_policy": cam_data.get("raw_policy"),
"sensor_width": cam_data.get("sensor_width", fusion_data.get("sensor_width")),
"sensor_height": cam_data.get("sensor_height", fusion_data.get("sensor_height")),
"bayer_pattern": cam_data.get("bayer_pattern", fusion_data.get("bayer_pattern")),
}
module_params = deep_merge(module_params, root_updates)
module_params["rgb_processing"] = deep_merge(
deep_merge(DEFAULT_RGB_PROCESSING, module_base.get("rgb_processing", {})),
cam_data.get("rgb_processing") if isinstance(cam_data.get("rgb_processing"), dict) else {},
)
camera_settings = cam_data.get("camera_settings")
if not isinstance(camera_settings, dict):
camera_settings = module_base.get("camera_settings")
if not isinstance(camera_settings, dict):
raise RuntimeError("camera_json sem camera_settings válido e sem fallback no module_params base")
module_params["camera_settings"] = camera_settings
module_params["rgb_calibration"] = deep_merge(
module_base.get("rgb_calibration", {}),
cam_data.get("rgb_calibration") if isinstance(cam_data.get("rgb_calibration"), dict) else {},
)
# =========================
# FUSION
# =========================
module_params["fusion_config"] = build_fusion_config(
fusion_data,
previous_fusion_config=module_base.get("fusion_config", {}),
)
# =========================
# RADIOMETRIC
# =========================
incoming_rad = pick_radiometric_config(radiometric_data, selected_profile=args.radiometric_profile)
if not isinstance(incoming_rad, dict):
incoming_rad = cam_data.get("radiometric_config") if isinstance(cam_data.get("radiometric_config"), dict) else {}
module_params["radiometric_config"] = normalize_radiometric_config(
module_base.get("radiometric_config", {}),
incoming_rad,
)
incoming_rad_norm = first_dict(
radiometric_data.get("radiometric_normalization") if isinstance(radiometric_data, dict) else None,
cam_data.get("radiometric_normalization") if isinstance(cam_data, dict) else None,
) or {}
module_params["radiometric_normalization"] = normalize_radiometric_normalization(
module_base.get("radiometric_normalization", {}),
incoming_rad_norm,
)
incoming_patch_norm = first_dict(
radiometric_data.get("patch_normalization") if isinstance(radiometric_data, dict) else None,
cam_data.get("patch_normalization") if isinstance(cam_data, dict) else None,
) or {}
module_params["patch_normalization"] = normalize_patch_normalization_contract(
module_base.get("patch_normalization", {}),
incoming_patch_norm,
)
# =========================
# FLATFIELD
# =========================
previous_flatfield = module_base.get("flatfield_config", {}) or {}
if args.disable_flatfield:
module_params["flatfield_config"] = deep_merge(previous_flatfield, {
"enabled": False,
"reason": "desabilitado via --disable_flatfield",
})
elif args.flatfield_json and os.path.isfile(args.flatfield_json):
flatfield_data = load_json(args.flatfield_json, required=True)
module_params["flatfield_config"] = build_flatfield_config(
args.flatfield_json,
flatfield_data,
previous_flatfield_config=previous_flatfield,
)
else:
# Não achou novo flatfield: preserva o anterior se já existia.
module_params["flatfield_config"] = deep_merge(previous_flatfield, DEFAULT_FLATFIELD_RUNTIME)
if not module_params["flatfield_config"].get("enabled", False):
module_params["flatfield_config"]["reason"] = "flatfield não informado ou arquivo inexistente"
# =========================
# Save
# =========================
save_json(args.out, module_params)
print(f"[OK] module_params gerado em: {args.out}")
print("[OK] contrato preservado: rgb_processing, patch_normalization, radiometric_config e knobs runtime do flatfield")
flat_cfg = module_params.get("flatfield_config", {}) or {}
if flat_cfg.get("enabled"):
print(f"[OK] flatfield habilitado: {flat_cfg.get('npz_file')}")
else:
print(f"[WARN] flatfield desabilitado: {flat_cfg.get('reason')}")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff