PerceptualHash.NET 0.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package PerceptualHash.NET --version 0.2.0
                    
NuGet\Install-Package PerceptualHash.NET -Version 0.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PerceptualHash.NET" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PerceptualHash.NET" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="PerceptualHash.NET" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PerceptualHash.NET --version 0.2.0
                    
#r "nuget: PerceptualHash.NET, 0.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PerceptualHash.NET@0.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PerceptualHash.NET&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=PerceptualHash.NET&version=0.2.0
                    
Install as a Cake Tool

PerceptualHash.NET

CI NuGet Downloads .NET Tests License

PerceptualHash.NET is a cross-platform image hashing library for modern .NET, implemented in the NetImgHash namespace and built on SixLabors.ImageSharp.

Output is verified against the Python imagehash reference implementation by a golden dataset, so hashes are comparable across the two ecosystems.

Status

This repository currently ships the MVP surface:

  • AverageHash (aHash)
  • DifferenceHash (dHash)
  • ImageHash value type with Hamming distance and similarity helpers

PerceptualHash (pHash) and WaveletHash (wHash) are reserved in the public API for follow-up releases.

Target Frameworks

  • net8.0 — in support until November 2026
  • net10.0 — LTS
  • net11.0

net9.0 is not targeted: it went out of support in May 2026.

The frameworks are fixed in Directory.Build.props rather than derived from the installed SDK, so a local build produces the same set of targets as CI and as the published package. Building all three needs the .NET 8, 10, and 11 SDKs; to build a subset, pass -p:TargetFrameworks=net10.0.

Install

dotnet add package PerceptualHash.NET

One runtime dependency: SixLabors.ImageSharp. Nothing else.

Usage

using NetImgHash;

using var stream1 = File.OpenRead("image-original.jpg");
using var stream2 = File.OpenRead("image-edited.jpg");

var hash1 = ImageHasher.Compute(stream1, HashAlgorithm.DifferenceHash);
var hash2 = ImageHasher.Compute(stream2, HashAlgorithm.DifferenceHash);

var distance = hash1.HammingDistance(hash2);   // 0 = identical, 64 = every bit differs
var similarity = hash1.Similarity(hash2);      // 1.0 = identical, 0.0 = every bit differs

// Hashes round-trip through lowercase hex, so they can be stored and compared later.
var stored = hash1.ToString();                 // e.g. "a1b2c3d4e5f60718"
var restored = ImageHash.Parse(stored);

A Hamming distance of 0–5 on a 64-bit hash usually means the same image; above about 10 usually means a different one. Tune the threshold against your own data.

Compute throws NotSupportedException for HashAlgorithm.PerceptualHash and HashAlgorithm.WaveletHash, which are reserved but not yet implemented.

Algorithm Specifications

Average Hash

  1. Convert the image to grayscale.
  2. Resize to 8x8.
  3. Compute the mean luminance value.
  4. Set each bit when the pixel is greater than the mean.
  5. Return the 64-bit result as lowercase hexadecimal.

Difference Hash

  1. Convert the image to grayscale.
  2. Resize to 9x8.
  3. Compare each pixel to its neighbor on the left.
  4. Set each bit when the right-hand pixel is brighter.
  5. Return the 64-bit result as lowercase hexadecimal.

Compatibility Notes

  • The implementation is tuned to stay close to Python imagehash semantics for aHash and dHash.
  • The checked-in golden dataset under tests/NetImgHash.Tests/TestData is used to lock hash formatting, bit ordering, and representative image behavior.
  • EXIF auto-orientation is not part of the hashing pipeline for this MVP.

Test Data

The test project includes a golden dataset with representative images:

  • standard RGB image
  • grayscale image
  • high-resolution resize
  • thumbnail
  • EXIF-orientation JPEG
  • transparent PNG
  • JPEG quality variants
  • cropped version
  • rotated version

The expected results live in tests/NetImgHash.Tests/TestData/expected_hashes.json.

To regenerate the dataset locally with Python:

  1. Create a local virtual environment.
  2. Install pillow and imagehash.
  3. Regenerate the images and manifest.
  4. Run dotnet test.

Tests

60 tests, run against each of the three target frameworks — 180 executions.

dotnet test PerceptualHash.NET.sln          # all three frameworks
dotnet test PerceptualHash.NET.sln -f net10.0   # just one
Suite Tests What it covers
GoldenDatasetTests 20 Exact hash strings for a checked-in dataset, generated by Python imagehash. This is the correctness anchor: it locks bit ordering, hex formatting, and behaviour on greyscale, transparency, EXIF orientation, resizes, crops, rotations and JPEG quality variants.
ImageHashInvariantTests 31 The bit-length invariant, the uninitialized default, mismatched lengths, and hex round-tripping.
AlgorithmTests 4 aHash and dHash bit packing against hand-constructed images.
ImageHashTests 5 Distance, similarity, parsing, formatting.

CI runs the whole suite on Linux and Windows. The golden dataset asserts exact hash strings, so a platform decoding difference would surface there rather than in production.

Development

dotnet build PerceptualHash.NET.sln

CI builds and tests on Linux and Windows across all three frameworks. Releases are tag-driven: pushing a v*.*.* tag packs, checks the tag against the package version, and publishes to NuGet.

Releasing

Publishing uses NuGet Trusted Publishing — nuget.org exchanges a short-lived GitHub OIDC token for a one-hour API key, so no long-lived secret is stored.

One-time setup on nuget.org (Account → Trusted Publishing):

Field Value
Repository Owner aelena
Repository imagehash-net
Workflow File release.yml (file name only, no path)
Environment production (the workflow declares it; the two must match)
Glob Patterns and Packages PerceptualHash.NET

Create a GitHub environment named production in the repository, and add a secret NUGET_USER holding the nuget.org profile name (not an email address).

A policy is bound to one repository, so each repository needs its own.

To cut a release: set <Version> in NetImgHash.csproj, commit, then git tag v0.2.0 && git push origin v0.2.0.

Changelog

See CHANGELOG.md.

License

MIT — see LICENSE. No copyleft anywhere in the dependency graph.

On ImageSharp

The single runtime dependency, SixLabors.ImageSharp, is under the Six Labors Split License, which is Apache 2.0 or a commercial licence depending on how you consume it. It is worth being precise about, because the licence text is explicit and the answer is favourable:

Works are licensed to You under the Apache License, Version 2.0 if […] You are consuming the Work as a Transitive Package Dependency.

If you install PerceptualHash.NET, ImageSharp arrives indirectly through it — that is a transitive dependency by the licence's own definition, so you receive ImageSharp under Apache 2.0 regardless of your organisation's size or revenue.

The commercial-licence threshold (for-profit, over $1M USD annual gross revenue) applies to a direct dependency on ImageSharp. It does not reach consumers of this package. This library itself qualifies for Apache 2.0 on a separate clause anyway, being open source.

Not legal advice, but the clause is unambiguous and quoted above so you can check it yourself.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed.  net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.1 51 9/18/2026
0.4.0 68 9/16/2026
0.3.0 73 9/16/2026
0.2.0 94 9/1/2026