benjaminc-tech/free-reverse-image-search

GitHub: benjaminc-tech/free-reverse-image-search

一个零依赖、零上传的纯浏览器端以图搜图工具,同时提供 EXIF 解析和感知哈希计算能力。

Stars: 0 | Forks: 0

# 免费以图搜图 **[立即体验:freereverseimagesearch.netlify.app](https://freereverseimagesearch.netlify.app)** 同时在 Google Lens、Yandex、Bing 和 TinEye 中搜索同一张图片,读取文件中隐藏的 EXIF 和 GPS 数据,并使用三种感知哈希(perceptual hash)对其进行指纹提取。 完全免费,无需账号,无广告,并且**不上传任何数据**。整个工具就是一个 HTML 文件,完全在您的浏览器中运行。这个承诺正是本仓库存在的意义:您可以亲自阅读源码并确认这里没有任何上传路径,这是任何托管工具都无法提供给您的保证。 ![截图](https://static.pigsec.cn/wp-content/uploads/repos/cas/9e/9e639179e47c83fd42c433421d643bad088e3e5cb002703088dd2f357b378466.png) - 单文件,无构建步骤,无依赖,无服务器 - 无需任何库,直接从原始字节解析 EXIF,包括将 GPS 转换为十进制 - 包含 aHash、dHash 和 pHash,甚至包含手动实现的 DCT - 标记出关于照片值得了解的两件事:是否携带位置信息,以及最后一次保存是否由编辑软件(而非相机)完成 ## 功能说明 - **多引擎搜索。** 一键将图片提交给 Google Lens、Yandex、Bing Visual Search 和 TinEye。 - **EXIF 提取。** 解析相机、镜头、时间戳、编辑软件和 GPS 坐标,无需任何库,直接从原始字节中解析。适用于本地文件,以及允许跨域读取的远程 URL。 - **感知哈希(Perceptual hashing)。** 在页面内计算 aHash、dHash 和 pHash,让您能够辨别两张图片在缩放或重新压缩后是否仍为同一张画面。 ## 决定一切的设计约束 反向图片搜索引擎必须能够**自行获取图片**。这将问题分成了两类: - **网络上已有的图片**拥有 URL,因此可以直接提交给所有四个引擎。这条路径完全可行。 - **您电脑上的文件**没有公开地址。通常的解决方案是将其上传到服务器以便引擎访问,这意味着需要承担托管成本、存储开销,以及对陌生人上传内容的责任。本工具拒绝这种妥协:它将图片复制到您的剪贴板,以便您自行粘贴到搜索引擎中。 正是这一选择,意味着没有后端、没有数据库、没有账单,也没有内容审核问题。 ## 功能限制 它本身并没有图片索引,因此无法自行回答“这张图片还出现在哪里”。这种能力属于 Google、Yandex 和 TinEye,它们花费了 15 年以上的时间和数百万的资金,构建了包含数十亿张图片的抓取索引。本工具只是它们优秀的访问入口,并额外提供了它们不会给您的分析功能。 ## 设计说明 全程使用 Monaco 字体,以 20px 为基准,配有粗黑线条和 `4px 4px 0` 偏移(无模糊)阴影。按钮和图块在点击时会有物理下陷效果:元素向右下方平移,阴影收缩为零。 Marvel 风格主导了调色板。标志文字像漫画刊头一样以纯红色底板反白显示,红色和黑色承担了结构构建的工作,金色作为辅助强调色。 刚好保留了一种 Google 的颜色,用在唯一真正属于 Google 的那个图块上。 | 变量 | Hex | 搭配色 | 对比度 | 用途 | |---|---|---|---:|---| | `--hero` | `#EC1D24` | 仅纯色块 | n/a | 分区标签、强调元素 | | `--hero-ink` | `#C7151C` | 白色 | 5.91:1 | 刊头底板、主按钮、Yandex 图块 | | `--gold` | `#F0B323` | 黑色 | 10.21:1 | TinEye 图块、编辑标记 | | `--ink` | `#0d0f13` | 白色 | 19.18:1 | Bing 图块、隐私徽章 | | `--g-blue` | `#4285F4` | 黑色 | 5.38:1 | Google Lens 图块、位置标记 | 以下规则基于实际测量,而非主观臆测: - **Marvel 红 `#EC1D24` 无论搭配白色还是黑色都不合格**(对比度分别为 4.40:1 和 4.35:1),因为它的亮度居中。它仅被用作不含文字的纯色块。所有包含文字的红色表面均使用 `#C7151C` 的 `--hero-ink`,其在白底上的对比度超过了 4.5:1。 - **铭牌自带文字颜色。** 它们不再全都是浅色,因此每个引擎在声明其 `tile` 的同时也会声明对应的 `ink`,并且每种搭配的对比度都被验证至少达到了 4.5:1。 - 金色配红色的对比度仅为 3.15:1,仅能达到大号粗体文本的合格标准。它只用于 35px 刊头中的一个词,除此之外不用于其他任何地方。 ### Favicon 在 Marvel 红色底板上的放大镜:白色镜片,金色手柄,所有轮廓像 UI 的其余部分一样用黑色勾勒。在真实尺寸下测试它而不是将其放大欣赏后,得出了三个设计决策: - **镜片是实心圆盘,而不是圆环。** 圆环在 16px 尺寸下会糊成一团并失去辨识度。 - **金色手柄带有黑色下划线。** 金色配红色的对比度仅为 3.15:1,如果没有黑色墨水将它们隔开,手柄在小尺寸下会与底板模糊地融为一体。 - **直接否决了图片边框的概念。** 它在 128px 时看起来还不错,但在 16px 时就会变成无法辨认的污迹。 源文件是 `favicon.svg`;它也作为 data URI 内联到了 `index.html` 中,因此该工具始终是一个完全独立的单文件。 ### 在手机上 已在 393px 尺寸下进行测试。有三个只有在这个宽度下才会暴露的问题需要修复: - 引擎网格之前使用的是 `minmax(400px, 1fr)`,这会导致即使在 345px 的容器中也保持 400px 的轨道宽度,从而使得图块溢出,并被 `overflow-x: hidden` 防护措施悄无声息地裁剪掉。现在已改为 `minmax(min(400px, 100%), 1fr)`。 - 表格值上的 `word-break: break-all` 对于 16 个字符的哈希值来说是正确的,但对于“TestCam Industries”这样的文字就不合适了。现在改为 `overflow-wrap: anywhere`,它会优先在空格处换行,只有在迫不得已时才会截断单词。 - 在宽度低于 560px 时,键值表会垂直堆叠,标签位于值上方,两者均占据全宽。否则,不换行的标签列会占据屏幕的大部分空间。 在深色模式下,颜色块保持其准确的色相,就像印刷页面上的墨水一样。只有它们周围的表面会发生反转,同时 `--edge` 会切换为接近白色,以保证线条轮廓依然清晰可见。 ## 测试说明 ``` ./tests/run.sh ``` 所有内容均已通过独立的参考实现进行了验证,并且测试会直接从 `index.html` 中提取真实代码,因此它们不会与实际上线的内容产生脱节。 - **EXIF** 的检查依赖于一个测试夹具,其字节是在 `tests/make_fixtures.py` 中手工组装的,因此每个预期值都是精确已知的。覆盖了 ASCII、SHORT、LONG 和 RATIONAL 类型,包含两个 sub-IFD,以及 GPS 的度/分/秒到十进制的转换。同时也覆盖了各种失败路径:无 EXIF、非 JPEG、空缓冲区以及截断的文件(在此情况下绝不能抛出异常)。 - **哈希值** 与单独的 Python 实现进行了逐位比对,向其输入完全相同的灰度像素阵列,从而确保测试衡量的是算法本身,而不是两种不同的重采样器。 预计浏览器输出的哈希值与测试输出的哈希值会有几位的差异。这并不是一个 bug:Canvas 的下采样缩放和 Pillow 的 `BILINEAR` 并不是同一个滤镜,因此它们会向完全相同的算法传递略微不同的像素。真正重要的是图像之间的距离保持稳定,而这正是鲁棒性测试所检查的内容。 - **鲁棒性** 测量了经过真实编辑后的真实 Hamming 距离。 测得的 pHash 行为(距离原图的距离,0 = 完全相同): | 编辑操作 | pHash | dHash | |---|---:|---:| | 缩放至 25% | 0 | 0 | | 缩放至 200% | 0 | 0 | | JPEG 质量 20 | 0 | 1 | | 亮度 +25% | 0 | 2 | | 转换为灰度图 | 0 | 2 | | 四周边缘各裁剪 5% | 2 | 4 | | 旋转 90 度 | 24 | 8 | | 完全不同的一张图片 | 32 | 46 | **已知局限:旋转会使其失效。** 旋转 90 度会被识别为不同的图像。这是此类哈希算法的工作原理所固有的特性,而不是 bug,但这意味着您应该在比较之前先将图像旋转复原。 ## 文件说明 ``` index.html the entire tool favicon.svg favicon source (also inlined into index.html) tests/run.sh runs everything tests/make_fixtures.py builds test-exif.jpg with a hand-built EXIF block tests/test_exif.mjs EXIF parser assertions tests/test_hash.mjs hash cross-check against Python tests/test_robustness.py Hamming distances across real edits ``` `test-exif.jpg` 和 `_base.jpg` 是由测试生成的,可以删除。 ## 本地运行 ``` python3 -m http.server 8765 # 然后打开 http://localhost:8765/ ``` 直接从磁盘打开 `index.html` 也可以运行,尽管在某些浏览器中,`file://` 协议下的剪贴板写入功能可能会受到限制。 ## 自行运行 直接打开 `index.html`,或者部署该服务: ``` python3 -m http.server 8765 ``` 在某些浏览器中,`file://` 协议下的剪贴板写入可能会受到限制,这也是在本地部署服务器的唯一理由。 ## 部署说明 `publish/` 目录包含了确切要发布到网络上的内容:页面、图标、`robots.txt`、`sitemap.xml` 和 `_headers`。测试夹具和测试套件被刻意排除在外。 ``` cp index.html favicon.svg publish/ netlify deploy --prod --dir=publish ``` 图标是根据与 `favicon.svg` 相同的几何结构生成的: ``` python3 tools/make-icons.py ``` ## 开源协议 MIT。请随心所欲地使用它。
标签:EXIF解析, 后端开发, 图像搜索, 图片元数据提取, 多模态安全, 感知哈希, 数据可视化, 纯前端工具, 网络安全, 逆向工具, 隐私保护