mirror of
https://github.com/arsenetar/dupeguru.git
synced 2024-11-16 20:29:02 +00:00
use rotation as option
This commit is contained in:
parent
b00f0bf4f1
commit
e12fd54928
@ -118,7 +118,7 @@ def get_match(first, second, percentage):
|
|||||||
return Match(first, second, percentage)
|
return Match(first, second, percentage)
|
||||||
|
|
||||||
|
|
||||||
def async_compare(ref_ids, other_ids, dbname, threshold, picinfo):
|
def async_compare(ref_ids, other_ids, dbname, threshold, picinfo, match_rotated=False):
|
||||||
# The list of ids in ref_ids have to be compared to the list of ids in other_ids. other_ids
|
# The list of ids in ref_ids have to be compared to the list of ids in other_ids. other_ids
|
||||||
# can be None. In this case, ref_ids has to be compared with itself
|
# can be None. In this case, ref_ids has to be compared with itself
|
||||||
# picinfo is a dictionary {pic_id: (dimensions, is_ref)}
|
# picinfo is a dictionary {pic_id: (dimensions, is_ref)}
|
||||||
@ -136,27 +136,33 @@ def async_compare(ref_ids, other_ids, dbname, threshold, picinfo):
|
|||||||
other_dimensions, other_is_ref = picinfo[other_id]
|
other_dimensions, other_is_ref = picinfo[other_id]
|
||||||
if ref_is_ref and other_is_ref:
|
if ref_is_ref and other_is_ref:
|
||||||
continue
|
continue
|
||||||
rotated_ref_dimensions = (ref_dimensions[1], ref_dimensions[0])
|
if ref_dimensions != other_dimensions:
|
||||||
if ref_dimensions != other_dimensions and rotated_ref_dimensions != other_dimensions:
|
if match_rotated:
|
||||||
continue
|
rotated_ref_dimensions = (ref_dimensions[1], ref_dimensions[0])
|
||||||
for orientation_ref in range(8):
|
if rotated_ref_dimensions != other_dimensions:
|
||||||
for orientation_other in range(8):
|
continue
|
||||||
try:
|
|
||||||
diff = avgdiff(ref_blocks[orientation_ref], other_blocks[orientation_other], limit, MIN_ITERATIONS)
|
|
||||||
percentage = 100 - diff
|
|
||||||
except (DifferentBlockCountError, NoBlocksError):
|
|
||||||
percentage = 0
|
|
||||||
if percentage >= threshold:
|
|
||||||
results.append((ref_id, other_id, percentage))
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
break
|
|
||||||
|
orientation_range = 1
|
||||||
|
if match_rotated:
|
||||||
|
orientation_range = 8
|
||||||
|
|
||||||
|
for orientation_ref in range(orientation_range):
|
||||||
|
try:
|
||||||
|
diff = avgdiff(ref_blocks[orientation_ref], other_blocks[0], limit, MIN_ITERATIONS)
|
||||||
|
percentage = 100 - diff
|
||||||
|
except (DifferentBlockCountError, NoBlocksError):
|
||||||
|
percentage = 0
|
||||||
|
if percentage >= threshold:
|
||||||
|
results.append((ref_id, other_id, percentage))
|
||||||
|
break
|
||||||
|
|
||||||
cache.close()
|
cache.close()
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def getmatches(pictures, cache_path, threshold, match_scaled=False, j=job.nulljob):
|
def getmatches(pictures, cache_path, threshold, match_scaled=False, match_rotated=False, j=job.nulljob):
|
||||||
def get_picinfo(p):
|
def get_picinfo(p):
|
||||||
if match_scaled:
|
if match_scaled:
|
||||||
return ((None, None), p.is_ref)
|
return ((None, None), p.is_ref)
|
||||||
@ -211,7 +217,7 @@ def getmatches(pictures, cache_path, threshold, match_scaled=False, j=job.nulljo
|
|||||||
picinfo.update({p.cache_id: get_picinfo(p) for p in other_chunk})
|
picinfo.update({p.cache_id: get_picinfo(p) for p in other_chunk})
|
||||||
else:
|
else:
|
||||||
other_ids = None
|
other_ids = None
|
||||||
args = (ref_ids, other_ids, cache_path, threshold, picinfo)
|
args = (ref_ids, other_ids, cache_path, threshold, picinfo, match_rotated)
|
||||||
async_results.append(pool.apply_async(async_compare, args))
|
async_results.append(pool.apply_async(async_compare, args))
|
||||||
collect_results()
|
collect_results()
|
||||||
collect_results(collect_all=True)
|
collect_results(collect_all=True)
|
||||||
|
@ -14,6 +14,7 @@ from core.pe import matchblock, matchexif
|
|||||||
class ScannerPE(Scanner):
|
class ScannerPE(Scanner):
|
||||||
cache_path = None
|
cache_path = None
|
||||||
match_scaled = False
|
match_scaled = False
|
||||||
|
match_rotated = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_scan_options():
|
def get_scan_options():
|
||||||
@ -29,6 +30,7 @@ class ScannerPE(Scanner):
|
|||||||
cache_path=self.cache_path,
|
cache_path=self.cache_path,
|
||||||
threshold=self.min_match_percentage,
|
threshold=self.min_match_percentage,
|
||||||
match_scaled=self.match_scaled,
|
match_scaled=self.match_scaled,
|
||||||
|
match_rotated=self.match_rotated,
|
||||||
j=j,
|
j=j,
|
||||||
)
|
)
|
||||||
elif self.scan_type == ScanType.EXIFTIMESTAMP:
|
elif self.scan_type == ScanType.EXIFTIMESTAMP:
|
||||||
|
@ -14,6 +14,10 @@ Preferences
|
|||||||
If you check this box, pictures of different dimensions will be allowed in the same
|
If you check this box, pictures of different dimensions will be allowed in the same
|
||||||
duplicate group.
|
duplicate group.
|
||||||
|
|
||||||
|
**Match pictures of different rotations:**
|
||||||
|
If you check this box, pictures of different rotations will be allowed in the same
|
||||||
|
duplicate group.
|
||||||
|
|
||||||
.. _filter-hardness:
|
.. _filter-hardness:
|
||||||
|
|
||||||
**Filter Hardness:**
|
**Filter Hardness:**
|
||||||
|
@ -307,6 +307,10 @@ msgstr "Debug mode (restart required)"
|
|||||||
msgid "Match pictures of different dimensions"
|
msgid "Match pictures of different dimensions"
|
||||||
msgstr "Match pictures of different dimensions"
|
msgstr "Match pictures of different dimensions"
|
||||||
|
|
||||||
|
#: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0
|
||||||
|
msgid "Match pictures of different rotations"
|
||||||
|
msgstr "Match pictures of different rotations"
|
||||||
|
|
||||||
#: qt/preferences_dialog.py:43
|
#: qt/preferences_dialog.py:43
|
||||||
msgid "Filter Hardness:"
|
msgid "Filter Hardness:"
|
||||||
msgstr "Filter Hardness:"
|
msgstr "Filter Hardness:"
|
||||||
|
@ -316,6 +316,10 @@ msgstr "Mode de depuración (se requiere reinicio)"
|
|||||||
msgid "Match pictures of different dimensions"
|
msgid "Match pictures of different dimensions"
|
||||||
msgstr "Coincidencia de imágenes de distintas dimensiones"
|
msgstr "Coincidencia de imágenes de distintas dimensiones"
|
||||||
|
|
||||||
|
#: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0
|
||||||
|
msgid "Match pictures of different rotations"
|
||||||
|
msgstr "Coincidencia de imágenes de distintas rotaciones"
|
||||||
|
|
||||||
#: qt/preferences_dialog.py:43
|
#: qt/preferences_dialog.py:43
|
||||||
msgid "Filter Hardness:"
|
msgid "Filter Hardness:"
|
||||||
msgstr "Dureza del Filtro:"
|
msgstr "Dureza del Filtro:"
|
||||||
|
@ -314,6 +314,10 @@ msgstr "Modo de Depuração (requer reinício)"
|
|||||||
msgid "Match pictures of different dimensions"
|
msgid "Match pictures of different dimensions"
|
||||||
msgstr "Coincidir fotos de dimensões diferentes"
|
msgstr "Coincidir fotos de dimensões diferentes"
|
||||||
|
|
||||||
|
#: qt/pe/preferences_dialog.py:19 cocoa/en.lproj/Localizable.strings:0
|
||||||
|
msgid "Match pictures of different rotations"
|
||||||
|
msgstr "Coincidir fotos de rotações diferentes"
|
||||||
|
|
||||||
#: qt/preferences_dialog.py:43
|
#: qt/preferences_dialog.py:43
|
||||||
msgid "Filter Hardness:"
|
msgid "Filter Hardness:"
|
||||||
msgstr "Pressão do Filtro:"
|
msgstr "Pressão do Filtro:"
|
||||||
|
@ -192,6 +192,7 @@ class DupeGuru(QObject):
|
|||||||
scanned_tags.add("year")
|
scanned_tags.add("year")
|
||||||
self.model.options["scanned_tags"] = scanned_tags
|
self.model.options["scanned_tags"] = scanned_tags
|
||||||
self.model.options["match_scaled"] = self.prefs.match_scaled
|
self.model.options["match_scaled"] = self.prefs.match_scaled
|
||||||
|
self.model.options["match_rotated"] = self.prefs.match_rotated
|
||||||
self.model.options["include_exists_check"] = self.prefs.include_exists_check
|
self.model.options["include_exists_check"] = self.prefs.include_exists_check
|
||||||
self.model.options["rehash_ignore_mtime"] = self.prefs.rehash_ignore_mtime
|
self.model.options["rehash_ignore_mtime"] = self.prefs.rehash_ignore_mtime
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ class PreferencesDialog(PreferencesDialogBase):
|
|||||||
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
|
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
|
||||||
self._setupAddCheckbox("matchScaledBox", tr("Match pictures of different dimensions"))
|
self._setupAddCheckbox("matchScaledBox", tr("Match pictures of different dimensions"))
|
||||||
self.widgetsVLayout.addWidget(self.matchScaledBox)
|
self.widgetsVLayout.addWidget(self.matchScaledBox)
|
||||||
|
self._setupAddCheckbox("matchRotatedBox", tr("Match pictures of different rotations"))
|
||||||
|
self.widgetsVLayout.addWidget(self.matchRotatedBox)
|
||||||
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"))
|
self._setupAddCheckbox("mixFileKindBox", tr("Can mix file kind"))
|
||||||
self.widgetsVLayout.addWidget(self.mixFileKindBox)
|
self.widgetsVLayout.addWidget(self.mixFileKindBox)
|
||||||
self._setupAddCheckbox("useRegexpBox", tr("Use regular expressions when filtering"))
|
self._setupAddCheckbox("useRegexpBox", tr("Use regular expressions when filtering"))
|
||||||
@ -57,6 +59,7 @@ show scrollbars to span the view around"
|
|||||||
|
|
||||||
def _load(self, prefs, setchecked, section):
|
def _load(self, prefs, setchecked, section):
|
||||||
setchecked(self.matchScaledBox, prefs.match_scaled)
|
setchecked(self.matchScaledBox, prefs.match_scaled)
|
||||||
|
setchecked(self.matchRotatedBox, prefs.match_rotated)
|
||||||
|
|
||||||
# Update UI state based on selected scan type
|
# Update UI state based on selected scan type
|
||||||
scan_type = prefs.get_scan_type(AppMode.PICTURE)
|
scan_type = prefs.get_scan_type(AppMode.PICTURE)
|
||||||
@ -67,5 +70,6 @@ show scrollbars to span the view around"
|
|||||||
|
|
||||||
def _save(self, prefs, ischecked):
|
def _save(self, prefs, ischecked):
|
||||||
prefs.match_scaled = ischecked(self.matchScaledBox)
|
prefs.match_scaled = ischecked(self.matchScaledBox)
|
||||||
|
prefs.match_rotated = ischecked(self.matchRotatedBox)
|
||||||
prefs.details_dialog_override_theme_icons = ischecked(self.details_dialog_override_theme_icons)
|
prefs.details_dialog_override_theme_icons = ischecked(self.details_dialog_override_theme_icons)
|
||||||
prefs.details_dialog_viewers_show_scrollbars = ischecked(self.details_dialog_viewers_show_scrollbars)
|
prefs.details_dialog_viewers_show_scrollbars = ischecked(self.details_dialog_viewers_show_scrollbars)
|
||||||
|
@ -225,6 +225,7 @@ class Preferences(PreferencesBase):
|
|||||||
self.scan_tag_genre = get("ScanTagGenre", self.scan_tag_genre)
|
self.scan_tag_genre = get("ScanTagGenre", self.scan_tag_genre)
|
||||||
self.scan_tag_year = get("ScanTagYear", self.scan_tag_year)
|
self.scan_tag_year = get("ScanTagYear", self.scan_tag_year)
|
||||||
self.match_scaled = get("MatchScaled", self.match_scaled)
|
self.match_scaled = get("MatchScaled", self.match_scaled)
|
||||||
|
self.match_rotated = get("MatchRotated", self.match_rotated)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.filter_hardness = 95
|
self.filter_hardness = 95
|
||||||
@ -277,6 +278,7 @@ class Preferences(PreferencesBase):
|
|||||||
self.scan_tag_genre = False
|
self.scan_tag_genre = False
|
||||||
self.scan_tag_year = False
|
self.scan_tag_year = False
|
||||||
self.match_scaled = False
|
self.match_scaled = False
|
||||||
|
self.match_rotated = False
|
||||||
|
|
||||||
def _save_values(self, settings):
|
def _save_values(self, settings):
|
||||||
set_ = self.set_value
|
set_ = self.set_value
|
||||||
@ -330,6 +332,7 @@ class Preferences(PreferencesBase):
|
|||||||
set_("ScanTagGenre", self.scan_tag_genre)
|
set_("ScanTagGenre", self.scan_tag_genre)
|
||||||
set_("ScanTagYear", self.scan_tag_year)
|
set_("ScanTagYear", self.scan_tag_year)
|
||||||
set_("MatchScaled", self.match_scaled)
|
set_("MatchScaled", self.match_scaled)
|
||||||
|
set_("MatchRotated", self.match_rotated)
|
||||||
|
|
||||||
# scan_type is special because we save it immediately when we set it.
|
# scan_type is special because we save it immediately when we set it.
|
||||||
def get_scan_type(self, app_mode):
|
def get_scan_type(self, app_mode):
|
||||||
|
Loading…
Reference in New Issue
Block a user