请上传宽度大于 1200px,高度大于 164px 的封面图片
    调整图片尺寸与位置
    滚轮可以放大缩小图片尺寸,按住图片拖动可调整位置,多余的会自动被裁剪掉
取消
Adiya(uid:104675)
职业资格认证:FCP-零代码开发工程师 | FCP-报表开发工程师
云函数分享:重复图片识别及图片地址返回的实现
一、应用场景:       简道云目前没有关于重复或相似图片的识别,但是我们在实际应用中会有相关的需求,如报销凭据、拍照打卡等。而目前该类信息的判定主要是通过人工进行判定,如果是在同一张表单中或者图片量少时,人工尚可,如是时间间隔较久或图片量较大,就可能出现漏失 那么如何对同一张图片进行判定呢? 二、解决方案: 通过搜索网络上成熟的解决方案      目前最简单的方法是使用加密哈希(例如MD5, SHA-1)判断。但是局限性非常大。例如一个txt文档,其MD5值是根据这个txt的二进制数据计算的,如果是这个txt文档的完全复制版,那他们的MD5值是完全相同的。但是,一旦改变副本的内容,哪怕只是副本的缩进格式,其MD5也会天差地别。因此加密哈希只能用于判断两个完全一致、未经修改的文件,如果是一张经过调色或者缩放的图片,根本无法判断其与另一张图片是否为同一张图片。 那么如何判断一张被PS过的图片是否与另一张图片本质上相同呢?比较简单、易用的解决方案是采用感知哈希算法(Perceptual Hash Algorithm)。   相似图片解决步骤: 分别计算两张图片的dHash值 通过dHash值计算两张图片的汉明距离(Hamming Distance),通过汉明距离的大小,判断两张图片的相似程度。 参考文章:1.相似图片检测:感知哈希算法之dHash的Python实现                  2.【相似图片检测:哈希算法之dHash的Python实现】详解   在简道云上的解决步骤: 1.在主表中通过前端事件将图片信息推送至云函数,并通过云函数计算出各个图片的dHash值,返回对应图片的dHash值及地址至主表 2.在主表中统计图片个数并通过云函数创建新的子表单,并分别获取单个图片dHash值及地址; 3.通过智能助手将返回的图片dHash值和地址推送至新的子表中,用以配合主表做重复图片验证; 4.设置字段显隐,当出现重复时显示重复图片信息及地址;   注:本文目前仅作重复图片验证,相似图片验证可能涉及到数据库,暂未实施。   测试示例: 三、详细步骤 1.在主表中通过前端事件将图片信息推送至云函数并返回dHash值及其地址 1.1 创建表单 以报销单为例,创建一个报销单,包含报销编码及报销明细,报销明细中至少包含图片、dHash值(文本)、图片地址(文本); 1.2 设置云函数 设置方法,请参考@张明亮 相关帖子,此文不再赘述; 代码参考如下: # -*- coding: utf8 -*- from PIL import Image import json import requests from io import BytesIO from urllib.parse import unquote def grayscale_Image(image,resize_width=9,resize_heith=8): #image为图片的请求网络路径信息,resize_width为缩放图片的宽度,resize_heith为缩放图片的高度 response = requests.get(image) #获取网络图片 im = Image.open(BytesIO(response.content)) #使用Image的open方法打开图片 smaller_image = im.resize((resize_width,resize_heith),Image.ANTIALIAS) #将图片进行缩放 grayscale_image = smaller_image.convert('L') #将图片灰度化 return grayscale_image def hash_String(image,resize_width=9,resize_heith=8): hash_string = "" #定义空字符串的变量,用于后续构造比较后的字符串 pixels = list(grayscale_Image(image,resize_width,resize_heith).getdata()) # 上一个函数grayscale_Image()缩放图片并返回灰度化图片,.getdata()方法可以获得每个像素的灰度值,使用内置函数list()将获得的灰度值序列化 for row in range(1,len(pixels)+1): #获取pixels元素个数,从1开始遍历 if row % resize_width : #因不同行之间的灰度值不进行比较,当与宽度的余数为0时,即表示当前位置为行首位,我们不进行比较 if pixels > pixels: #当前位置非行首位时,我们拿前一位数值与当前位进行比较 hash_string += '1' #当为真时,构造字符串为1 else: hash_string += '0' #否则,构造字符串为0 #最后可得出由0、1组64位数字字符串,可视为图像的指纹 return int(hash_string,2) #把64位数当作2进制的数值并转换成十进制数值 #下面函数主要是针对简道云前端事件获取的信息进行处理 def dhash_String(src): src_list = src.split('https') #先分组 谨防图片名称中包含"https" src_list = #解码图片URL(二次解码,处理中文名称图片),去除空格、空元素影响 再建图片地址列表 dhash_list = #调用hash_String函数生成数值码 src_list = #给类表中每个元素(地址)添加指定字符||,防止图片命名干扰其分割列表 return ''.join(str(dhash_list)).replace(" ",""),''.join(str(src_list)).replace(" ","").replace("'","") #dhash列表转化为字符串 去除空格和,返回图片地址 class Factory: def __init__(self,cs): self.chuli={} self.chuli = dhash_String(cs) def Release(self): return { "isBase64Encoded": False, "statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": json.dumps(self.chuli) } def main_handler(event, context): # 处理获取到的参数 chuli = Factory(event) # 返回处理结果 return chuli.Release() 注:由于刚接触Python,算法及代码书写不是很规范,实际运行中 可能会有卡顿,有功夫的道友可以帮忙提供优化建议。 1.3 创建前端事件 设置如下: a.触发字段及请求设置       第二张图片中URL地址源于云函数 触发管理中的访问路径 在简道云请求设置的URL中,要在从云函数中复制的地址后面加上: ?cs=请求字段 b.返回值设置 点击添加 表单字段及对应返回值 其中 dHash 值为:$.release 、图片地址为:$.release       2.在主表中统计图片个数并通过云函数创建新的子表单,并分别获取单个图片dHash值及地址 2.1 创建dHash值集合及图片地址集合并计算出图片的数量 相关公式如下: dHash值集合(文本):报销明细.dHash值 图片地址集合(文本):报销明细.图片地址 图片数量(数字):COUNT(SPLIT(dHash值集合,",")) 2.2 创建 “重复图片判定” 子表单  相关公式设置如下: 序号(数字):#(为空,使用云函数) dHash值(文本):SPLIT(dHash值集合,",") 图片位置信息(文本):CONCATENATE('报销单号:',报销编码,' 第',重复图片判定.序号,'图片')    #可以自行设置 图片地址(文本):SPLIT(SPLIT(图片地址集合,"||,"),"||") 2.3 设置云函数,根据图片数量设置子表单 设置方法请参考@张明亮的帖子:《超爽:自建云函数+前端事件 激活你的更多使用场景》    3.通过智能助手将返回的图片dHash值和地址推送至新的子表中,用以配合主表重复图片验证; 3.1 创建一个表单:图片集 用于搜集所有图片的dHash值及位置信息 dHash值(文本)、图片位置信息(文本)、地址(文本) 3.2 设置智能助手推送 由于是测试,该实例中使用的是表单新增触发智能助手,用以演示,各位道友可自行设置 4.设置字段显隐,当出现重复时显示重复图片信息及地址 4.1设置关联数据 在报销单的“重复图片判定”中添加“重复图片位置信息”、“重复图片地址”,分别设置数据联动设置,关联上述“图片集”中的信息 数据联动设置   4.2 设置显隐字段 显隐字段的设置,主要是为了美观,各位道友可按照需要自行设置,以下仅为参考: 我们可以在“重复图片判定”的dHash值中设置不允许重复值,可以确保本次提交不允许重复图片 我们也可以在“重复图片判定”子表单上设置多行文本或单行文本:“重复图片信息”,并设置显隐规则,用以提示: 当出现重复提交图片时,可在其显示报销单中的重复图片位置信息及地址。 注意: 1.获取的地址包含token信息,请注意保密; 2.获取的图片地址,放在浏览器中可直接下载,用以人工判定。(目前尚未找到可以直接预览图片的方法) 3.文中使用的前端事件请求类型均是get,对数据安全性较高的,需要慎重。 4.子表单提交多张图片时 云函数反应稍有迟钝,使用时请注意不要因提交过快而导致dHash值为空数据(设置为必填项); 5.当报销明细中删除项目时,如删除掉一行子表单,下方重复图片判定中的子表单行数不会减少,此时可以刷新网页重新提交(有解决方案的道友可留言指导),避免智能助手提交空数据。   以上就是重复图片识别及图片地址返回的实现的步骤,下面是相似图片实现的思路   四、拓展:相似图片识别的解决思路与困惑 1.汉明距离   汉明距离表示将A修改成为B,需要多少个步骤。 比如字符串“abc”与“ab3”,汉明距离为1,因为只需要修改“c”为“3”即可。dHash中的汉明距离是通过计算差异值的修改位数。我们的差异值是用0、1表示的,可以看做二进制。二进制0110与1111的汉明距离为2。我们将两张图片的dHash值转换为二进制difference,并取异或。计算异或结果的“1”的位数,也就是不相同的位数,这就是汉明距离。   def Difference(dhash1, dhash2): difference = dhash1 ^ dhash2 #将两个数值进行异或运算 return bin(difference).count('1') #异或运算后计算两数不同的个数,即个数<5,可视为同一或相似图片   2.思路 2.1 通过云函数将所有图片的dHash值提交至数据库; 2.2 将目标图片dHash值与数据库中所有dHash值进行汉明计算,小于5的可以认为是相同图片; 2.3 再次通过云函数将小于5的图片信息返回至主表,用以人工判定。   3.实际测试   3.1 个人在测试下面三张原图片时, 云函数测试的 dHash 值分别是:1885395423738997412、1885395423738997413、1885395973501627045 本地测试的 dHash 值 分别是:1012823547742809698,1012823547742809702,1012823547742809830 虽然值不相同(未找到具体原因,使用Postman测试是结果与上述也不相同,不知是不是因为转码原因),但是本地dHash值前两张图片汉明距离为1,第二张与第三张也是1,第一张与第三张为2;网络dHash值 前两张汉明距离为1,第一、三两张为5,第二、三为4。   测试结果有所波动,但均在理论范围内 认定为相同图片;   3.2 如果对图片进行截切(如下面三张图,从此文章下载到桌面上进行测试,云函数测试汉明距离,差距最小的是1、3两张图片,结果为12) 结果不是很理想。   综上,个人目前尚未有效掌握汉明距离的使用,期待各位道友的建议与分享。        2021.12.07更新说明 不知各位道友有没有这个问题:相同的图片本地测试的值与云函数的值不相同; 且云函数实际使用中出现,图片中仅金额有差异其他都相同的图片,最终dHash值也会相同,导致误判。 目前代码更新了一下(上文源代码已更新): #将源码中下面代码 smaller_image = im.resize((resize_width,resize_heith)) #将图片进行缩放 #替换为 smaller_image = im.resize((resize_width,resize_heith),Image.ANTIALIAS) #将图片进行缩放  目前排查的结果是,下图红框中相同图片调试时 云函数中的灰度值 与本地的灰度值 几乎每个值都相差1,最终导致云函数中值与本地值 不相同。 目前该问题尚未解决,待解决后会及时更新!
个人成就
内容被浏览5,993
加入社区6年146天
返回顶部