1. #
  2. # Cython wrapper code for pHash
  3. #
  4. # By Mostafa Razavi
  5. #
  6.  
  7. # import malloc, free, etc.
  8. from stdlib cimport *
  9.  
  10. cdef extern from "pHash.h":
  11. ctypedef unsigned long long ulong64
  12. cdef struct ph_hashtype:
  13. char* id
  14. void* hash
  15. float* path
  16. int hash_length
  17. short int hash_type
  18. ctypedef ph_hashtype DP
  19.  
  20. int ph_dct_imagehash(char* file, ulong64 hash)
  21. ulong64* ph_dct_videohash(char* filename, int Length)
  22. int ph_hamming_distance(ulong64 hasha, ulong64 hashb)
  23. DP** ph_dct_image_hashes(char* files[], int count, int threads)
  24. DP** ph_dct_video_hashes(char* files[], int count, int threads)
  25.  
  26. class Hash(object):
  27. def __init__(self, id, hash):
  28. self.id = id
  29. if type(hash) == list:
  30. self.hash = hash
  31. else:
  32. self.hash = [hash]
  33.  
  34. def __repr__(self):
  35. return "<Hash('{0}')={1}>".format(self.id, self.hash[0] if len(self.hash) == 1 else self.hash)
  36.  
  37. class PHashException(Exception):
  38. pass
  39.  
  40. def imageHash(file):
  41. cdef ulong64 hash
  42. ret = ph_dct_imagehash(file, hash)
  43. if ret == -1:
  44. raise PHashException()
  45.  
  46. return hash
  47.  
  48. def videoHash(file):
  49. cdef ulong64* hash
  50. cdef int length
  51. cdef int i
  52.  
  53. hash = ph_dct_videohash(file, length)
  54. if hash == NULL:
  55. raise PHashException()
  56.  
  57. ret = []
  58. for i in xrange(0, length):
  59. ret.append(hash[i])
  60.  
  61. free(hash)
  62.  
  63. return ret
  64.  
  65. def hammingDistance(ulong64 hash1, ulong64 hash2):
  66. return ph_hamming_distance(hash1, hash2)
  67.  
  68. def imageHashes(files, threads):
  69. if threads < 1 or threads > len(files):
  70. threads = len(files)
  71.  
  72. # first convert files into a C array
  73. cdef char** cfiles = <char**> malloc(len(files) * sizeof(char*))
  74. cdef int i
  75.  
  76. for i in xrange(0, len(files)):
  77. cfiles[i] = files[i]
  78.  
  79. # now call the wrapped function and convert its return values to Python objects
  80. cdef DP** ret = ph_dct_image_hashes(cfiles, len(files), threads)
  81.  
  82. hashes = []
  83. for i in xrange(0, len(files)):
  84. # [0] dereferences ret[i].hash which is a pointer
  85. hashes.append(Hash(ret[i].id, (<ulong64*> ret[i].hash)[0]))
  86.  
  87. # free allocated array and the returned values
  88. free(cfiles)
  89. for i in xrange(0, len(files)):
  90. free(ret[i].id)
  91. free(ret[i].hash)
  92. free(ret[i])
  93. free(ret)
  94.  
  95. return hashes
  96.  
  97. def videoHashes(files, threads):
  98. if threads < 1 or threads > len(files):
  99. threads = len(files)
  100.  
  101. # first convert files into a C array
  102. cdef char** cfiles = <char**> malloc(len(files) * sizeof(char*))
  103. cdef int i
  104. cdef int j
  105.  
  106. for i in xrange(0, len(files)):
  107. cfiles[i] = files[i]
  108.  
  109. # now call the wrapped function and convert its return values to Python objects
  110. cdef DP** ret = ph_dct_video_hashes(cfiles, len(files), threads)
  111.  
  112. hashes = []
  113. for i in xrange(0, len(files)):
  114. h = []
  115. for j in xrange(0, ret[i].hash_length):
  116. h.append((<ulong64*> ret[i].hash)[j])
  117. hashes.append(Hash(ret[i].id, h))
  118.  
  119. # free allocated array and the returned values
  120. free(cfiles)
  121. for i in xrange(0, len(files)):
  122. free(ret[i].id)
  123. free(ret[i].hash)
  124. free(ret[i])
  125. free(ret)
  126.  
  127. return hashes