{"id":19654955,"url":"https://github.com/kilianb/jimagehash","last_synced_at":"2025-04-04T19:11:23.190Z","repository":{"id":41658391,"uuid":"122503006","full_name":"KilianB/JImageHash","owner":"KilianB","description":"Perceptual image hashing library used to match similar images","archived":false,"fork":false,"pushed_at":"2024-03-08T02:36:30.000Z","size":7468,"stargazers_count":419,"open_issues_count":43,"forks_count":84,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-28T18:12:52.041Z","etag":null,"topics":["fingerprinting","image","java","perceptive","perceptual-hashes","phash"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KilianB.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-22T16:17:41.000Z","updated_at":"2025-03-27T08:25:46.000Z","dependencies_parsed_at":"2022-09-26T22:01:46.914Z","dependency_job_id":"7a01f0b2-78ad-43c3-91a4-ac2221fb20e0","html_url":"https://github.com/KilianB/JImageHash","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KilianB%2FJImageHash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KilianB%2FJImageHash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KilianB%2FJImageHash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KilianB%2FJImageHash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KilianB","download_url":"https://codeload.github.com/KilianB/JImageHash/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247234921,"owners_count":20905854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fingerprinting","image","java","perceptive","perceptual-hashes","phash"],"created_at":"2024-11-11T15:18:56.072Z","updated_at":"2025-04-04T19:11:23.170Z","avatar_url":"https://github.com/KilianB.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg align=left src = \"https://user-images.githubusercontent.com/9025925/48595271-388ba280-e954-11e8-8bc6-8b8afe108682.png\" /\u003e\n\n# JImageHash\n\n[![Travis](https://travis-ci.org/KilianB/JImageHash.svg?branch=master)](https://travis-ci.org/KilianB/JImageHash)\n[![GitHub license](https://img.shields.io/github/license/KilianB/JImageHash.svg)](https://github.com/KilianB/JImageHash/blob/master/LICENSE)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/3c7db745b9ff4dd9b89484a6aa46ad2f)](https://www.codacy.com/app/KilianB/JImageHash?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=KilianB/JImageHash\u0026utm_campaign=Badge_Grade)\n\nJImageHash is a performant perceptual image fingerprinting library entirely written in Java. The library returns a similarity score aiming to identify entities which are likely modifications of the original source while being robust various attack vectors ie. color, rotation and scale transformation.\n\n\u003e A perceptual hash is a fingerprint of a multimedia file derived from various features from its content. Unlike cryptographic hash functions which rely on the avalanche effect of small changes in input leading to drastic changes in the output, perceptual hashes are \"close\" to one another if the features are similar.\n\nThis library was inspired by _Dr. Neal Krawetz_ blog post \"\u003ca href=\"http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html\"\u003ekind of like that\u003c/a\u003e\" and incorporates several improvements. A comprehensive overview of perceptual image hashing can be found in this \u003ca href=\"https://www.phash.org/docs/pubs/thesis_zauner.pdf\"\u003epaper\u003c/a\u003e by Christoph Zauner.\n\n\n\n## Maven\n\nThe project is hosted on maven central\n\n```XML\n\u003cdependency\u003e\n\t\u003cgroupId\u003edev.brachtendorf\u003c/groupId\u003e\n\t\u003cartifactId\u003eJImageHash\u003c/artifactId\u003e\n\t\u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003c!-- If you want to use the database image matcher you need to add h2 as well --\u003e\n\u003cdependency\u003e\n\t\u003cgroupId\u003ecom.h2database\u003c/groupId\u003e\n\t\u003cartifactId\u003eh2\u003c/artifactId\u003e\n\t\u003cversion\u003e1.4.200\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Breaking Changes: migration guide to version 1.0.0\n\n**Please be aware that migrating from one major version to another usually invalidates created hashes in order to retain validity when persistently storing the hashes.**\nThe algorithm id of hashes is adjusted in order for the jvm to throw an error if the possibility exist that hashes generated for the same input image are not consistent throughout the compared versions.\n\n Hashes generated with the following 2 algorithm have to be regenerated:\n\n- RotPAverage hash was fixed to correctly return hashes when the algorithm is used multiple times.\n- KernelAverageHash algorithm id changed due to JVMs internal hashcode calculation and the package name update. Hashes generated with this algorithm have to be regenerated.\n\nThe package is now published to maven central under a new group id. The internal package structure has been adjusted from `com.github.kilianB` to `dev.brachtendorf.jimagehash`. Adjust your imports accordingly.\n\n\n## Hello World\n\n```Java\nFile img0 = new File(\"path/to/file.png\");\nFile img1 = new File(\"path/to/secondFile.jpg\");\n\nHashingAlgorithm hasher = new PerceptiveHash(32);\n\nHash hash0 = hasher.hash(img0);\nHash hash1 = hasher.hash(img1);\n\ndouble similarityScore = hash0.normalizedHammingDistance(hash1);\n\nif(similarityScore \u003c .2) {\n    //Considered a duplicate in this particular case\n}\n\n//Chaining multiple matcher for single image comparison\n\nSingleImageMatcher matcher = new SingleImageMatcher();\nmatcher.addHashingAlgorithm(new AverageHash(64),.3);\nmatcher.addHashingAlgorithm(new PerceptiveHash(32),.2);\n\nif(matcher.checkSimilarity(img0,img1)) {\n    //Considered a duplicate in this particular case\n}\n```\n\n## Examples\n\nExamples and convenience methods can be found in the [examples repository](https://github.com/KilianB/JImageHash-Examples)\n\n## Transparent image support\n\nSupport for transparent images has to be enabled specifically due to backwards compatibility and force users of the libraries to understand the implication of this setting.\n\nThe `setOpaqueHandling(Color? replacementColor, int alphaThreshold)` will replace transparent pixels with the specified color before calculating the hash.\n\n### Be aware of the following culprits: \n\n- the replacement color must be consistent throughout hash calculation for the entire sample space to ensure robustness against color transformations of the images.\n- the replacement color should be a color that does not appear often in the input space to avoid masking out available information.\n- when not specified `Orange` will be used as replacement. This choice was arbitrary and ideally, a default color should be chosen which results in 0 and 1 bits being computed in 50% of the time in respect to all other pixels and hashing algorithms.\n- supplying a replacement value of null will attempt to either use black or white as a replacement color conflicting with the advice given above. Computing the contrast color will fail if the transparent area of an image covers a large space and comes with a steep performance penalty.\n\n```java\nHashingAlgorithm hasher = new PerceptiveHash(32);\n\n//Replace all pixels with alpha values smaller than 0-255. The alpha value cutoff is taken into account after down scaling the image, therefore choose a reasonable value.  \nint alphaThreshold = 253;\nhasher.setOpaqueHandling(alphaThreshold)\n\n```\n\n## Multiple types image matchers are available for each situation\n\nThe `persistent` package allows hashes and matchers to be saved to disk. In turn the images are not kept in memory and are only referenced by file path allowing to handle a great deal of images\nat the same time.\nThe `cached` version keeps the BufferedImage image objects in memory allowing to change hashing algorithms on the fly and a direct retrieval of the buffered image objects of matching images.\nThe `categorize` package contains image clustering matchers. KMeans and Categorical as well as weighted matchers.\nThe `exotic` package features BloomFilter, and the SingleImageMatcher used to match 2 images without any fancy additions.\n\n\u003ctable\u003e\n\u003ctr\u003e \u003cth\u003eImage\u003c/th\u003e  \u003cth\u003e\u003c/th\u003e \u003cth\u003eHigh\u003c/th\u003e \u003cth\u003eLow\u003c/th\u003e \u003cth\u003eCopyright\u003c/th\u003e \u003cth\u003eThumbnail\u003c/th\u003e \u003cth\u003eBallon\u003c/th\u003e \u003c/tr\u003e\n\n\u003ctr\u003e \u003ctd\u003eHigh Quality\u003c/td\u003e  \u003ctd\u003e\u003cimg width= 75% src=\"https://user-images.githubusercontent.com/9025925/36542413-046d8116-17e1-11e8-93ed-210f65293d51.jpg\"\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\u003c/tr\u003e \n\u003ctr\u003e \u003ctd\u003eLow Quality\u003c/td\u003e  \u003ctd\u003e\u003cimg width= 75% src=\"https://user-images.githubusercontent.com/9025925/36542414-0498079c-17e1-11e8-9224-a9852797b96f.jpg\"\u003e\u003c/td\u003e \n\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n \u003ctr\u003e \u003ctd\u003eAltered Copyright\u003c/td\u003e  \u003ctd\u003e\u003cimg width= 75% src=\"https://user-images.githubusercontent.com/9025925/36542411-0438eb36-17e1-11e8-9a59-2c69937560bf.jpg\"\u003e \u003c/td\u003e \n\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e \u003ctd\u003eThumbnail\u003c/td\u003e  \u003ctd\u003e\u003cimg src=\"https://user-images.githubusercontent.com/9025925/36542415-04ca8078-17e1-11e8-9be4-9a90b08c404b.jpg\"\u003e\u003c/td\u003e \n\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e\n\u003c/tr\u003e \n\t\n\u003ctr\u003e \u003ctd\u003eBallon\u003c/td\u003e  \u003ctd\u003e\u003cimg width= 75% src=\"https://user-images.githubusercontent.com/9025925/36542417-04f3e6a2-17e1-11e8-91b2-50f9961524b4.jpg\"\u003e\u003c/td\u003e \n\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/DC143C?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e \n\t\u003ctd\u003e\u003cp align=\"center\"\u003e\u003cimage src=\"https://via.placeholder.com/30/228B22?text=+\"/\u003e\u003c/p\u003e\u003c/td\u003e\n\u003c/tr\u003e \n\t\n\u003c/table\u003e\n\n## Hashing algorithm\n\nImage matchers can be configured using different algorithm. Each comes with individual properties\n\n\u003ctable\u003e\n  \u003ctr\u003e\u003cth\u003eAlgorithm\u003c/th\u003e  \u003cth\u003eFeature\u003c/th\u003e\u003cth\u003eNotes\u003c/th\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#averagehash-averagekernelhash-medianhash-averagecolorhash\"\u003eAverageHash\u003c/a\u003e\u003c/td\u003e  \u003ctd\u003eAverage Luminosity\u003c/td\u003e \u003ctd\u003eFast and good all purpose algorithm\u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#averagehash-averagekernelhash-medianhash-averagecolorhash\"\u003eAverageColorHash\u003c/a\u003e\u003c/td\u003e  \u003ctd\u003eAverage Color\u003c/td\u003e \u003ctd\u003eVersion 1.x.x AHash. Usually worse off than AverageHash. Not robust against color changes\u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#differencehash\"\u003eDifferenceHash\u003c/a\u003e\u003c/td\u003e \u003ctd\u003eGradient/Edge detection\u003c/td\u003e \u003ctd\u003eA bit more robust against hue/sat changes compared to AColorHash \u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003eWavelet Hash\u003c/td\u003e \u003ctd\u003eFrequency \u0026 Location\u003c/td\u003e \u003ctd\u003eFeature extracting by applying haar wavlets multiple times to the input image. Detection quality better than inbetween aHash and pHash.\u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#perceptive-hash\"\u003ePerceptiveHash\u003c/a\u003e\u003c/td\u003e \u003ctd\u003eFrequency\u003c/td\u003e \u003ctd\u003eHash based on Discrete Cosine Transformation. Smaller hash distribution but best accuracy / bitResolution.\u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#averagehash-averagekernelhash-medianhash-averagecolorhash\"\u003eMedianHash\u003c/a\u003e\u003c/td\u003e \u003ctd\u003eMedian Luminosity\u003c/td\u003e \u003ctd\u003eIdentical to AHash but takes the median value into account. A bit better to detect watermarks but worse at scale transformation\u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#averagehash-averagekernelhash-medianhash-averagecolorhash\"\u003eAverageKernelHash\u003c/a\u003e\u003c/td\u003e  \u003ctd\u003eAverage luminosity \u003c/td\u003e \u003ctd\u003eSame as AHash with kernel preprocessing. So far usually performs worse, but testing is not done yet.\u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd colspan=3 align=center\u003e\u003cb\u003eRotational Invariant\u003c/b\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#rotphash\"\u003eRotAverageHash\u003c/a\u003e\u003c/td\u003e  \u003ctd\u003eAverage Luminosity\u003c/td\u003e \u003ctd\u003eRotational robust version of AHash. Performs well but performance scales disastrous with higher bit resolutions . Conceptual issue: pixels further away from the center are weightend less.\u003c/td\u003e \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#rotphash\"\u003eRotPHash\u003c/a\u003e\u003c/td\u003e \u003ctd\u003eFrequency\u003c/td\u003e \u003ctd\u003e Rotational invariant version of pHash using ring partition to map pixels in a circular fashion. Lower complexity for high bit sizes but due to sorting pixel values usually maps to a lower normalized distance. Usually bit res of \u003e= 64bits are preferable\u003c/td\u003e \u003c/tr\u003e  \n   \u003ctr\u003e\u003ctd colspan=3 align=\"center\"\u003e\u003ci\u003e\u003cb\u003eExperimental.\u003c/b\u003e Hashes available but not well tuned and subject to changes\u003c/i\u003e\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\u003ctd\u003e\u003ca href=\"https://github.com/KilianB/JImageHash/wiki/Hashing-Algorithms#hoghash\"\u003eHogHash\u003c/a\u003e\u003c/td\u003e \u003ctd\u003eAngular Gradient based (detection of shapes?) \u003c/td\u003e \u003ctd\u003eA hashing algorithm based on hog feature detection which extracts gradients and pools them by angles. Usually used in support vector machine/NNs human outline detection. It's not entirely set how the feature vectors should be encoded. Currently average, but not great results, expensive to compute and requires a rather high bit resolution\u003c/td\u003e \u003c/tr\u003e  \n\u003c/table\u003e\n\n### Version 3.0.0 Image clustering\n\nImage clustering with fuzzy hashes allowing to represent hashes with probability bits instead of simple 0's and 1's\n\n![1_fxpw79yoon8xo3slqsvmta](https://user-images.githubusercontent.com/9025925/51272388-439d9600-19ca-11e9-8220-fe3539ed6061.png)\n\n### Algorithm benchmarking\n\nSee the wiki page on how to test different hashing algorithms with your set of images\nCode available at the example repo: https://github.com/KilianB/JImageHash-Examples/tree/main/src/main/java/com/github/kilianB/benchmark\n\n\u003cimg src=\"https://user-images.githubusercontent.com/9025925/49185669-c14a0b80-f362-11e8-92fa-d51a20476937.jpg\" /\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkilianb%2Fjimagehash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkilianb%2Fjimagehash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkilianb%2Fjimagehash/lists"}