使用Python Ctypes获取Futronic FS88h指纹扫描仪图像
问题核心原因
你当前无法获取图像的问题出在两个地方:
- 对
ftrScanGetImage的接口定义错误:该接口并非仅接收设备句柄、返回布尔值,而是需要调用方提前传入预分配好的图像缓冲区指针,布尔返回值仅代表采集是否成功。你代码里还误将restype写为restypes,会导致ctypes解析返回值异常。 - 未提前获取图像尺寸参数:不知道单帧图像占用的内存大小,无法分配长度匹配的缓冲区,和你找到的C示例里先获取图像尺寸再分配内存的逻辑对应。
实现步骤
- 补全SDK依赖的基础类型、图像尺寸结构体定义,避免因结构体内存对齐错误导致调用崩溃。
- 打开设备后先调用
ftrScanGetImageSize接口,获取采集图像的宽、高、单帧字节数。Futronic FS88h输出的是8位单通道灰度图,无需额外做色彩空间转换。 - 按照获取到的单帧大小,创建ctypes的
c_ubyte数组作为接收缓冲区。 - 检测到手指放置后,将设备句柄、缓冲区指针传入
ftrScanGetImage接口完成采集。如果需要未经过SDK优化的原始裸数据,可以替换为C示例中提到的ftrScanGetFrame接口,两者参数完全一致。 - 采集成功后将缓冲区二进制数据转换为图像数组,用PIL或OpenCV保存为本地可查看的图片文件即可。
- 操作完成后记得调用
ftrScanCloseDevice关闭设备句柄,避免占用设备资源。
完整可运行代码
from ctypes import windll, wintypes import ctypes import time import numpy as np from PIL import Image # 加载SDK lib = ctypes.WinDLL('ftrScanAPI.dll') # 补全基础类型定义 FTRHANDLE = ctypes.c_void_p FTR_BYTE = ctypes.c_ubyte FTR_WORD = ctypes.c_ushort FTR_DWORD = ctypes.c_ulong FTR_BOOL = wintypes.BOOL # 结构体定义 class FTRSCAN_DEVICE_INFO(ctypes.Structure): _fields_ = [ ("dwStructSize", FTR_DWORD), ("byDeviceCompatibility", FTR_BYTE), ("wPixelSizeX", FTR_WORD), ("wPixelSizeY", FTR_WORD) ] PFTRSCAN_DEVICE_INFO = ctypes.POINTER(FTRSCAN_DEVICE_INFO) class FTRSCAN_FAKE_REPLICA_PARAMETERS(ctypes.Structure): _fields_ = [ ("bCalculated", FTR_BOOL), ("nCalculatedSum1", ctypes.c_int), ("nCalculatedSumFuzzy", ctypes.c_int), ("nCalculatedSumEmpty", ctypes.c_int), ("nCalculatedSum2", ctypes.c_int), ("dblCalculatedTremor", ctypes.c_double), ("dblCalculatedValue", ctypes.c_double), ] PFTRSCAN_FAKE_REPLICA_PARAMETERS = ctypes.POINTER(FTRSCAN_FAKE_REPLICA_PARAMETERS) class FTRSCAN_FRAME_PARAMETERS(ctypes.Structure): _fields_ = [ ("nContrastOnDose2", ctypes.c_int), ("nContrastOnDose4", ctypes.c_int), ("nDose", ctypes.c_int), ("nBrightnessOnDose1", ctypes.c_int), ("nBrightnessOnDose2", ctypes.c_int), ("nBrightnessOnDose3", ctypes.c_int), ("nBrightnessOnDose4", ctypes.c_int), ("FakeReplicaParams", FTRSCAN_FAKE_REPLICA_PARAMETERS), ] PFTRSCAN_FRAME_PARAMETERS = ctypes.POINTER(FTRSCAN_FRAME_PARAMETERS) # 图像尺寸结构体 class FTRSCAN_IMAGE_SIZE(ctypes.Structure): _fields_ = [ ("nWidth", ctypes.c_int), ("nHeight", ctypes.c_int), ("nImageSize", ctypes.c_int) ] if __name__ == "__main__": # 打开设备 lib.ftrScanOpenDevice.argtypes = [] lib.ftrScanOpenDevice.restype = FTRHANDLE h_device = lib.ftrScanOpenDevice() if not h_device: raise RuntimeError("设备打开失败,请检查连接和驱动") # 获取图像尺寸 img_size = FTRSCAN_IMAGE_SIZE() lib.ftrScanGetImageSize.argtypes = [FTRHANDLE, ctypes.POINTER(FTRSCAN_IMAGE_SIZE)] lib.ftrScanGetImageSize.restype = FTR_BOOL if not lib.ftrScanGetImageSize(h_device, ctypes.byref(img_size)): lib.ftrScanCloseDevice(h_device) raise RuntimeError("获取图像尺寸失败") print(f"图像参数:宽{img_size.nWidth}px,高{img_size.nHeight}px,单帧大小{img_size.nImageSize}字节") # 分配图像缓冲区 img_buffer = (ctypes.c_ubyte * img_size.nImageSize)() frame_parameters = FTRSCAN_FRAME_PARAMETERS() # 配置接口参数 lib.ftrScanIsFingerPresent.argtypes = [FTRHANDLE, PFTRSCAN_FRAME_PARAMETERS] lib.ftrScanIsFingerPresent.restype = FTR_BOOL lib.ftrScanGetImage.argtypes = [FTRHANDLE, ctypes.POINTER(ctypes.c_ubyte)] lib.ftrScanGetImage.restype = FTR_BOOL lib.ftrScanCloseDevice.argtypes = [FTRHANDLE] lib.ftrScanCloseDevice.restype = FTR_BOOL try: print("请将手指放在扫描仪上...") while True: if lib.ftrScanIsFingerPresent(h_device, ctypes.byref(frame_parameters)): # 延时100ms等帧稳定 time.sleep(0.1) ret = lib.ftrScanGetImage(h_device, img_buffer) if ret: # 转换为图像文件 img_arr = np.frombuffer(img_buffer, dtype=np.uint8).reshape((img_size.nHeight, img_size.nWidth)) img = Image.fromarray(img_arr, mode='L') img.save("fingerprint.bmp") print("指纹图像已保存为 fingerprint.bmp") break else: print("采集失败,请重新放置手指") time.sleep(0.05) finally: # 确保设备被关闭 lib.ftrScanCloseDevice(h_device)
注意事项
- 如果需要获取SDK未做降噪、对比度调整的原始采集数据,把代码里的
ftrScanGetImage替换为ftrScanGetFrame即可,参数定义无需修改。 - 不要将图像按RGB三通道解析,FS88h输出的是单通道8位灰度数据,按三通道解析会出现花屏、尺寸不匹配问题。
- 运行代码前确保
ftrScanAPI.dll放在代码同级目录或者系统System32路径下,同时安装好设备官方驱动,否则会出现加载dll失败、找不到设备的问题。
内容的提问来源于stack exchange,提问作者Eduardo Aldana
相关产品推荐
相关产品推荐

