nearest.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. #!/usr/bin/env python3
  2. import csv
  3. import math
  4. import random
  5. from argparse import ArgumentParser
  6. from convert import rgb_to_cieluv
  7. def norm(r, g, b):
  8. return math.sqrt(r * r + g * g + b * b)
  9. parser = ArgumentParser()
  10. parser.add_argument("color", nargs="?", default=None, help="Target color, randomized if not provided")
  11. parser.add_argument("-n", "--number", default=1, type=int, help="Number of Pokemon to find")
  12. parser.add_argument("-c", "--closeness", default=2, type=int, help="Closeness coefficient")
  13. parser.add_argument("-d", "--database", default="database.csv", help="Database file")
  14. parser.add_argument("-x", "--exclude-x", action="store_true", help="Exclude X")
  15. parser.add_argument("-z", "--normalize", action="store_true", help="Normalize q and Y")
  16. parser.add_argument("-l", "--convert-luv", action="store_true", help="Convert input color to CIELUV before calculation")
  17. parser.add_argument("-v", "--verbose", action="store_true", help="Print raw scores")
  18. args = parser.parse_args()
  19. if args.number <= 0:
  20. raise ValueError("Must request a number greater than 0")
  21. if args.color is not None:
  22. cleaned_color = args.color.strip("#")
  23. if len(cleaned_color) != 6:
  24. raise ValueError("Color must be a 6 digit hex")
  25. color = (int(cleaned_color[0:2], base=16), int(cleaned_color[2:4], base=16), int(cleaned_color[4:6], base=16))
  26. else:
  27. color = tuple(int(random.random() * 255) for _ in range(3))
  28. print(f"Generated random color: #{''.join(hex(c)[2:] for c in color)} / {color}")
  29. if args.convert_luv:
  30. color = rgb_to_cieluv(*color)
  31. yfactor = args.closeness
  32. if args.normalize:
  33. yfactor /= norm(*color)
  34. results = []
  35. with open(args.database) as infile:
  36. for name, x, *y in csv.reader(infile, delimiter=",", quotechar="'"):
  37. xval = 0 if args.exclude_x else float(x)
  38. yvec = [float(y_c) for y_c in y]
  39. yval = sum(y_c * c for y_c, c in zip(yvec, color))
  40. if args.normalize:
  41. yval /= norm(*yvec)
  42. score = xval - yfactor * yval
  43. results.append((score, name))
  44. if args.number > 1:
  45. for i, (score, name) in enumerate(sorted(results)[:args.number]):
  46. print(f"{i+1}:\t{name}")
  47. if args.verbose:
  48. print(f"\t\t\t{score=}")
  49. else:
  50. best_score, best_name = min(results)
  51. print(f"Best match: {best_name}")
  52. if args.verbose:
  53. print(f"{score=}")