python-libphash 1.0.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 (60) hide show
  1. python_libphash-1.0.0/LICENSE +21 -0
  2. python_libphash-1.0.0/MANIFEST.in +4 -0
  3. python_libphash-1.0.0/PKG-INFO +124 -0
  4. python_libphash-1.0.0/README.md +104 -0
  5. python_libphash-1.0.0/native/libphash/.clang-format +18 -0
  6. python_libphash-1.0.0/native/libphash/.git +1 -0
  7. python_libphash-1.0.0/native/libphash/.gitignore +25 -0
  8. python_libphash-1.0.0/native/libphash/CMakeLists.txt +48 -0
  9. python_libphash-1.0.0/native/libphash/LICENSE +21 -0
  10. python_libphash-1.0.0/native/libphash/Makefile +51 -0
  11. python_libphash-1.0.0/native/libphash/README.md +76 -0
  12. python_libphash-1.0.0/native/libphash/include/libphash.h +168 -0
  13. python_libphash-1.0.0/native/libphash/src/core.c +73 -0
  14. python_libphash-1.0.0/native/libphash/src/hashes/ahash.c +28 -0
  15. python_libphash-1.0.0/native/libphash/src/hashes/bmh.c +36 -0
  16. python_libphash-1.0.0/native/libphash/src/hashes/color_hash.c +56 -0
  17. python_libphash-1.0.0/native/libphash/src/hashes/common.c +98 -0
  18. python_libphash-1.0.0/native/libphash/src/hashes/dhash.c +25 -0
  19. python_libphash-1.0.0/native/libphash/src/hashes/mhash.c +33 -0
  20. python_libphash-1.0.0/native/libphash/src/hashes/phash.c +57 -0
  21. python_libphash-1.0.0/native/libphash/src/hashes/radial.c +103 -0
  22. python_libphash-1.0.0/native/libphash/src/hashes/whash.c +53 -0
  23. python_libphash-1.0.0/native/libphash/src/image.c +66 -0
  24. python_libphash-1.0.0/native/libphash/src/internal.h +34 -0
  25. python_libphash-1.0.0/native/libphash/tests/photo.jpeg +0 -0
  26. python_libphash-1.0.0/native/libphash/tests/photo_color_changed.jpeg +0 -0
  27. python_libphash-1.0.0/native/libphash/tests/photo_copy.jpeg +0 -0
  28. python_libphash-1.0.0/native/libphash/tests/photo_rotated_90.jpeg +0 -0
  29. python_libphash-1.0.0/native/libphash/tests/test_bmh.c +41 -0
  30. python_libphash-1.0.0/native/libphash/tests/test_color.c +55 -0
  31. python_libphash-1.0.0/native/libphash/tests/test_core.c +16 -0
  32. python_libphash-1.0.0/native/libphash/tests/test_hashes.c +14 -0
  33. python_libphash-1.0.0/native/libphash/tests/test_image_hashes.c +39 -0
  34. python_libphash-1.0.0/native/libphash/tests/test_internal.c +36 -0
  35. python_libphash-1.0.0/native/libphash/tests/test_macros.h +37 -0
  36. python_libphash-1.0.0/native/libphash/tests/test_mhash.c +38 -0
  37. python_libphash-1.0.0/native/libphash/tests/test_radial.c +72 -0
  38. python_libphash-1.0.0/native/libphash/tests/test_whash.c +37 -0
  39. python_libphash-1.0.0/native/libphash/vendor/stb_image.h +8748 -0
  40. python_libphash-1.0.0/pyproject.toml +35 -0
  41. python_libphash-1.0.0/setup.cfg +4 -0
  42. python_libphash-1.0.0/setup.py +12 -0
  43. python_libphash-1.0.0/src/libphash/__init__.py +24 -0
  44. python_libphash-1.0.0/src/libphash/_build.py +97 -0
  45. python_libphash-1.0.0/src/libphash/_native.pyi +39 -0
  46. python_libphash-1.0.0/src/libphash/context.py +94 -0
  47. python_libphash-1.0.0/src/libphash/exceptions.py +36 -0
  48. python_libphash-1.0.0/src/libphash/py.typed +0 -0
  49. python_libphash-1.0.0/src/libphash/types.py +58 -0
  50. python_libphash-1.0.0/src/libphash/utils.py +28 -0
  51. python_libphash-1.0.0/src/python_libphash.egg-info/PKG-INFO +124 -0
  52. python_libphash-1.0.0/src/python_libphash.egg-info/SOURCES.txt +58 -0
  53. python_libphash-1.0.0/src/python_libphash.egg-info/dependency_links.txt +1 -0
  54. python_libphash-1.0.0/src/python_libphash.egg-info/not-zip-safe +1 -0
  55. python_libphash-1.0.0/src/python_libphash.egg-info/requires.txt +6 -0
  56. python_libphash-1.0.0/src/python_libphash.egg-info/top_level.txt +1 -0
  57. python_libphash-1.0.0/tests/test_digests.py +27 -0
  58. python_libphash-1.0.0/tests/test_distances.py +39 -0
  59. python_libphash-1.0.0/tests/test_hashes.py +23 -0
  60. python_libphash-1.0.0/tests/test_loading.py +37 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gudoshnikovn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include native/libphash *
4
+ recursive-exclude native/libphash/.git *
@@ -0,0 +1,124 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-libphash
3
+ Version: 1.0.0
4
+ Summary: High-performance perceptual hashing library (CFFI bindings)
5
+ Author-email: gudoshnikovn <gudoshnikov-na@yandex.ru>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/gudoshnikovn/python-libphash
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: C
10
+ Classifier: Topic :: Multimedia :: Graphics
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: cffi>=1.15.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest; extra == "dev"
17
+ Requires-Dist: mypy; extra == "dev"
18
+ Requires-Dist: ruff; extra == "dev"
19
+ Dynamic: license-file
20
+
21
+ # python-libphash
22
+
23
+ High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash), a C library for perceptual image hashing.
24
+
25
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
26
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
27
+
28
+ ## Overview
29
+
30
+ `libphash` provides multiple algorithms to generate "perceptual hashes" of images. Unlike cryptographic hashes (like MD5 or SHA256), perceptual hashes change only slightly if the image is resized, compressed, or has minor color adjustments. This makes them ideal for finding duplicate or similar images.
31
+
32
+ ### Supported Algorithms
33
+
34
+ * **64-bit Hashes (uint64):**
35
+ * `ahash`: Average Hash
36
+ * `dhash`: Difference Hash
37
+ * `phash`: Perceptual Hash (DCT based)
38
+ * `whash`: Wavelet Hash
39
+ * `mhash`: Median Hash
40
+ * **Digest Hashes (Multi-byte):**
41
+ * `bmh`: Block Mean Hash
42
+ * `color_hash`: Color Moment Hash
43
+ * `radial_hash`: Radial Variance Hash
44
+
45
+ ## Installation
46
+
47
+ ### Prerequisites
48
+ * A C compiler (GCC/Clang or MSVC)
49
+ * Python 3.8 or higher
50
+
51
+ ### Install from source
52
+ ```bash
53
+ git clone --recursive https://github.com/yourusername/python-libphash.git
54
+ cd python-libphash
55
+ pip install .
56
+ ```
57
+
58
+ ## Quick Start
59
+
60
+ ### Basic Usage
61
+ ```python
62
+ from libphash import ImageContext, HashMethod, hamming_distance
63
+
64
+ # Use the context manager for automatic memory management
65
+ with ImageContext("photo.jpg") as ctx:
66
+ # Get standard 64-bit hashes
67
+ phash_val = ctx.phash
68
+ dhash_val = ctx.dhash
69
+
70
+ print(f"pHash: {phash_val:016x}")
71
+ print(f"dHash: {dhash_val:016x}")
72
+
73
+ # Compare two images
74
+ from libphash import compare_images
75
+ distance = compare_images("image1.jpg", "image2.jpg", method=HashMethod.PHASH)
76
+ print(f"Hamming Distance: {distance}")
77
+ ```
78
+
79
+ ### Working with Digests (Advanced Hashes)
80
+ Algorithms like Radial Hash or Color Hash return a `Digest` object instead of a single integer.
81
+
82
+ ```python
83
+ with ImageContext("photo.jpg") as ctx:
84
+ digest = ctx.radial_hash
85
+ print(f"Digest size: {digest.size} bytes")
86
+ print(f"Raw data: {digest.data.hex()}")
87
+
88
+ # Comparing digests
89
+ with ImageContext("photo_v2.jpg") as ctx2:
90
+ digest2 = ctx2.radial_hash
91
+
92
+ # Hamming distance for bit-wise comparison
93
+ h_dist = digest.distance_hamming(digest2)
94
+
95
+ # L2 (Euclidean) distance for similarity
96
+ l2_dist = digest.distance_l2(digest2)
97
+ ```
98
+
99
+ ## API Reference
100
+
101
+ ### `ImageContext`
102
+ The main class for loading images and computing hashes.
103
+ * `__init__(path=None, bytes_data=None)`: Load an image from a file path or memory.
104
+ * `set_gamma(gamma: float)`: Set gamma correction (useful for Radial Hash).
105
+ * **Properties**: `ahash`, `dhash`, `phash`, `whash`, `mhash` (returns `int`).
106
+ * **Properties**: `bmh`, `color_hash`, `radial_hash` (returns `Digest`).
107
+
108
+ ### `Digest`
109
+ * `data`: The raw `bytes` of the hash.
110
+ * `size`: Length of the hash in bytes.
111
+ * `distance_hamming(other)`: Calculates bit-wise distance.
112
+ * `distance_l2(other)`: Calculates Euclidean distance.
113
+
114
+ ### Utilities
115
+ * `hamming_distance(h1: int, h2: int)`: Returns the number of differing bits between two 64-bit integers.
116
+ * `get_hash(path, method)`: Quick way to get a hash without manual context management.
117
+ * `compare_images(path1, path2, method)`: Returns the Hamming distance between two image files.
118
+
119
+ ## Performance
120
+ Since the core logic is implemented in C and uses `stb_image` for decoding, `libphash` is significantly faster than pure-Python alternatives. It also uses CFFI's "out-of-line" mode for minimal overhead.
121
+
122
+ ## License
123
+ This project is licensed under the MIT License - see the LICENSE file for details.
124
+
@@ -0,0 +1,104 @@
1
+ # python-libphash
2
+
3
+ High-performance Python bindings for [libphash](https://github.com/gudoshnikovn/libphash), a C library for perceptual image hashing.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
7
+
8
+ ## Overview
9
+
10
+ `libphash` provides multiple algorithms to generate "perceptual hashes" of images. Unlike cryptographic hashes (like MD5 or SHA256), perceptual hashes change only slightly if the image is resized, compressed, or has minor color adjustments. This makes them ideal for finding duplicate or similar images.
11
+
12
+ ### Supported Algorithms
13
+
14
+ * **64-bit Hashes (uint64):**
15
+ * `ahash`: Average Hash
16
+ * `dhash`: Difference Hash
17
+ * `phash`: Perceptual Hash (DCT based)
18
+ * `whash`: Wavelet Hash
19
+ * `mhash`: Median Hash
20
+ * **Digest Hashes (Multi-byte):**
21
+ * `bmh`: Block Mean Hash
22
+ * `color_hash`: Color Moment Hash
23
+ * `radial_hash`: Radial Variance Hash
24
+
25
+ ## Installation
26
+
27
+ ### Prerequisites
28
+ * A C compiler (GCC/Clang or MSVC)
29
+ * Python 3.8 or higher
30
+
31
+ ### Install from source
32
+ ```bash
33
+ git clone --recursive https://github.com/yourusername/python-libphash.git
34
+ cd python-libphash
35
+ pip install .
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### Basic Usage
41
+ ```python
42
+ from libphash import ImageContext, HashMethod, hamming_distance
43
+
44
+ # Use the context manager for automatic memory management
45
+ with ImageContext("photo.jpg") as ctx:
46
+ # Get standard 64-bit hashes
47
+ phash_val = ctx.phash
48
+ dhash_val = ctx.dhash
49
+
50
+ print(f"pHash: {phash_val:016x}")
51
+ print(f"dHash: {dhash_val:016x}")
52
+
53
+ # Compare two images
54
+ from libphash import compare_images
55
+ distance = compare_images("image1.jpg", "image2.jpg", method=HashMethod.PHASH)
56
+ print(f"Hamming Distance: {distance}")
57
+ ```
58
+
59
+ ### Working with Digests (Advanced Hashes)
60
+ Algorithms like Radial Hash or Color Hash return a `Digest` object instead of a single integer.
61
+
62
+ ```python
63
+ with ImageContext("photo.jpg") as ctx:
64
+ digest = ctx.radial_hash
65
+ print(f"Digest size: {digest.size} bytes")
66
+ print(f"Raw data: {digest.data.hex()}")
67
+
68
+ # Comparing digests
69
+ with ImageContext("photo_v2.jpg") as ctx2:
70
+ digest2 = ctx2.radial_hash
71
+
72
+ # Hamming distance for bit-wise comparison
73
+ h_dist = digest.distance_hamming(digest2)
74
+
75
+ # L2 (Euclidean) distance for similarity
76
+ l2_dist = digest.distance_l2(digest2)
77
+ ```
78
+
79
+ ## API Reference
80
+
81
+ ### `ImageContext`
82
+ The main class for loading images and computing hashes.
83
+ * `__init__(path=None, bytes_data=None)`: Load an image from a file path or memory.
84
+ * `set_gamma(gamma: float)`: Set gamma correction (useful for Radial Hash).
85
+ * **Properties**: `ahash`, `dhash`, `phash`, `whash`, `mhash` (returns `int`).
86
+ * **Properties**: `bmh`, `color_hash`, `radial_hash` (returns `Digest`).
87
+
88
+ ### `Digest`
89
+ * `data`: The raw `bytes` of the hash.
90
+ * `size`: Length of the hash in bytes.
91
+ * `distance_hamming(other)`: Calculates bit-wise distance.
92
+ * `distance_l2(other)`: Calculates Euclidean distance.
93
+
94
+ ### Utilities
95
+ * `hamming_distance(h1: int, h2: int)`: Returns the number of differing bits between two 64-bit integers.
96
+ * `get_hash(path, method)`: Quick way to get a hash without manual context management.
97
+ * `compare_images(path1, path2, method)`: Returns the Hamming distance between two image files.
98
+
99
+ ## Performance
100
+ Since the core logic is implemented in C and uses `stb_image` for decoding, `libphash` is significantly faster than pure-Python alternatives. It also uses CFFI's "out-of-line" mode for minimal overhead.
101
+
102
+ ## License
103
+ This project is licensed under the MIT License - see the LICENSE file for details.
104
+
@@ -0,0 +1,18 @@
1
+ ---
2
+ BasedOnStyle: LLVM
3
+ IndentWidth: 4
4
+ ColumnLimit: 100
5
+ UseTab: Never
6
+ BreakBeforeBraces: Attach
7
+ AllowShortIfStatementsOnASingleLine: false
8
+ IndentCaseLabels: true
9
+ AlignAfterOpenBracket: Align
10
+ AlwaysBreakAfterReturnType: None
11
+ PointerAlignment: Right
12
+ SpaceAfterCStyleCast: false
13
+ SpaceBeforeParens: ControlStatements
14
+ Cpp11BracedListStyle: true
15
+ SortIncludes: true
16
+ MaxEmptyLinesToKeep: 1
17
+ ---
18
+
@@ -0,0 +1 @@
1
+ gitdir: ../../.git/modules/native/libphash
@@ -0,0 +1,25 @@
1
+ # Object files and libraries
2
+ *.o
3
+ *.a
4
+ *.so
5
+ *.dylib
6
+ *.dll
7
+ obj/
8
+
9
+ # Executables (Tests)
10
+ test_*
11
+ !tests/test_*.c
12
+
13
+ # IDEs and Editors
14
+ .vscode/
15
+ .idea/
16
+ *.swp
17
+ *.swo
18
+ .DS_Store
19
+
20
+ # Build artifacts
21
+ dist/
22
+ build/
23
+
24
+ # Test images (if generated)
25
+ tests/output_*.jpeg
@@ -0,0 +1,48 @@
1
+ cmake_minimum_required(VERSION 3.10)
2
+ project(libphash VERSION 1.2.0 LANGUAGES C)
3
+
4
+ # --- Options ---
5
+ option(PHASH_BUILD_TESTS "Build tests" ON)
6
+ option(PHASH_BUILD_SHARED "Build shared library" OFF)
7
+
8
+ # --- Compiler Flags ---
9
+ if(MSVC)
10
+ add_compile_options(/W3 /O2)
11
+ else()
12
+ add_compile_options(-Wall -Wextra -O3 -fPIC)
13
+ # Architecture specific optimizations
14
+ if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64")
15
+ add_compile_options(-msse4.2)
16
+ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
17
+ add_compile_options(-march=armv8-a+simd)
18
+ endif()
19
+ endif()
20
+
21
+ # --- Library ---
22
+ file(GLOB_RECURSE SOURCES "src/*.c")
23
+
24
+ if(PHASH_BUILD_SHARED)
25
+ add_library(phash SHARED ${SOURCES})
26
+ target_compile_definitions(phash PRIVATE LIBPHASH_EXPORTS)
27
+ else()
28
+ add_library(phash STATIC ${SOURCES})
29
+ endif()
30
+
31
+ target_include_directories(phash PUBLIC
32
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
33
+ $<INSTALL_INTERFACE:include>
34
+ )
35
+
36
+ target_link_libraries(phash PRIVATE m) # Link math library
37
+
38
+ # --- Tests ---
39
+ if(PHASH_BUILD_TESTS)
40
+ enable_testing()
41
+ file(GLOB TEST_SOURCES "tests/test_*.c")
42
+ foreach(test_src ${TEST_SOURCES})
43
+ get_filename_component(test_name ${test_src} NAME_WE)
44
+ add_executable(${test_name} ${test_src})
45
+ target_link_libraries(${test_name} PRIVATE phash)
46
+ add_test(NAME ${test_name} COMMAND ${test_name})
47
+ endforeach()
48
+ endif()
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gudoshnikovn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,51 @@
1
+ CC = gcc
2
+ CFLAGS = -I./include -O3 -Wall -Wextra -fPIC
3
+ LDFLAGS = -lm
4
+
5
+ UNAME_M := $(shell uname -m)
6
+ ifeq ($(UNAME_M),x86_64)
7
+ CFLAGS += -msse4.2
8
+ endif
9
+ ifeq ($(UNAME_M),arm64)
10
+ CFLAGS += -march=armv8-a+simd
11
+ endif
12
+
13
+ LIB_NAME = libphash.a
14
+ OBJ_DIR = obj
15
+ SRC_DIR = src
16
+ HASH_DIR = $(SRC_DIR)/hashes
17
+ TEST_DIR = tests
18
+ INC_DIR = include
19
+
20
+ SRCS = $(wildcard $(SRC_DIR)/*.c) $(wildcard $(HASH_DIR)/*.c)
21
+ OBJS = $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
22
+
23
+ TEST_SRCS = $(wildcard $(TEST_DIR)/test_*.c)
24
+ TEST_BINS = $(TEST_SRCS:$(TEST_DIR)/%.c=%)
25
+
26
+ all: $(LIB_NAME) $(TEST_BINS)
27
+
28
+ format:
29
+ find $(SRC_DIR) $(TEST_DIR) $(INC_DIR) -name "*.c" -o -name "*.h" | xargs clang-format -i
30
+
31
+ debug: CFLAGS = -I./include -g -O0 -fsanitize=address,undefined -Wall -Wextra -fPIC
32
+ debug: clean all
33
+
34
+ $(LIB_NAME): $(OBJS)
35
+ ar rcs $@ $^
36
+
37
+ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
38
+ @mkdir -p $(dir $@)
39
+ $(CC) $(CFLAGS) -c $< -o $@
40
+
41
+ test_%: $(TEST_DIR)/test_%.c $(LIB_NAME)
42
+ $(CC) $(CFLAGS) $< $(LIB_NAME) -o $@ $(LDFLAGS)
43
+
44
+ test: $(TEST_BINS)
45
+ @for test in $(TEST_BINS); do ./$$test || exit 1; done
46
+ @echo "ALL TESTS PASSED"
47
+
48
+ clean:
49
+ rm -rf $(OBJ_DIR) *.a test_*
50
+
51
+ .PHONY: all debug test clean format
@@ -0,0 +1,76 @@
1
+ # libphash
2
+
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
+
5
+ ## Features
6
+
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.
13
+ - **Cross-Platform**: Compatible with GCC, Clang, and MSVC.
14
+
15
+ ## Architecture
16
+
17
+ The library follows a strict separation between public API and internal implementation:
18
+ - `include/libphash.h`: Public interface and error codes.
19
+ - `src/internal.h`: Internal structures and image processing helpers.
20
+ - `src/hashes/`: Core hash algorithm implementations.
21
+
22
+ ## Building
23
+
24
+ To build the static library and run tests, you only need `make` and a C compiler:
25
+
26
+ ```bash
27
+ # Build the library (libphash.a)
28
+ make
29
+
30
+ # Run all tests
31
+ make test
32
+
33
+ # Clean build artifacts
34
+ make clean
35
+ ```
36
+
37
+ ## Usage Example (C)
38
+
39
+ ```c
40
+ #include <libphash.h>
41
+ #include <stdio.h>
42
+
43
+ int main() {
44
+ ph_context_t *ctx = NULL;
45
+ uint64_t hash1, hash2;
46
+
47
+ ph_create(&ctx);
48
+
49
+ ph_load_from_file(ctx, "image1.jpg");
50
+ ph_compute_phash(ctx, &hash1);
51
+
52
+ ph_load_from_file(ctx, "image2.jpg");
53
+ ph_compute_phash(ctx, &hash2);
54
+
55
+ int distance = ph_hamming_distance(hash1, hash2);
56
+ printf("Hamming Distance: %d\n", distance);
57
+
58
+ if (distance < 5) {
59
+ printf("Images are very similar!\n");
60
+ }
61
+
62
+ ph_free(ctx);
63
+ return 0;
64
+ }
65
+ ```
66
+
67
+ ## FFI Integration Notes
68
+
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`.
72
+
73
+ ## License
74
+
75
+ This project is licensed under the MIT License - see the LICENSE file for details.
76
+ Includes `stb_image` by Sean Barrett (Public Domain/MIT).
@@ -0,0 +1,168 @@
1
+ #ifndef LIBPHASH_H
2
+ #define LIBPHASH_H
3
+
4
+ #include <stddef.h>
5
+ #include <stdint.h>
6
+
7
+ /**
8
+ * @file libphash.h
9
+ * @brief High-performance, thread-safe perceptual hashing library.
10
+ *
11
+ * Designed for easy FFI integration (Python, Rust, Node.js).
12
+ * All functions are thread-safe provided they operate on different contexts.
13
+ */
14
+
15
+ // --- Platform & Export Macros ---
16
+ #ifndef PH_API
17
+ #if defined(_WIN32) || defined(__CYGWIN__)
18
+ #ifdef LIBPHASH_EXPORTS
19
+ #define PH_API __declspec(dllexport)
20
+ #else
21
+ #define PH_API __declspec(dllimport)
22
+ #endif
23
+ #else
24
+ #if __GNUC__ >= 4
25
+ #define PH_API __attribute__((visibility("default")))
26
+ #else
27
+ #define PH_API
28
+ #endif
29
+ #endif
30
+ #endif
31
+
32
+ #ifndef PH_NODISCARD
33
+ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
34
+ #define PH_NODISCARD [[nodiscard]]
35
+ #elif defined(__GNUC__) || defined(__clang__)
36
+ #define PH_NODISCARD __attribute__((warn_unused_result))
37
+ #elif defined(_MSC_VER) && _MSC_VER >= 1700
38
+ #define PH_NODISCARD _Check_return_
39
+ #else
40
+ #define PH_NODISCARD
41
+ #endif
42
+ #endif
43
+
44
+ #ifdef __cplusplus
45
+ extern "C" {
46
+ #endif
47
+
48
+ // --- Constants ---
49
+
50
+ /** Maximum size in bytes for any digest supported by the library (64 bytes =
51
+ * 512 bits). */
52
+ #define PH_DIGEST_MAX_BYTES 64
53
+
54
+ // --- Error Codes ---
55
+
56
+ typedef enum {
57
+ PH_SUCCESS = 0,
58
+ PH_ERR_ALLOCATION_FAILED = -1,
59
+ PH_ERR_DECODE_FAILED = -2,
60
+ PH_ERR_INVALID_ARGUMENT = -3,
61
+ PH_ERR_NOT_IMPLEMENTED = -4,
62
+ PH_ERR_EMPTY_IMAGE = -5,
63
+ } ph_error_t;
64
+
65
+ // --- Types ---
66
+
67
+ /**
68
+ * @brief Opaque context structure holding image data and configuration.
69
+ * Treat this as a void* in FFI.
70
+ */
71
+ typedef struct ph_context ph_context_t;
72
+
73
+ /**
74
+ * @brief A flat structure representing a hash digest.
75
+ *
76
+ * @note This structure is FFI-safe and can be allocated on the stack.
77
+ * It does not own any heap memory.
78
+ */
79
+ typedef struct {
80
+ uint8_t data[PH_DIGEST_MAX_BYTES]; ///< The raw hash bytes.
81
+ uint8_t size; ///< Number of valid bytes in 'data'.
82
+ uint8_t reserved[7]; ///< Padding for 64-bit alignment.
83
+ } ph_digest_t;
84
+
85
+ // --- Lifecycle & Configuration ---
86
+
87
+ /**
88
+ * @brief Returns the library version string (e.g., "1.2.0").
89
+ */
90
+ PH_API const char *ph_version(void);
91
+
92
+ /**
93
+ * @brief Allocates a new context with default settings (Gamma 2.2).
94
+ * @param[out] out_ctx Pointer to the created context.
95
+ */
96
+ PH_API PH_NODISCARD ph_error_t ph_create(ph_context_t **out_ctx);
97
+
98
+ /**
99
+ * @brief Frees the context and all associated image memory.
100
+ * @param ctx The context to free. Safe to pass NULL.
101
+ */
102
+ PH_API void ph_free(ph_context_t *ctx);
103
+
104
+ /**
105
+ * @brief Sets the gamma correction value for the context.
106
+ *
107
+ * Recomputes the internal lookup table (LUT).
108
+ * Default value is 2.2.
109
+ *
110
+ * @param ctx The context.
111
+ * @param gamma The gamma value (e.g., 2.2). Must be > 0.
112
+ */
113
+ PH_API void ph_context_set_gamma(ph_context_t *ctx, float gamma);
114
+
115
+ // --- Loading ---
116
+
117
+ /**
118
+ * @brief Loads an image from a file path.
119
+ * @param ctx The context.
120
+ * @param filepath Path to the image file.
121
+ */
122
+ PH_API PH_NODISCARD ph_error_t ph_load_from_file(ph_context_t *ctx, const char *filepath);
123
+
124
+ /**
125
+ * @brief Loads an image from a memory buffer.
126
+ * @param ctx The context.
127
+ * @param buffer Pointer to the raw file data (e.g., JPEG bytes).
128
+ * @param length Size of the buffer.
129
+ */
130
+ PH_API PH_NODISCARD ph_error_t ph_load_from_memory(ph_context_t *ctx, const uint8_t *buffer,
131
+ size_t length);
132
+
133
+ // --- uint64_t Hash Algorithms ---
134
+
135
+ PH_API PH_NODISCARD ph_error_t ph_compute_ahash(ph_context_t *ctx, uint64_t *out_hash);
136
+ PH_API PH_NODISCARD ph_error_t ph_compute_dhash(ph_context_t *ctx, uint64_t *out_hash);
137
+ PH_API PH_NODISCARD ph_error_t ph_compute_phash(ph_context_t *ctx, uint64_t *out_hash);
138
+ PH_API PH_NODISCARD ph_error_t ph_compute_whash(ph_context_t *ctx, uint64_t *out_hash);
139
+ PH_API PH_NODISCARD ph_error_t ph_compute_mhash(ph_context_t *ctx, uint64_t *out_hash);
140
+
141
+ // --- Digest Hash Algorithms ---
142
+
143
+ /**
144
+ * @brief Computes Block Mean Hash (BMH). Returns a 256-bit (32-byte) digest.
145
+ */
146
+ PH_API PH_NODISCARD ph_error_t ph_compute_bmh(ph_context_t *ctx, ph_digest_t *out_digest);
147
+
148
+ /**
149
+ * @brief Computes Color Hash. Returns a digest representing color distribution.
150
+ */
151
+ PH_API PH_NODISCARD ph_error_t ph_compute_color_hash(ph_context_t *ctx, ph_digest_t *out_digest);
152
+
153
+ /**
154
+ * @brief Computes Radial Hash. Robust against rotation. Uses context Gamma.
155
+ */
156
+ PH_API PH_NODISCARD ph_error_t ph_compute_radial_hash(ph_context_t *ctx, ph_digest_t *out_digest);
157
+
158
+ // --- Comparison Functions ---
159
+
160
+ PH_API int ph_hamming_distance(uint64_t hash1, uint64_t hash2);
161
+ PH_API int ph_hamming_distance_digest(const ph_digest_t *a, const ph_digest_t *b);
162
+ PH_API double ph_l2_distance(const ph_digest_t *a, const ph_digest_t *b);
163
+
164
+ #ifdef __cplusplus
165
+ }
166
+ #endif
167
+
168
+ #endif // LIBPHASH_H