You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 05:01:33