|
1 | 1 | from collections.abc import Iterable
|
2 | 2 | from math import sqrt
|
3 |
| -from typing import TYPE_CHECKING |
4 | 3 |
|
5 | 4 | import cv2
|
6 | 5 | import Levenshtein
|
|
22 | 21 | RANGES = (0, MAXRANGE, 0, MAXRANGE, 0, MAXRANGE)
|
23 | 22 | MASK_SIZE_MULTIPLIER = ColorChannel.Alpha * MAXBYTE * MAXBYTE
|
24 | 23 | MAX_VALUE = 1.0
|
| 24 | +CV2_PHASH_SIZE = 8 |
25 | 25 |
|
26 | 26 |
|
27 | 27 | def compare_histograms(source: MatLike, capture: MatLike, mask: MatLike | None = None):
|
@@ -90,41 +90,39 @@ def compare_template(source: MatLike, capture: MatLike, mask: MatLike | None = N
|
90 | 90 | return 1 - (min_val / max_error)
|
91 | 91 |
|
92 | 92 |
|
93 |
| -try: |
94 |
| - from scipy import fft |
95 |
| - |
96 |
| - def __cv2_scipy_compute_phash(image: MatLike, hash_size: int, highfreq_factor: int = 4): |
97 |
| - """Implementation copied from https://github.com/JohannesBuchner/imagehash/blob/38005924fe9be17cfed145bbc6d83b09ef8be025/imagehash/__init__.py#L260 .""" # noqa: E501 |
98 |
| - img_size = hash_size * highfreq_factor |
99 |
| - image = cv2.cvtColor(image, cv2.COLOR_BGRA2GRAY) |
100 |
| - image = cv2.resize(image, (img_size, img_size), interpolation=cv2.INTER_AREA) |
101 |
| - dct = fft.dct(fft.dct(image, axis=0), axis=1) |
102 |
| - dct_low_frequency = dct[:hash_size, :hash_size] |
103 |
| - median = np.median(dct_low_frequency) |
104 |
| - return dct_low_frequency > median |
105 |
| - |
106 |
| - def __cv2_phash(source: MatLike, capture: MatLike, hash_size: int = 8): |
107 |
| - source_hash = __cv2_scipy_compute_phash(source, hash_size) |
108 |
| - capture_hash = __cv2_scipy_compute_phash(capture, hash_size) |
109 |
| - hash_diff = np.count_nonzero(source_hash != capture_hash) |
110 |
| - return 1 - (hash_diff / 64.0) |
111 |
| - |
112 |
| -except ModuleNotFoundError: |
113 |
| - if not TYPE_CHECKING: # opencv-contrib-python-headless being installed is based on architecture |
114 |
| - |
115 |
| - def __cv2_phash(source: MatLike, capture: MatLike, hash_size: int = 8): |
116 |
| - # OpenCV has its own pHash comparison implementation in `cv2.img_hash`, |
117 |
| - # but it requires contrib/extra modules and is inaccurate |
118 |
| - # unless we precompute the size with a specific interpolation. |
119 |
| - # See: https://github.com/opencv/opencv_contrib/issues/3295#issuecomment-1172878684 |
120 |
| - # |
121 |
| - phash = cv2.img_hash.PHash.create() |
122 |
| - source = cv2.resize(source, (hash_size, hash_size), interpolation=cv2.INTER_AREA) |
123 |
| - capture = cv2.resize(capture, (hash_size, hash_size), interpolation=cv2.INTER_AREA) |
124 |
| - source_hash = phash.compute(source) |
125 |
| - capture_hash = phash.compute(capture) |
126 |
| - hash_diff = phash.compare(source_hash, capture_hash) |
127 |
| - return 1 - (hash_diff / 64.0) |
| 93 | +# The old scipy-based implementation. |
| 94 | +# Turns out this cuases an extra 25 MB build compared to opencv-contrib-python-headless |
| 95 | +# # from scipy import fft |
| 96 | +# def __cv2_scipy_compute_phash(image: MatLike, hash_size: int, highfreq_factor: int = 4): |
| 97 | +# """Implementation copied from https://github.com/JohannesBuchner/imagehash/blob/38005924fe9be17cfed145bbc6d83b09ef8be025/imagehash/__init__.py#L260 .""" # noqa: E501 |
| 98 | +# img_size = hash_size * highfreq_factor |
| 99 | +# image = cv2.cvtColor(image, cv2.COLOR_BGRA2GRAY) |
| 100 | +# image = cv2.resize(image, (img_size, img_size), interpolation=cv2.INTER_AREA) |
| 101 | +# dct = fft.dct(fft.dct(image, axis=0), axis=1) |
| 102 | +# dct_low_frequency = dct[:hash_size, :hash_size] |
| 103 | +# median = np.median(dct_low_frequency) |
| 104 | +# return dct_low_frequency > median |
| 105 | +# def __cv2_phash(source: MatLike, capture: MatLike, hash_size: int = 8): |
| 106 | +# source_hash = __cv2_scipy_compute_phash(source, hash_size) |
| 107 | +# capture_hash = __cv2_scipy_compute_phash(capture, hash_size) |
| 108 | +# hash_diff = np.count_nonzero(source_hash != capture_hash) |
| 109 | +# return 1 - (hash_diff / 64.0) |
| 110 | + |
| 111 | + |
| 112 | +def __cv2_phash(source: MatLike, capture: MatLike): |
| 113 | + """ |
| 114 | + OpenCV has its own pHash comparison implementation in `cv2.img_hash`, |
| 115 | + but is inaccurate unless we precompute the size with a specific interpolation. |
| 116 | +
|
| 117 | + See: https://github.com/opencv/opencv_contrib/issues/3295#issuecomment-1172878684 |
| 118 | + """ |
| 119 | + phash = cv2.img_hash.PHash.create() |
| 120 | + source = cv2.resize(source, (CV2_PHASH_SIZE, CV2_PHASH_SIZE), interpolation=cv2.INTER_AREA) |
| 121 | + capture = cv2.resize(capture, (CV2_PHASH_SIZE, CV2_PHASH_SIZE), interpolation=cv2.INTER_AREA) |
| 122 | + source_hash = phash.compute(source) |
| 123 | + capture_hash = phash.compute(capture) |
| 124 | + hash_diff = phash.compare(source_hash, capture_hash) |
| 125 | + return 1 - (hash_diff / 64.0) |
128 | 126 |
|
129 | 127 |
|
130 | 128 | def compare_phash(source: MatLike, capture: MatLike, mask: MatLike | None = None):
|
|
0 commit comments