saolaolsson / pixiple

Pixiple is a Windows application that searches your files for images that are similar in pixel and metadata content and presents you with a sorted list of similar image pairs to compare
MIT License
112 stars 16 forks source link

Identical images shows different pixel hash due to Image::calculate_hash() not accounting for pixel format differences #15

Open wmjdgla opened 1 year ago

wmjdgla commented 1 year ago

Image::calculate_hash() grabs the bitmap data directly without normalizing it to account for alpha channel: https://github.com/saolaolsson/pixiple/blob/62bcaccef0bfa4a3569b78bdc7195199b1ad713b/src/image.cpp#L724-L749

Thus when given 2 identical images, one without alpha channel and one with a redundant alpha channel (i.e. all alpha values are 0xFF), the computed pixel hash would be different. The image without the alpha channel should be normalized to a bitmap with alpha channel before computing the pixel hash (it may also be a good idea to indicate the presence of alpha channel in the UI).

As a hack fix, I replaced lines 739-748 with the following chunk from Image::load_pixels() that does conversion to RGBA: https://github.com/saolaolsson/pixiple/blob/62bcaccef0bfa4a3569b78bdc7195199b1ad713b/src/image.cpp#L432-L452

We can then simply do:

pixel_hash = Hash(pixel_buffer.data(), pixel_buffer_size);

This normalizes all images regardless of whether they have an alpha channel. I'm not sure whether this will adversely impact performance. If you want to normalize only those without alpha channel, add the following code (thanks to this SO question):

WICPixelFormatGUID pixelFormat{};
er = frame->GetPixelFormat(&pixelFormat);
ComPtr<IWICComponentInfo> imageInfo;
er = wic_factory->CreateComponentInfo(pixelFormat, &imageInfo);
ComPtr<IWICPixelFormatInfo> imageFormatInfo;
er = imageInfo->QueryInterface(__uuidof(IWICPixelFormatInfo), reinterpret_cast<void**>(&imageFormatInfo));
UINT bitsPerPixel{};
er = imageFormatInfo->GetBitsPerPixel(&bitsPerPixel);

if (bitsPerPixel == 24)
{
    // normalize
}
wmjdgla commented 1 year ago

Found that the issue is not just limited to alpha channels. Found an image format that uses a single channel and 8 bits per pixel. When compared against an identical images with 4 channels and 32 bits per pixel, the hash will inevitably be different. Seems like it'll just be simpler to always normalize before computing the hash.