Irina
Irina

Reputation: 29

Why does aHash work better than dHash?

I have some problems with aHash and dHash : http://www.hackerfactor.com/blog/?/archives/529-Kind-of-Like-That.html

I used C++ and OpenCV

The problem is that dHash works worse than aHash. Although in theory, it should be better. For example: I chose a picture, did some manipulation with that and calculated the Hamming Distance between hashes of original and modificated images. The result is there

here is the function for aHash

__int64 calcImageHash(IplImage* src, bool show_results)
{
    if(!src){
        return 0;
    }
    IplImage *res=0, *gray=0, *bin =0;
    res = cvCreateImage( cvSize(8, 8), src->depth, src->nChannels);
    gray = cvCreateImage( cvSize(8, 8), IPL_DEPTH_8U, 1);
    bin = cvCreateImage( cvSize(8, 8), IPL_DEPTH_8U, 1);
    cvResize(src, res);
    cvCvtColor(res, gray, CV_BGR2GRAY);
    CvScalar average = cvAvg(gray);
    printf("[i] average: %.2f \n", average.val[0]);
    cvThreshold(gray, bin, average.val[0], 255, CV_THRESH_BINARY);
    __int64 hash = 0;
    int i=0;
    for( int y=0; y<bin->height; y++ ) {
        uchar* ptr = (uchar*) (bin->imageData + y * bin->widthStep);
        for( int x=0; x<bin->width; x++ ) {
            if(ptr[x]){
                hash |= (__int64)1<<i;
            }
            i++;
        }
    }
    printf("[i] hash: %I64X \n", hash);
    cvReleaseImage(&res);
    cvReleaseImage(&gray);
    cvReleaseImage(&bin);
    return hash;
}

and function for dHash

__int64 calcImageHash(IplImage* src, bool show_results)
{
    if(!src)
    {
        return 0;
    }

    IplImage *res=0, *gray=0;
    res = cvCreateImage( cvSize(9,8), src->depth, src->nChannels);
    gray = cvCreateImage( cvSize(9,8), IPL_DEPTH_8U, 1);
    cvResize(src, res);
    cvCvtColor(res, gray, COLOR_BGR2GRAY);
    __int64 hash = 0;
    int i=0;
    cout<<gray->height;
    for( int y=0; y<gray->height; y++ ) {
        uchar* ptr = (uchar*) (gray->imageData + y* gray->widthStep);
        for( int x=0; x<gray->width-1; x++ ) {
            if( ptr[x+1] > ptr[x] ){
                hash |= (__int64)1<<i;
            }
            i++;
        }
    }
    printf("[i] hash: %I64X \n", hash, "\n");
    std::cout<<endl;
    cvReleaseImage(&res);
    cvReleaseImage(&gray);
    return hash;
}

Upvotes: 2

Views: 1341

Answers (1)

Matthias Winkelmann
Matthias Winkelmann

Reputation: 16394

Just in case someone comes across this as well, I have noticed that dhash has a specific weakness with illustrations and some artwork.

More specifically, it tends toward randomness for any image that has very low contrast, or that contains large areas of solid colors. I am working with a collection of photos of art, and the problem is especially pronounced for sketches, line drawing, or where the artwork itself is framed by solid-colored passepartout.

It's somewhat obvious why this happens: solid areas are liable to randomly see one segment be slightly brighter than an adjacent one with even minor artifacts of lossy compression. But I am somewhat surprised by the magnitude of it: simply scaling an image by 50% will often result in an image that is indistinguishable to the naked eye, but gives dhash differences close to randomness.

I have seen some improvements by making the comparison not about >= vs <, but to add a somewhat arbitrary bias, requiring a >= (b + 15), for example. The reasoning here is that solid-coloured are relatively abundant, but linear gradients (which would result in the same sort of pathology again) are relatively rare. If that's still a problem, one could also make the bias random but location-dependent, i. e. "add 12 at (x=3, y=5), substract 3 at x=20, y=5). The probability of significant number of images having matching patterns would be far lower than it is for solid colors and well-prescribed gradients.

Upvotes: 2

Related Questions