如何对OpenCV帧哈希以缓存Python函数?解决numpy不可哈希问题
解决numpy数组无法被functools.cache缓存的问题
针对你遇到的unhashable type: 'numpy.ndarray'错误,核心原因是numpy数组属于不可哈希类型,而functools.cache依赖参数的哈希值存储缓存结果。下面提供几种可行的解决思路:
方法一:将numpy数组转换为可哈希类型
把帧数组转换成bytes、元组这类可哈希格式,作为缓存函数的参数。如果你的frames集合是固定的,可以预先转换一次,避免重复开销:
import functools import cv2 import numpy as np def array_to_hashable(arr): # 将数组转为bytes,同时记录形状和dtype用于还原 return arr.tobytes(), arr.shape, arr.dtype # 预先处理固定的frames集合,转为可哈希的元组 frames_hashed = tuple(array_to_hashable(frame) for frame in frames) @functools.cache def _closest_frame(frame_bytes, frame_shape, frame_dtype): # 还原输入帧 frame = np.frombuffer(frame_bytes, dtype=frame_dtype).reshape(frame_shape) # 还原所有候选帧 candidate_frames = [np.frombuffer(b, dtype=d).reshape(s) for b, s, d in frames_hashed] # 寻找最匹配的帧 return min(candidate_frames, key=lambda x: cv2.norm(frame, x, cv2.NORM_L2)) def closest_frame(frame): # 转换输入帧为可哈希格式 frame_hashable = array_to_hashable(frame) return _closest_frame(*frame_hashable)
方法二:提取帧特征降低计算成本(更推荐)
直接计算整帧的L2距离成本极高,尤其当frames数量很大时。可以先提取帧的低维特征(比如直方图、ORB特征),用特征匹配代替整帧匹配,同时特征更容易转为可哈希类型:
示例:用直方图特征匹配
import functools import cv2 import numpy as np def extract_frame_feature(frame): # 转换为HSV色彩空间,提取三维直方图作为特征 hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) hist = cv2.calcHist([hsv_frame], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256]) # 归一化特征并转为可哈希的元组 cv2.normalize(hist, hist) return tuple(hist.flatten()) # 预先提取所有候选帧的特征和对应帧 preprocessed_frames = [(extract_frame_feature(f), f) for f in frames] # 拆分特征列表和帧列表(转为元组保证可哈希) feature_list = tuple(f[0] for f in preprocessed_frames) frame_list = tuple(f[1] for f in preprocessed_frames) @functools.cache def _closest_frame(frame_feature): # 找到特征距离最小的帧索引 match_idx = min(range(len(feature_list)), key=lambda i: cv2.norm(frame_feature, feature_list[i], cv2.NORM_L2)) return frame_list[match_idx] def closest_frame(frame): frame_feature = extract_frame_feature(frame) return _closest_frame(frame_feature)
这种方法不仅解决了缓存问题,还大幅降低了每次匹配的计算量,适合处理大规模帧集合。
方法三:自定义缓存装饰器(灵活处理数组哈希)
如果你想保留原函数的参数形式,可以写一个自定义装饰器,自动将numpy数组转为可哈希格式:
import functools import cv2 import numpy as np def hashable_array_decorator(func): @functools.wraps(func) def wrapper(frame, frames): # 转换输入帧为可哈希格式 frame_hash = (frame.tobytes(), frame.shape, frame.dtype) # 转换frames为可哈希的元组集合 frames_hash = tuple((f.tobytes(), f.shape, f.dtype) for f in frames) # 调用缓存的内部函数 return _cached_func(frame_hash, frames_hash) @functools.cache def _cached_func(frame_hash, frames_hash): # 还原帧 frame = np.frombuffer(frame_hash[0], dtype=frame_hash[2]).reshape(frame_hash[1]) frames = [np.frombuffer(h[0], dtype=h[2]).reshape(h[1]) for h in frames_hash] return min(frames, key=lambda x: cv2.norm(frame, x, cv2.NORM_L2)) return wrapper @hashable_array_decorator def closest_frame(frame, frames): # 原函数逻辑被移到_cached_func中,此处可留空或添加注释 pass
这种方式对原函数的调用方式无影响,但如果frames是动态变化的,缓存命中率会很低,建议仅在frames固定时使用。
内容的提问来源于stack exchange,提问作者kevinlinxc
相关产品推荐
相关产品推荐

