Perception Toolkit

repository·main·Indexed 41 days ago

https://github.com/thorn-oss/perception

A toolkit for perceptual hashing research, development, and production use, designed for media deduplication and content identification. It supports various hashing algorithms including PHash, PDQ, DHash, AverageHash, Marr-Hildreth, Color Moment, Block Mean, and WaveletHash. The library includes comprehensive benchmarking tools for image and video datasets, transformation pipelines, and recall metric evaluation.

Tokens
7.5K
Snippets
21
Records
34
Agent score
52%

What's inside Perception

  1. Benchmark image hashing quality

    main

    You can evaluate the performance of different image hashers by applying various transformations (like blur, noise, or rotation) to a dataset and measuring how well the hashers maintain similarity. The process involves loading a dataset, applying transformations, computing hashes, and calculating recall metrics at a specific precision threshold.

    Key steps:

    1. Load Dataset: Use benchmarking.BenchmarkImageDataset.from_tuples to create a dataset from file paths and metadata.
    2. Transform Dataset: Use dataset.transform() to apply a dictionary of transformation functions. This can save the results to a storage_dir for later reloading.
    3. Reload Transformed Data: Use benchmarking.BenchmarkImageTransforms.load() to reload a previously computed transformed dataset from a directory or zip file.
    4. Compute Hashes: Use transformed.compute_hashes(hashers=...) where hashers is a dictionary mapping names to hasher instances.
    5. Calculate Metrics: Use hashes.compute_threshold_recall(precision_threshold=...) to get performance metrics like recall and precision.
    import os
    import glob
    from perception import benchmarking, hashers
    from perception.hashers.image.pdq import PDQHash
    
    # 1. Load dataset
    dataset = benchmarking.BenchmarkImageDataset.from_tuples(files=[
        (filepath, filepath.split(os.path.sep)[-2]) for filepath in glob.glob('path/to/images/*.jpg')
    ])
    
    # 2. Define transforms and apply them
    transforms = {'blur': some_blur_func, 'noop': lambda x: x}
    transformed = dataset.transform(transforms=transforms, storage_dir='transformed', errors="raise")
    
    # 3. (Optional) Reload later
    transformed = benchmarking.BenchmarkImageTransforms.load(path_to_zip_or_directory='transformed')
    
    # 4. Define hashers and compute
    hashers_dict = {'phash': hashers.PHash(hash_size=16), 'pdq': PDQHash()}
    hashes = transformed.compute_hashes(hashers=hashers_dict)
    
    # 5. Compute recall at 99.99% precision
    metrics = hashes.compute_threshold_recall(precision_threshold=99.99).reset_index()
  2. Benchmark video hashing quality

    main

    You can evaluate how well different hashers perform against various video transformations using the perception.benchmarking module. The process involves:

    1. Loading a dataset: Use BenchmarkVideoDataset.from_tuples to create a dataset from file paths and metadata (e.g., scene labels).
    2. Defining transformations: Create a dictionary of transformations using perception.benchmarking.video_transforms. A noop (no-operation) transform is required as the baseline.
    3. Applying transforms: Use dataset.transform(transforms=..., storage_dir=...) to generate the transformed video files. This can be reloaded later using BenchmarkVideoTransforms.load().
    4. Computing hashes: Define your hashers (e.g., using perception.hashers) and use transformed.compute_hashes(hashers=..., max_workers=...) to generate the hash data.
    5. Evaluating metrics: Load the computed hashes with BenchmarkHashes.load() and call compute_threshold_recall() to find the optimal thresholds for a target precision.
    import perception.benchmarking
    import perception.hashers
    
    # 1. Load dataset
    dataset = perception.benchmarking.BenchmarkVideoDataset.from_tuples(
        files=df[['filepath', 'scene']].itertuples(index=False)
    )
    
    # 2. Define transforms (noop is required)
    transforms = {
        'noop': perception.benchmarking.video_transforms.get_simple_transform(width=240, height=240),
        'shrink': perception.benchmarking.video_transforms.get_simple_transform(width='ceil(0.7*iw/2)*2', height='ceil(0.7*ih/2)*2')
    }
    
    # 3. Transform and save
    transformed = dataset.transform(transforms=transforms, storage_dir='benchmarking_videos')
    
    # 4. Compute hashes
    hashers = {
        'my_hasher': perception.hashers.PHashU8(hash_size=12)
    }
    hashes = transformed.compute_hashes(hashers=hashers, max_workers=5)
    hashes.save('hashes.csv')
    
    # 5. Evaluate
    benchmark_hashes = perception.benchmarking.BenchmarkHashes.load('hashes.csv')
    benchmark_hashes.compute_threshold_recall(precision_threshold=99.9, grouping=['transform_name'])
  3. Deduplicate videos using `FramewiseHasher`

    main

    Video deduplication is achieved by extracting representative frames from each video and comparing those frames across the dataset.

    1. Use hashers.FramewiseHasher to extract hashes from video frames.
    2. Configure frames_per_second to control sampling density.
    3. Use interframe_threshold and quality_threshold to ensure extracted frames are sufficiently different and high quality.
    4. Use hasher.compute_parallel to generate hashes for all files.
    5. Flatten the resulting hashes and use tools.deduplicate_hashes to find matches.
    import perception.hashers
    import perception.tools
    
    # 1. Setup the frame hasher
    frame_hasher = perception.hashers.PHash(hash_size=16)
    video_hasher = perception.hashers.FramewiseHasher(
        frames_per_second=1, 
        frame_hasher=frame_hasher, 
        interframe_threshold=50, 
        quality_threshold=90
    )
    
    # 2. Compute hashes for video files
    filepaths = ['video1.m4v', 'video2.gif']
    hashes = video_hasher.compute_parallel(filepaths=filepaths)
    
    # 3. Flatten hashes into (filepath, hash_string) tuples
    hashes_flattened = perception.tools.flatten([
        [(h['filepath'], h_str) for h_str in h['hash']] 
        for h in hashes
    ])
    
    # 4. Find duplicates across the flattened hashes
    duplicates = perception.tools.deduplicate_hashes(
        hashes=hashes_flattened,
        threshold=50,
        hasher=video_hasher
    )
  4. Deduplicate images with isometric transformations

    main

    To make deduplication more robust against rotations or flips, use the isometric=True flag in tools.deduplicate. This will compare not just the original images, but also their 8 isometric transformations.

    To improve performance on large datasets (like the Caltech 256 dataset), group files by category (e.g., by folder) and run deduplication within each group to avoid unnecessary pairwise comparisons between unrelated categories.

    from perception import hashers, tools
    
    # Grouping files by category (example logic)
    # current_filepaths = [list of files in one category]
    
    comparison_hashers = [(hashers.PHash(hash_size=16), 0.2)]
    
    duplicate_pairs = tools.deduplicate(
        files=current_filepaths,
        hashers=comparison_hashers,
        isometric=True
    )
  5. Install perception with benchmarking dependencies

    main

    If you need to perform performance comparisons or benchmarking, install the benchmarking extra. This includes the necessary dependencies for these tasks.

    # Install from PyPi with benchmarking support
    pip install perception[benchmarking]
    
    # Install from GitHub with benchmarking support
    pip install opencv-python git+https://github.com/thorn-oss/perception.git#egg=perception[benchmarking]
  6. Configure SaferMatcher authentication

    main

    The tools.SaferMatcher class supports different authentication methods depending on your Thorn Safer service subscription:

    1. API Key: Pass your key to the api_key parameter.
    2. Username/Password: In some cases, you may need to use a username and password instead of an API key. Refer to the full API documentation for the specific parameter names required for this method.
  7. Install perception with optional extras

    main

    The perception package provides optional extras for specific functionalities.

    Available Extras

    • approximate-deduplication: FAISS-based approximate-nearest-neighbor deduplication and graph community/clique detection (used by perception.approximate_deduplication and perception.local_descriptor_deduplication).
    • benchmarking: Tools for benchmarking perceptual hashes.
    • matching: Async matching utilities.
    • pdq: Facebook's PDQ hash support.

    Installation via uv

    If you use uv, extras are handled automatically:

    uv pip install "perception[benchmarking]"

    Installation via pip (Handling OpenCV Conflicts)

    Installing the benchmarking extra introduces a conflict because it depends on albumentations (which requires opencv-python-headless), while perception requires opencv-contrib-python-headless. To resolve this with plain pip, you must force-reinstall the contrib variant:

    pip install "perception[benchmarking]"
    pip install --force-reinstall --no-deps opencv-contrib-python-headless