如何通过瓦片对比实现图像瓦片列表的缩减?
问题描述
我有一批图像,希望通过生成瓦片列表并对图像建立索引的方式简化图像。目前正以单张图像进行测试,试图找到合适的瓦片对比算法,甚至可能推翻现有方案重新实现。
当前编写了如下代码用于生成瓦片:
def tileize(img): tile_list = [] new_img = np.zeros((int(IMGX/TILESIZE),int(IMGY/TILESIZE),0)) i = 0 ri = 0 for r in range(0,img.shape[0],TILESIZE): cj = 0 for c in range(0,img.shape[1],TILESIZE): t1 = img[r:r+TILESIZE, c:c+TILESIZE,:] new_img[ri,cj] = i tile = Tile(t1) tile_list.append(tile) i+=1 cj +=1 ri +=1 return tile_list,new_img
为了缩减tile_list,我创建了Tile类,期望通过set()去重:
class Tile(object): def __init__(self, mat): self.mat = mat self.hist = cv2.calcHist([mat],[0],None,[256],[0,256]) def __eq__(self, other): res = cv2.compareHist(self.hist,other.hist,cv2.HISTCMP_CORREL) if res > 0.95: return True return False def __hash__(self): return hash(self.hist.flatten())
但该方案无法生效,原因是hist为数组类型,直接对比Tile对象无法得到更短的列表。请问实现瓦片列表缩减的最佳方法是什么?
解决方案
你的方案失效核心原因是哈希值和相等判断逻辑不匹配:__hash__用的是直方图数组的哈希,但__eq__用的是相关性阈值判断,这违反了Python哈希表的规则——如果两个对象a == b为真,那么hash(a)必须等于hash(b)。现在的逻辑里,两个被判定为相等的Tile,哈希值几乎不可能相同,所以set()根本识别不出它们是重复项。
针对瓦片去重/缩减,推荐以下几种可行方案:
1. 修正哈希与相等判断的一致性
如果坚持用set()或字典去重,必须让相等的Tile拥有相同哈希值。可以把直方图做离散化处理,比如:
- 将直方图归一化后,按阈值(比如0.01)把小值置0,保留关键特征
- 将直方图数组转成不可变类型(如元组),再计算哈希,或者直接用离散后的特征作为哈希依据
修改后的Tile类示例:
class Tile(object): def __init__(self, mat): self.mat = mat # 计算直方图并归一化,离散化处理 self.hist = cv2.calcHist([mat],[0],None,[256],[0,256]) self.hist = cv2.normalize(self.hist, self.hist).flatten() # 离散化:把小于阈值的置0,保留显著特征 self.discrete_hist = tuple(v if v > 0.01 else 0 for v in self.hist) def __eq__(self, other): res = cv2.compareHist(self.hist, other.hist, cv2.HISTCMP_CORREL) return res > 0.95 def __hash__(self): # 用离散后的直方图元组计算哈希 return hash(self.discrete_hist)
这种方法能让相似的Tile更可能拥有相同哈希,配合__eq__的阈值判断,set()就能正确去重。
2. 聚类分组缩减列表
如果瓦片相似性是连续的(不是非黑即白的相等),聚类算法更合适:
- 提取所有瓦片的特征(直方图、均值/方差、甚至SIFT特征)
- 用K-Means、DBSCAN等算法将相似瓦片归为一类
- 每个类别只保留一个代表瓦片,实现列表缩减
示例代码思路:
from sklearn.cluster import KMeans # 提取所有瓦片的直方图特征 features = np.array([tile.hist.flatten() for tile in tile_list]) # K-Means聚类,假设分成N类(可根据需求调整) kmeans = KMeans(n_clusters=50, random_state=42).fit(features) # 按聚类标签分组,保留每组第一个瓦片 reduced_tiles = [] seen_labels = set() for tile, label in zip(tile_list, kmeans.labels_): if label not in seen_labels: seen_labels.add(label) reduced_tiles.append(tile)
这种方法比单纯去重更灵活,能根据相似程度批量缩减,适合大规模瓦片处理。
3. 基于特征哈希的快速去重
如果追求效率,可以用**感知哈希(Perceptual Hash)**替代直方图:
- 将瓦片缩放到小尺寸(如8x8),转灰度图
- 计算均值,生成二进制哈希值
- 哈希值相同的瓦片视为相似,直接去重
这种方法计算速度快,哈希值稳定,适合高吞吐量场景。
额外优化建议
- 你的
tileize函数里,new_img的形状定义有问题:np.zeros((int(IMGX/TILESIZE),int(IMGY/TILESIZE),0))最后一维是0,应该改成适合存储索引的类型,比如np.zeros((int(img.shape[0]/TILESIZE), int(img.shape[1]/TILESIZE)), dtype=np.int32),避免后续赋值出错。 - 如果是彩色图像,当前直方图只计算了第0通道(BGR的B通道),应该考虑计算全通道直方图或者转为灰度图后计算,避免丢失颜色信息。
内容的提问来源于stack exchange,提问作者maugch
相关产品推荐
相关产品推荐

