python-libphash 1.0.3__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {python_libphash-1.0.3/src/python_libphash.egg-info → python_libphash-1.1.0}/PKG-INFO +2 -2
  2. {python_libphash-1.0.3 → python_libphash-1.1.0}/README.md +1 -1
  3. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/README.md +33 -9
  4. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/include/libphash.h +1 -0
  5. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/core.c +11 -2
  6. python_libphash-1.1.0/native/libphash/src/hashes/ahash.c +32 -0
  7. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/hashes/dhash.c +11 -8
  8. python_libphash-1.1.0/native/libphash/src/hashes/phash.c +78 -0
  9. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/image.c +43 -4
  10. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/internal.h +6 -1
  11. {python_libphash-1.0.3 → python_libphash-1.1.0}/pyproject.toml +1 -1
  12. {python_libphash-1.0.3 → python_libphash-1.1.0/src/python_libphash.egg-info}/PKG-INFO +2 -2
  13. python_libphash-1.0.3/native/libphash/src/hashes/ahash.c +0 -28
  14. python_libphash-1.0.3/native/libphash/src/hashes/phash.c +0 -57
  15. {python_libphash-1.0.3 → python_libphash-1.1.0}/LICENSE +0 -0
  16. {python_libphash-1.0.3 → python_libphash-1.1.0}/MANIFEST.in +0 -0
  17. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/.clang-format +0 -0
  18. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/.git +0 -0
  19. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/.gitignore +0 -0
  20. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/CMakeLists.txt +0 -0
  21. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/LICENSE +0 -0
  22. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/Makefile +0 -0
  23. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/hashes/bmh.c +0 -0
  24. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/hashes/color_hash.c +0 -0
  25. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/hashes/common.c +0 -0
  26. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/hashes/mhash.c +0 -0
  27. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/hashes/radial.c +0 -0
  28. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/src/hashes/whash.c +0 -0
  29. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/photo.jpeg +0 -0
  30. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/photo_color_changed.jpeg +0 -0
  31. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/photo_copy.jpeg +0 -0
  32. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/photo_rotated_90.jpeg +0 -0
  33. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_bmh.c +0 -0
  34. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_color.c +0 -0
  35. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_core.c +0 -0
  36. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_hashes.c +0 -0
  37. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_image_hashes.c +0 -0
  38. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_internal.c +0 -0
  39. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_macros.h +0 -0
  40. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_mhash.c +0 -0
  41. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_radial.c +0 -0
  42. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/tests/test_whash.c +0 -0
  43. {python_libphash-1.0.3 → python_libphash-1.1.0}/native/libphash/vendor/stb_image.h +0 -0
  44. {python_libphash-1.0.3 → python_libphash-1.1.0}/setup.cfg +0 -0
  45. {python_libphash-1.0.3 → python_libphash-1.1.0}/setup.py +0 -0
  46. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/__init__.py +0 -0
  47. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/_build.py +0 -0
  48. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/_native.pyi +0 -0
  49. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/context.py +0 -0
  50. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/exceptions.py +0 -0
  51. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/py.typed +0 -0
  52. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/types.py +0 -0
  53. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/libphash/utils.py +0 -0
  54. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/python_libphash.egg-info/SOURCES.txt +0 -0
  55. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/python_libphash.egg-info/dependency_links.txt +0 -0
  56. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/python_libphash.egg-info/not-zip-safe +0 -0
  57. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/python_libphash.egg-info/requires.txt +0 -0
  58. {python_libphash-1.0.3 → python_libphash-1.1.0}/src/python_libphash.egg-info/top_level.txt +0 -0
  59. {python_libphash-1.0.3 → python_libphash-1.1.0}/tests/test_digests.py +0 -0
  60. {python_libphash-1.0.3 → python_libphash-1.1.0}/tests/test_distances.py +0 -0
  61. {python_libphash-1.0.3 → python_libphash-1.1.0}/tests/test_hashes.py +0 -0
  62. {python_libphash-1.0.3 → python_libphash-1.1.0}/tests/test_loading.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-libphash
3
- Version: 1.0.3
3
+ Version: 1.1.0
4
4
  Summary: High-performance perceptual hashing library (CFFI bindings)
5
5
  Author-email: gudoshnikovn <gudoshnikov-na@yandex.ru>
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ Dynamic: license-file
20
20
 
21
21
  # python-libphash
22
22
 
23
- High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash), a C library for perceptual image hashing.
23
+ High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash) v1.3.0, a C library for perceptual image hashing.
24
24
 
25
25
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
26
26
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
@@ -1,6 +1,6 @@
1
1
  # python-libphash
2
2
 
3
- High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash), a C library for perceptual image hashing.
3
+ High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash) v1.3.0, a C library for perceptual image hashing.
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
@@ -2,16 +2,34 @@
2
2
 
3
3
  A high-performance, portable C library for Perceptual Hashing. Designed for image similarity detection with zero external dependencies (except for the included `stb_image`).
4
4
 
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ ## Language Bindings
8
+
9
+ Official and community-supported wrappers:
10
+ - **Python**: [python-libphash](https://github.com/gudoshnikovn/python-libphash) (`pip install python-libphash`)
11
+
12
+ ---
13
+
5
14
  ## Features
6
15
 
7
- - **Multiple Algorithms**:
8
- - `aHash` (Average Hash): Fast, based on average intensity.
9
- - `dHash` (Difference Hash): Fast, resistant to aspect ratio changes.
10
- - `pHash` (Perceptual Hash): Robust, uses Discrete Cosine Transform (DCT).
11
- - **FFI-Friendly**: Clean C API with opaque pointers, making it easy to wrap in Python (ctypes/cffi), Rust, or Node.js.
12
- - **Thread-Safe**: No global state.
16
+ - **Comprehensive Algorithm Suite**:
17
+ - `aHash` (Average Hash): Fast, based on average pixel intensity.
18
+ - `dHash` (Difference Hash): Extremely fast, resistant to aspect ratio changes.
19
+ - `pHash` (Perceptual Hash): High precision, uses optimized Discrete Cosine Transform (DCT).
20
+ - `mHash` (Median Hash): Robust against non-linear image adjustments.
21
+ - `bmh` (Block Mean Hash): Divides image into blocks for localized analysis.
22
+ - `wHash` (Wavelet Hash): Frequency-based hashing using Wavelet transform (if implemented).
23
+ - **High Performance**:
24
+ - Internal **Bilinear Interpolation** for high-quality image scaling.
25
+ - **Lazy-loading** grayscale cache to avoid redundant conversions.
26
+ - Pre-computed trigonometric tables for DCT.
27
+ - **FFI-Friendly**: Clean C API with opaque pointers, optimized for Python (ctypes/cffi), Rust, or Node.js.
28
+ - **Thread-Safe**: No global state (optimized one-time internal initialization).
13
29
  - **Cross-Platform**: Compatible with GCC, Clang, and MSVC.
14
30
 
31
+
32
+
15
33
  ## Architecture
16
34
 
17
35
  The library follows a strict separation between public API and internal implementation:
@@ -32,6 +50,7 @@ make test
32
50
 
33
51
  # Clean build artifacts
34
52
  make clean
53
+
35
54
  ```
36
55
 
37
56
  ## Usage Example (C)
@@ -46,12 +65,15 @@ int main() {
46
65
 
47
66
  ph_create(&ctx);
48
67
 
68
+ // Load and compute pHash for first image
49
69
  ph_load_from_file(ctx, "image1.jpg");
50
70
  ph_compute_phash(ctx, &hash1);
51
71
 
72
+ // Load and compute pHash for second image
52
73
  ph_load_from_file(ctx, "image2.jpg");
53
74
  ph_compute_phash(ctx, &hash2);
54
75
 
76
+ // Calculate similarity
55
77
  int distance = ph_hamming_distance(hash1, hash2);
56
78
  printf("Hamming Distance: %d\n", distance);
57
79
 
@@ -62,15 +84,17 @@ int main() {
62
84
  ph_free(ctx);
63
85
  return 0;
64
86
  }
87
+
65
88
  ```
66
89
 
67
90
  ## FFI Integration Notes
68
91
 
69
- - **Opaque Pointer**: `ph_context_t` is an opaque struct. In high-level languages, treat it as a `void*` or `uintptr_t`.
70
- - **Memory Management**: Always call `ph_free()` to release image data and context memory allocated on the C heap.
71
- - **Error Handling**: Functions return `ph_error_t` (int). `0` always indicates `PH_SUCCESS`.
92
+ * **Opaque Pointer**: `ph_context_t` is an opaque struct. In high-level languages, treat it as a `void*` or `uintptr_t`.
93
+ * **Memory Management**: Always call `ph_free()` to release image data and context memory allocated on the C heap.
94
+ * **Error Handling**: Functions return `ph_error_t` (int). `0` (PH_SUCCESS) indicates success.
72
95
 
73
96
  ## License
74
97
 
75
98
  This project is licensed under the MIT License - see the LICENSE file for details.
76
99
  Includes `stb_image` by Sean Barrett (Public Domain/MIT).
100
+
@@ -161,6 +161,7 @@ PH_API int ph_hamming_distance(uint64_t hash1, uint64_t hash2);
161
161
  PH_API int ph_hamming_distance_digest(const ph_digest_t *a, const ph_digest_t *b);
162
162
  PH_API double ph_l2_distance(const ph_digest_t *a, const ph_digest_t *b);
163
163
 
164
+ void init_dct_matrix(void);
164
165
  #ifdef __cplusplus
165
166
  }
166
167
  #endif
@@ -28,17 +28,26 @@ PH_API ph_error_t ph_create(ph_context_t **out_ctx) {
28
28
  if (!ctx)
29
29
  return PH_ERR_ALLOCATION_FAILED;
30
30
 
31
- // Initialize default gamma (2.2)
31
+ init_dct_matrix();
32
+
33
+ ctx->data = NULL;
34
+ ctx->gray_data = NULL;
35
+ ctx->width = 0;
36
+ ctx->height = 0;
37
+ ctx->channels = 0;
38
+ ctx->is_loaded = 0;
39
+
32
40
  ph_context_set_gamma(ctx, 2.2f);
33
41
 
34
42
  *out_ctx = ctx;
35
43
  return PH_SUCCESS;
36
44
  }
37
-
38
45
  PH_API void ph_free(ph_context_t *ctx) {
39
46
  if (ctx) {
40
47
  if (ctx->data)
41
48
  stbi_image_free(ctx->data);
49
+ if (ctx->gray_data)
50
+ free(ctx->gray_data);
42
51
  free(ctx);
43
52
  }
44
53
  }
@@ -0,0 +1,32 @@
1
+ #include "../internal.h"
2
+ #include <stdlib.h>
3
+
4
+ PH_API ph_error_t ph_compute_ahash(ph_context_t *ctx, uint64_t *out_hash) {
5
+ if (!ctx || !ctx->is_loaded || !out_hash) {
6
+ return PH_ERR_INVALID_ARGUMENT;
7
+ }
8
+
9
+ uint8_t *gray_full = ph_get_gray(ctx);
10
+ if (!gray_full) {
11
+ return PH_ERR_ALLOCATION_FAILED;
12
+ }
13
+
14
+ uint8_t tiny[64];
15
+ ph_resize_bilinear(gray_full, ctx->width, ctx->height, tiny, 8, 8);
16
+
17
+ uint64_t total_sum = 0;
18
+ for (int i = 0; i < 64; i++) {
19
+ total_sum += tiny[i];
20
+ }
21
+ uint8_t avg = (uint8_t)(total_sum / 64);
22
+
23
+ uint64_t hash = 0;
24
+ for (int i = 0; i < 64; i++) {
25
+ if (tiny[i] >= avg) {
26
+ hash |= (1ULL << i);
27
+ }
28
+ }
29
+
30
+ *out_hash = hash;
31
+ return PH_SUCCESS;
32
+ }
@@ -2,24 +2,27 @@
2
2
  #include <stdlib.h>
3
3
 
4
4
  PH_API ph_error_t ph_compute_dhash(ph_context_t *ctx, uint64_t *out_hash) {
5
- if (!ctx || !ctx->is_loaded || !out_hash)
5
+ if (!ctx || !ctx->is_loaded || !out_hash) {
6
6
  return PH_ERR_INVALID_ARGUMENT;
7
- uint8_t gray[72]; // 9x8
8
- uint8_t *full_gray = malloc(ctx->width * ctx->height);
9
- if (!full_gray)
7
+ }
8
+
9
+ uint8_t *gray_full = ph_get_gray(ctx);
10
+ if (!gray_full) {
10
11
  return PH_ERR_ALLOCATION_FAILED;
12
+ }
11
13
 
12
- ph_to_grayscale(ctx->data, ctx->width, ctx->height, ctx->channels, full_gray);
13
- ph_resize_grayscale(full_gray, ctx->width, ctx->height, gray, 9, 8);
14
- free(full_gray);
14
+ uint8_t tiny[72];
15
+ ph_resize_bilinear(gray_full, ctx->width, ctx->height, tiny, 9, 8);
15
16
 
16
17
  uint64_t hash = 0;
17
18
  for (int row = 0; row < 8; row++) {
18
19
  for (int col = 0; col < 8; col++) {
19
- if (gray[row * 9 + col] < gray[row * 9 + col + 1])
20
+ if (tiny[row * 9 + col] < tiny[row * 9 + col + 1]) {
20
21
  hash |= (1ULL << (row * 8 + col));
22
+ }
21
23
  }
22
24
  }
25
+
23
26
  *out_hash = hash;
24
27
  return PH_SUCCESS;
25
28
  }
@@ -0,0 +1,78 @@
1
+ #include "../internal.h"
2
+ #include <math.h>
3
+ #include <stdatomic.h>
4
+ #include <stdbool.h>
5
+ #include <stdlib.h>
6
+
7
+ static double dct_matrix[32][32];
8
+ static atomic_bool dct_globally_initialized = false;
9
+
10
+ void init_dct_matrix(void) {
11
+ if (atomic_exchange(&dct_globally_initialized, true)) {
12
+ return;
13
+ }
14
+ double c = sqrt(1.0 / 32.0);
15
+ for (int j = 0; j < 32; j++)
16
+ dct_matrix[0][j] = c;
17
+
18
+ c = sqrt(2.0 / 32.0);
19
+ for (int i = 1; i < 32; i++) {
20
+ for (int j = 0; j < 32; j++) {
21
+ dct_matrix[i][j] = c * cos(M_PI * i * (j + 0.5) / 32.0);
22
+ }
23
+ }
24
+ dct_globally_initialized = true;
25
+ }
26
+
27
+ PH_API ph_error_t ph_compute_phash(ph_context_t *ctx, uint64_t *out_hash) {
28
+ if (!ctx || !ctx->is_loaded || !out_hash)
29
+ return PH_ERR_INVALID_ARGUMENT;
30
+
31
+ uint8_t *gray_full = ph_get_gray(ctx);
32
+ if (!gray_full)
33
+ return PH_ERR_ALLOCATION_FAILED;
34
+
35
+ uint8_t gray32[1024];
36
+ ph_resize_bilinear(gray_full, ctx->width, ctx->height, gray32, 32, 32);
37
+
38
+ double temp[1024], dct_out[1024];
39
+
40
+ for (int i = 0; i < 32; i++) {
41
+ for (int j = 0; j < 32; j++) {
42
+ double sum = 0;
43
+ for (int k = 0; k < 32; k++)
44
+ sum += dct_matrix[j][k] * gray32[i * 32 + k];
45
+ temp[i * 32 + j] = sum;
46
+ }
47
+ }
48
+ for (int j = 0; j < 32; j++) {
49
+ for (int i = 0; i < 32; i++) {
50
+ double sum = 0;
51
+ for (int k = 0; k < 32; k++)
52
+ sum += dct_matrix[i][k] * temp[k * 32 + j];
53
+ dct_out[i * 32 + j] = sum;
54
+ }
55
+ }
56
+
57
+ double sum_dct = 0;
58
+ for (int i = 0; i < 8; i++) {
59
+ for (int j = 0; j < 8; j++) {
60
+ if (i == 0 && j == 0)
61
+ continue;
62
+ sum_dct += dct_out[i * 32 + j];
63
+ }
64
+ }
65
+
66
+ double avg = sum_dct / 63.0;
67
+ uint64_t hash = 0;
68
+ for (int i = 0; i < 8; i++) {
69
+ for (int j = 0; j < 8; j++) {
70
+ if (dct_out[i * 32 + j] > avg) {
71
+ hash |= (1ULL << (i * 8 + j));
72
+ }
73
+ }
74
+ }
75
+
76
+ *out_hash = hash;
77
+ return PH_SUCCESS;
78
+ }
@@ -4,15 +4,54 @@
4
4
  #include <stdlib.h>
5
5
  #include <string.h>
6
6
 
7
+ uint8_t *ph_get_gray(ph_context_t *ctx) {
8
+ if (!ctx->gray_data && ctx->data) {
9
+ ctx->gray_data = malloc(ctx->width * ctx->height);
10
+ if (ctx->gray_data) {
11
+ ph_to_grayscale(ctx->data, ctx->width, ctx->height, ctx->channels, ctx->gray_data);
12
+ }
13
+ }
14
+ return ctx->gray_data;
15
+ }
16
+
7
17
  void ph_to_grayscale(const uint8_t *src, int w, int h, int channels, uint8_t *dst) {
8
18
  for (int i = 0; i < w * h; i++) {
9
- uint8_t r = src[i * channels];
10
- uint8_t g = src[i * channels + 1];
11
- uint8_t b = src[i * channels + 2];
12
- dst[i] = (uint8_t)((77 * r + 150 * g + 29 * b) >> 8);
19
+ uint32_t r = src[i * channels];
20
+ uint32_t g = src[i * channels + 1];
21
+ uint32_t b = src[i * channels + 2];
22
+ dst[i] = (uint8_t)((r * 38 + g * 75 + b * 15) >> 7);
13
23
  }
14
24
  }
25
+ void ph_resize_bilinear(const uint8_t *src, int sw, int sh, uint8_t *dst, int dw, int dh) {
26
+ double x_ratio = (dw > 1) ? (double)(sw - 1) / (dw - 1) : 0;
27
+ double y_ratio = (dh > 1) ? (double)(sh - 1) / (dh - 1) : 0;
28
+
29
+ for (int i = 0; i < dh; i++) {
30
+ for (int j = 0; j < dw; j++) {
31
+ double x_pos = x_ratio * j;
32
+ double y_pos = y_ratio * i;
33
+ int x = (int)x_pos;
34
+ int y = (int)y_pos;
35
+
36
+ double x_diff = x_pos - x;
37
+ double y_diff = y_pos - y;
15
38
 
39
+ int index = y * sw + x;
40
+
41
+ int next_x = (x < sw - 1) ? 1 : 0;
42
+ int next_y = (y < sh - 1) ? sw : 0;
43
+
44
+ uint8_t a = src[index];
45
+ uint8_t b = src[index + next_x];
46
+ uint8_t c = src[index + next_y];
47
+ uint8_t d = src[index + next_y + next_x];
48
+
49
+ dst[i * dw + j] =
50
+ (uint8_t)(a * (1 - x_diff) * (1 - y_diff) + b * (x_diff) * (1 - y_diff) +
51
+ c * (y_diff) * (1 - x_diff) + d * (x_diff * y_diff));
52
+ }
53
+ }
54
+ }
16
55
  void ph_resize_grayscale(const uint8_t *src, int sw, int sh, uint8_t *dst, int dw, int dh) {
17
56
  double x_ratio = (double)sw / dw;
18
57
  double y_ratio = (double)sh / dh;
@@ -14,15 +14,20 @@ void ph_to_grayscale(const uint8_t *src, int w, int h, int channels, uint8_t *ds
14
14
  /* Resizes a grayscale image */
15
15
  void ph_resize_grayscale(const uint8_t *src, int sw, int sh, uint8_t *dst, int dw, int dh);
16
16
 
17
- //* Applies a 3x3 Gaussian Blur to reduce noise */
17
+ /* Applies a 3x3 Gaussian Blur to reduce noise */
18
18
  void ph_apply_gaussian_blur(const uint8_t *src, int w, int h, uint8_t *dst);
19
19
 
20
20
  /* Applies Gamma Correction (gamma=2.2) to normalize brightness */
21
21
  void ph_apply_gamma(const ph_context_t *ctx, uint8_t *data, int w, int h);
22
22
 
23
+ void ph_resize_bilinear(const uint8_t *src, int sw, int sh, uint8_t *dst, int dw, int dh);
24
+
25
+ uint8_t *ph_get_gray(ph_context_t *ctx);
26
+
23
27
  /* Internal Context Structure */
24
28
  struct ph_context {
25
29
  uint8_t *data;
30
+ uint8_t *gray_data;
26
31
  int width;
27
32
  int height;
28
33
  int channels;
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-libphash"
7
- version = "1.0.3"
7
+ version = "1.1.0"
8
8
  description = "High-performance perceptual hashing library (CFFI bindings)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-libphash
3
- Version: 1.0.3
3
+ Version: 1.1.0
4
4
  Summary: High-performance perceptual hashing library (CFFI bindings)
5
5
  Author-email: gudoshnikovn <gudoshnikov-na@yandex.ru>
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ Dynamic: license-file
20
20
 
21
21
  # python-libphash
22
22
 
23
- High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash), a C library for perceptual image hashing.
23
+ High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash) v1.3.0, a C library for perceptual image hashing.
24
24
 
25
25
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
26
26
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
@@ -1,28 +0,0 @@
1
- #include "../internal.h"
2
- #include <stdlib.h>
3
-
4
- PH_API ph_error_t ph_compute_ahash(ph_context_t *ctx, uint64_t *out_hash) {
5
- if (!ctx || !ctx->is_loaded || !out_hash)
6
- return PH_ERR_INVALID_ARGUMENT;
7
- uint8_t gray[64];
8
- uint8_t *full_gray = malloc(ctx->width * ctx->height);
9
- if (!full_gray)
10
- return PH_ERR_ALLOCATION_FAILED;
11
-
12
- ph_to_grayscale(ctx->data, ctx->width, ctx->height, ctx->channels, full_gray);
13
- ph_resize_grayscale(full_gray, ctx->width, ctx->height, gray, 8, 8);
14
- free(full_gray);
15
-
16
- uint64_t avg = 0;
17
- for (int i = 0; i < 64; i++)
18
- avg += gray[i];
19
- avg /= 64;
20
-
21
- uint64_t hash = 0;
22
- for (int i = 0; i < 64; i++) {
23
- if (gray[i] >= avg)
24
- hash |= (1ULL << i);
25
- }
26
- *out_hash = hash;
27
- return PH_SUCCESS;
28
- }
@@ -1,57 +0,0 @@
1
- #include "../internal.h"
2
- #include <math.h>
3
- #include <stdlib.h>
4
-
5
- static void ph_dct_1d(const double *src, double *dst, int size) {
6
- for (int i = 0; i < size; i++) {
7
- double sum = 0;
8
- for (int j = 0; j < size; j++)
9
- sum += src[j] * cos(M_PI * i * (j + 0.5) / size);
10
- dst[i] = sum * ((i == 0) ? sqrt(1.0 / size) : sqrt(2.0 / size));
11
- }
12
- }
13
-
14
- PH_API ph_error_t ph_compute_phash(ph_context_t *ctx, uint64_t *out_hash) {
15
- if (!ctx || !ctx->is_loaded || !out_hash)
16
- return PH_ERR_INVALID_ARGUMENT;
17
- uint8_t gray32[1024];
18
- uint8_t *full_gray = malloc(ctx->width * ctx->height);
19
- if (!full_gray)
20
- return PH_ERR_ALLOCATION_FAILED;
21
-
22
- ph_to_grayscale(ctx->data, ctx->width, ctx->height, ctx->channels, full_gray);
23
- ph_resize_grayscale(full_gray, ctx->width, ctx->height, gray32, 32, 32);
24
- free(full_gray);
25
-
26
- double dct_in[1024], dct_out[1024], temp[1024];
27
- for (int i = 0; i < 1024; i++)
28
- dct_in[i] = (double)gray32[i];
29
-
30
- // 2D DCT
31
- for (int i = 0; i < 32; i++)
32
- ph_dct_1d(&dct_in[i * 32], &temp[i * 32], 32);
33
- for (int j = 0; j < 32; j++) {
34
- double col_in[32], col_out[32];
35
- for (int i = 0; i < 32; i++)
36
- col_in[i] = temp[i * 32 + j];
37
- ph_dct_1d(col_in, col_out, 32);
38
- for (int i = 0; i < 32; i++)
39
- dct_out[i * 32 + j] = col_out[i];
40
- }
41
-
42
- double sum = 0;
43
- for (int i = 0; i < 8; i++)
44
- for (int j = 0; j < 8; j++)
45
- if (i != 0 || j != 0)
46
- sum += dct_out[i * 32 + j];
47
-
48
- double avg = sum / 63.0;
49
- uint64_t hash = 0;
50
- for (int i = 0; i < 8; i++)
51
- for (int j = 0; j < 8; j++)
52
- if (dct_out[i * 32 + j] > avg)
53
- hash |= (1ULL << (i * 8 + j));
54
-
55
- *out_hash = hash;
56
- return PH_SUCCESS;
57
- }
File without changes