agrobot_base/Python/gal5000/gal5000_test_params.py

251 lines
7.6 KiB
Python

import math
import os
import time
import ctypes as C
from ctypes import wintypes as W
import numpy as np
import cv2
# =========================
# CONFIG
# =========================
SDK_DIR = os.path.join(os.path.dirname(__file__), "dlls")
DLL_NAME = "VT_SDK64.dll"
TIMEOUT_MS = 2000
WINDOW_NAME = "GAL5000 4CH Preview (E=AEC toggle, G=AGC toggle, Q=quit)"
# Camera scan/open
DEVICE_UDEF = 0
DEVICE_INDEX = 0
DATA_RAW = 0
# RAW geometry (como você já capturou)
RAW_W = 2592
RAW_H = 2056
# Bayer+NIR pattern (2x2):
# R G
# IR B
# => R = [0::2,0::2], G=[0::2,1::2], IR=[1::2,0::2], B=[1::2,1::2]
# =========================
# PARAM IDs (VT_Param.h)
# =========================
BUF_SIZE = 256
PARAM_ID_SENSOR_EXPOSURETIMERAW = 0x00003010
PARAM_ID_SENSOR_GAINANALOGRAW = 0x00003020
PARAM_ID_SENSOR_EXPOSUREAUTOENABLE = 0x00003011
PARAM_ID_SENSOR_GAINANALOGAUTOENABLE = 0x00003021
PARAM_ID_SENSOR_GAINANALOGAGCMAX = 0x00003022
PARAM_ID_COMMON_DISPLAYFPS = 0x00000203 # float R (média efetiva) :contentReference[oaicite:9]{index=9}
# PARAM_VALUETYPE
VALUE_INT = 0
VALUE_FLOAT = 1
VALUE_STR = 2
# =========================
# STRUCTS (mínimo necessário)
# =========================
class VT_FRAMEINFO(C.Structure):
_fields_ = [
("lFrameID", W.DWORD),
("lBufSize", W.DWORD),
("lWidth", W.DWORD),
("lHeight", W.DWORD),
("lPixBits", C.c_ubyte),
("_pad0", C.c_ubyte * 3),
("pBufPtr", C.POINTER(C.c_ubyte)),
("lFrameStatus", W.DWORD),
("lPixType", W.DWORD),
("lTimeStamp", W.DWORD),
("_reserve", W.DWORD * 8),
]
class VT_DEVPARAM(C.Structure):
_fields_ = [
("bUseName", W.BOOL),
("lParamByID", W.DWORD),
("lParamByName", C.c_char * BUF_SIZE),
]
def devparam_by_id(pid: int) -> VT_DEVPARAM:
p = VT_DEVPARAM()
p.bUseName = False
p.lParamByID = pid
p.lParamByName = b"" # <- CORRETO: bytes (fica zerado / string vazia)
return p
# =========================
# DLL LOAD + prototypes
# =========================
os.add_dll_directory(SDK_DIR)
dll = C.WinDLL(os.path.join(SDK_DIR, DLL_NAME))
print("DLL carregada OK:", dll)
dll.VT_DeviceScan.argtypes = [C.POINTER(C.c_ubyte), C.c_int]
dll.VT_DeviceScan.restype = C.c_int
dll.VT_DeviceOpen.argtypes = [C.c_void_p, C.POINTER(W.HANDLE), C.c_int, C.c_int]
dll.VT_DeviceOpen.restype = C.c_int
dll.VT_SingleFrameCapture.argtypes = [W.HANDLE, C.POINTER(VT_FRAMEINFO), C.c_int, C.c_int, W.BOOL]
dll.VT_SingleFrameCapture.restype = C.c_int
dll.VT_DeviceClose.argtypes = [C.POINTER(W.HANDLE)]
dll.VT_DeviceClose.restype = C.c_int
# Param API
dll.VT_ParamGetValue.argtypes = [W.HANDLE, VT_DEVPARAM, C.c_void_p, C.c_int]
dll.VT_ParamGetValue.restype = C.c_int
dll.VT_ParamSetValue.argtypes = [W.HANDLE, VT_DEVPARAM, C.c_void_p, C.c_int]
dll.VT_ParamSetValue.restype = C.c_int
def ck(ret: int, name: str):
if ret != 0:
print(f"{name} falhou, ret={ret}")
raise RuntimeError(f"{name} falhou, ret={ret}")
def param_set_int(h: W.HANDLE, pid: int, value: int):
p = devparam_by_id(pid)
v = C.c_int(value)
ret = dll.VT_ParamSetValue(h, p, C.byref(v), VALUE_INT)
ck(ret, f"VT_ParamSetValue({hex(pid)})")
def param_get_int(h: W.HANDLE, pid: int) -> int:
p = devparam_by_id(pid)
v = C.c_int(0)
ret = dll.VT_ParamGetValue(h, p, C.byref(v), VALUE_INT)
ck(ret, f"VT_ParamGetValue({hex(pid)})")
return int(v.value)
def param_get_float(h: W.HANDLE, pid: int) -> float:
p = devparam_by_id(pid)
v = C.c_float(0)
ret = dll.VT_ParamGetValue(h, p, C.byref(v), VALUE_FLOAT)
ck(ret, f"VT_ParamGetValue({hex(pid)})")
return float(v.value)
def set_bool(h: W.HANDLE, pid: int, enabled: bool):
param_set_int(h, pid, 1 if enabled else 0) # boolean no SDK é int 0/1
def param_supported(h, pid, vtype):
p = devparam_by_id(pid)
minv = C.c_int()
maxv = C.c_int()
inc = C.c_int()
ret = dll.VT_ParamGetRange(h, p,
C.byref(minv),
C.byref(maxv),
C.byref(inc),
vtype)
return ret == 0
def capture_raw8(h: W.HANDLE) -> np.ndarray:
fi = VT_FRAMEINFO()
ret = dll.VT_SingleFrameCapture(h, C.byref(fi), DATA_RAW, TIMEOUT_MS, True)
ck(ret, "VT_SingleFrameCapture")
w, hh = int(fi.lWidth), int(fi.lHeight)
if w != RAW_W or hh != RAW_H:
# Se em algum momento você mudar resolução/ROI, aqui te avisa.
print(f"[WARN] Res mudou: {w}x{hh} (esperado {RAW_W}x{RAW_H})")
buf = C.string_at(fi.pBufPtr, fi.lBufSize)
arr = np.frombuffer(buf, dtype=np.uint8)
# garante reshape correto
needed = w * hh
if arr.size < needed:
arr = np.pad(arr, (0, needed - arr.size), mode="constant", constant_values=0)
arr = arr[:needed].reshape(hh, w)
return arr
def make_rgb_preview(raw: np.ndarray, upscale=2):
R = raw[0::2, 0::2]
G = raw[0::2, 1::2]
B = raw[1::2, 1::2]
# normalização leve só para display (p2-p98)
def norm8(x):
lo = np.percentile(x, 2)
hi = np.percentile(x, 98)
if hi <= lo + 1:
return x
y = (x.astype(np.float32) - lo) * (255.0 / (hi - lo))
return np.clip(y, 0, 255).astype(np.uint8)
Rn, Gn, Bn = map(norm8, [R, G, B])
rgb = np.dstack([Bn, Gn, Rn]) # OpenCV = BGR
if upscale and upscale != 1:
rgb = cv2.resize(rgb, (rgb.shape[1]*upscale, rgb.shape[0]*upscale), interpolation=cv2.INTER_NEAREST)
return rgb
def main():
# scan
n = C.c_ubyte(0)
ret = dll.VT_DeviceScan(C.byref(n), DEVICE_UDEF)
ck(ret, "VT_DeviceScan")
if n.value == 0:
raise RuntimeError("Nenhuma câmera encontrada.")
# open
idx = C.c_ubyte(0)
h = W.HANDLE()
ret = dll.VT_DeviceOpen(C.byref(idx), C.byref(h), DEVICE_INDEX, DEVICE_UDEF)
ck(ret, "VT_DeviceOpen")
print("DeviceOpen OK, handle=", h.value)
cv2.namedWindow("RGB", cv2.WINDOW_NORMAL)
aec_on = False
agc_on = False
exp_raw = 1500
gain_a = 0
gain_d = 0
has_hw_aec = param_supported(h, PARAM_ID_SENSOR_EXPOSUREAUTOENABLE, VALUE_INT)
has_hw_agc = param_supported(h, PARAM_ID_SENSOR_GAINANALOGAUTOENABLE, VALUE_INT)
has_exp_raw = param_supported(h, PARAM_ID_SENSOR_EXPOSURETIMERAW, VALUE_INT)
has_gain_a = param_supported(h, PARAM_ID_SENSOR_GAINANALOGRAW, VALUE_INT)
if (has_hw_aec):
aec_on = bool(param_get_int(h, PARAM_ID_SENSOR_EXPOSUREAUTOENABLE))
if (has_hw_agc):
agc_on = bool(param_get_int(h, PARAM_ID_SENSOR_GAINANALOGAUTOENABLE))
if (has_exp_raw):
exp_raw = param_get_int(h, PARAM_ID_SENSOR_EXPOSURETIMERAW)
if (has_gain_a):
gain_a = param_get_int(h, PARAM_ID_SENSOR_GAINANALOGRAW)
print(f"has_hw_aec: {has_hw_aec}, aec_on: {aec_on}\r\nhas_hw_agc: {has_hw_agc}, agc_on: {agc_on}\r\nhas_exp_raw: {has_exp_raw}, exp_raw: {exp_raw}\r\nhas_gain_a: {has_gain_a}, gain_a: {gain_a}")
while True:
raw = capture_raw8(h)
rgb = make_rgb_preview(raw, upscale=2)
cv2.imshow("RGB", rgb)
k = cv2.waitKey(1) & 0xFF
if k in (ord('q'), ord('Q'), 27):
break
elif k in (ord('e'), ord('E')): # exemplo: E alterna AEC do hardware
aec_on = not aec_on
set_bool(h, PARAM_ID_SENSOR_EXPOSUREAUTOENABLE, aec_on)
print("AEC(hw) =", aec_on)
elif k in (ord('g'), ord('G')): # G alterna AGC do hardware
agc_on = not agc_on
set_bool(h, PARAM_ID_SENSOR_GAINANALOGAUTOENABLE, agc_on)
print("AGC(hw) =", agc_on)
if __name__ == "__main__":
main()