q6 commited on
Commit
8763ff0
·
1 Parent(s): 274ec21

Detect duplicate images during migration

Browse files
Files changed (1) hide show
  1. app.py +39 -13
app.py CHANGED
@@ -108,6 +108,9 @@ PREVIEW_CACHE_SIZE = 256
108
  PREVIEW_QUALITY = 70
109
  PREVIEW_SIZE = 320
110
  IMAGE_KEY_CACHE_SIZE = 512
 
 
 
111
  DEFAULT_RETURN_SCALE = 1
112
  DEFAULT_BATCH_SIZE = 1
113
  DEFAULT_UI_BATCH_SIZE = 1
@@ -333,21 +336,30 @@ def cleanup_mount():
333
  path.unlink()
334
  log(f"Deleted img/{path.relative_to(IMAGE_DIR)}")
335
  if HAS_DATA_MOUNT:
336
- pngs = list(IMAGE_DIR.glob("????-??-??/*.png"))
337
- duplicates = {
338
- path.with_suffix(".png")
339
- for path in IMAGE_DIR.glob("????-??-??/*.epng")
340
- }
341
- for path in pngs:
342
- if path in duplicates:
343
- path.unlink()
344
- log(f"Deleted img/{path.relative_to(IMAGE_DIR)}")
345
- for path in pngs:
346
- if path in duplicates:
347
- continue
348
  with Image.open(path) as image:
349
- save_named_image(image, path.with_suffix(".epng"))
 
 
 
 
 
 
 
 
 
 
 
 
350
  path.unlink()
 
 
 
 
351
 
352
 
353
  def download(url, target):
@@ -1988,6 +2000,20 @@ def archive_image(image, from_api):
1988
  log(f"Archive failed: {error}")
1989
 
1990
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1991
  def image_png_bytes(image):
1992
  pnginfo = PngInfo()
1993
  for key, value in image.info.items():
 
108
  PREVIEW_QUALITY = 70
109
  PREVIEW_SIZE = 320
110
  IMAGE_KEY_CACHE_SIZE = 512
111
+ DUPLICATE_HASH_SIZE = 16
112
+ DUPLICATE_HASH_DISTANCE = 24
113
+ DUPLICATE_COLOR_DISTANCE = 24
114
  DEFAULT_RETURN_SCALE = 1
115
  DEFAULT_BATCH_SIZE = 1
116
  DEFAULT_UI_BATCH_SIZE = 1
 
336
  path.unlink()
337
  log(f"Deleted img/{path.relative_to(IMAGE_DIR)}")
338
  if HAS_DATA_MOUNT:
339
+ fingerprints = []
340
+ for path in IMAGE_DIR.rglob("*.epng"):
341
+ with Image.open(BytesIO(stored_bytes(path))) as image:
342
+ fingerprints.append(image_fingerprint(image))
343
+ for path in IMAGE_DIR.glob("????-??-??/*.png"):
 
 
 
 
 
 
 
344
  with Image.open(path) as image:
345
+ fingerprint = image_fingerprint(image)
346
+ duplicate = any(
347
+ (fingerprint[0] ^ known[0]).bit_count()
348
+ <= DUPLICATE_HASH_DISTANCE
349
+ and sum(
350
+ abs(left - right)
351
+ for left, right in zip(fingerprint[1], known[1])
352
+ )
353
+ <= DUPLICATE_COLOR_DISTANCE
354
+ for known in fingerprints
355
+ )
356
+ if not duplicate:
357
+ save_named_image(image, path.with_suffix(".epng"))
358
  path.unlink()
359
+ if duplicate:
360
+ log(f"Deleted duplicate img/{path.relative_to(IMAGE_DIR)}")
361
+ else:
362
+ fingerprints.append(fingerprint)
363
 
364
 
365
  def download(url, target):
 
2000
  log(f"Archive failed: {error}")
2001
 
2002
 
2003
+ def image_fingerprint(image):
2004
+ pixels = np.asarray(
2005
+ ImageOps.exif_transpose(image).convert("RGB").resize(
2006
+ (DUPLICATE_HASH_SIZE + 1, DUPLICATE_HASH_SIZE),
2007
+ Image.Resampling.LANCZOS,
2008
+ )
2009
+ )
2010
+ differences = pixels[:, 1:] > pixels[:, :-1]
2011
+ return (
2012
+ int.from_bytes(np.packbits(differences).tobytes()),
2013
+ tuple(int(value) for value in pixels.mean(axis=(0, 1))),
2014
+ )
2015
+
2016
+
2017
  def image_png_bytes(image):
2018
  pnginfo = PngInfo()
2019
  for key, value in image.info.items():