-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgui.py
343 lines (251 loc) · 9.71 KB
/
gui.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
import sys
from PySide import QtGui
from PySide.QtCore import *
from PIL import Image
from PIL import ExifTags
import PIL
import os
import shutil
import hashlib
import time
from PySide.QtGui import QMessageBox
class Window(QtGui.QWidget):
root_folder = None
dest_folder = None
def __init__(self):
super(Window, self).__init__()
self.init_ui()
def init_ui(self):
vbox = QtGui.QVBoxLayout()
#Create label for root folder selection
root_folder_description = QtGui.QHBoxLayout()
description_label = QtGui.QLabel("Select folder to start from:")
description_label.setFont(QtGui.QFont('SansSerif', 14))
root_folder_description.addWidget(description_label)
#Create textbox and button to select root folder
folder_selection = QtGui.QHBoxLayout()
self.choose_root_btn = self.button("Browse...")
root_folder_txt = QtGui.QLineEdit()
folder_selection.addWidget(root_folder_txt)
folder_selection.addWidget(self.choose_root_btn)
#Add all components to the overlying vbox.
vbox.addLayout(root_folder_description)
vbox.addLayout(folder_selection)
#############################################################################
self.progress_bar = QtGui.QProgressBar()
self.progress_bar.setTextVisible(True)
vbox.addWidget(self.progress_bar)
####################################################################
vbox.addStretch(1)
#Open window
self.resize(450,450)
self.center()
self.setLayout(vbox)
self.setWindowTitle('ImageSorter')
####################################################################
#TEST HERE:
self.connect(self.choose_root_btn, SIGNAL("clicked()"), self.process_data)
self.image_sorter = ImageSorter(self,
root_folder="/Users/Morten/Desktop/Test",
dest_folder="/Users/Morten/Desktop/",
min_width = 500,
min_height = 500,
allowed_types=["jpg"] )
self.connect(self.image_sorter, SIGNAL("count_completed(qint32)"), self.done_counting, Qt.DirectConnection)
self.connect(self.image_sorter, SIGNAL("update_progress(qint16)"), self.update_progress_bar, Qt.DirectConnection)
self.connect(self.image_sorter, SIGNAL("finished()"), self.sorting_done, Qt.DirectConnection)
#####################################################################
self.show()
def button(self, text):
button = QtGui.QPushButton(text)
button.setFont(QtGui.QFont('SansSerif', 12))
return button
def center(self):
qr = self.frameGeometry()
cp = QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def process_data(self):
self.image_sorter.start()
print "started"
def done_counting(self, message):
print message
def update_progress_bar(self, percent):
self.progress_bar.setValue(percent)
def sorting_done(self):
print "DONE!"
#Overrides normal close event. Makes sure you don't mistakenly close while running.
def closeEvent(self, event):
if self.image_sorter.isRunning():
event.ignore()
else:
event.accept()
class ImageSorter(QThread):
#Folder to start search from:
root_folder = None
#Folder to place Photos folder:
dest_folder = None
#Picture minimum size:
min_width = 0
min_height = 0
#Prepare allowed types
allowed_types = None
#Prepare hash list for detecting duplicates:
hash_dict = {}
#Prepare camera dict
cameras = {}
#Duplicate count
duplicates = 0
gui = None
def __init__(self, parent=None, *args, **kw):
super(ImageSorter, self).__init__(parent)
self.set_variables(*args, **kw)
def set_variables(self, root_folder, dest_folder, min_width, min_height, allowed_types):
self.root_folder = root_folder
self.dest_folder = dest_folder
self.min_width = min_width
self.min_height = min_height
self.allowed_types = allowed_types
def set_dir(self):
try:
os.chdir(self.root_folder)
except OSError as e:
print e
def get_exif(self, img):
try:
exif = {
PIL.ExifTags.TAGS[k]: v
for k, v in img._getexif().items()
if k in PIL.ExifTags.TAGS
}
except:
exif = {"Model":"No Exif"}
return exif
def get_year_month(self, exif):
exif_split = exif['DateTime'].split(":")
return [exif_split[0], exif_split[1]]
def check_camera_type(self, exif):
try:
model = exif["Model"]
except:
model = "None"
try:
make = exif["Make"]
except:
make = "None"
try:
lens_model = exif["LensModel"]
except:
lens_model = "None"
if not self.cameras.has_key(model):
self.cameras[model] = (make, lens_model)
return model
def check_size(self, img):
try:
width = img.size[0]
height = img.size[1]
if width > self.min_width and height > self.min_height:
return True
else:
return False
except:
#TODO: Should raise an exception here.
None
def check_duplicates(self, image):
#TODO: Do these need to be specified global?
#TODO: should it really only read 10000?
img = open(image).read(10000)
hash = hashlib.md5(img).hexdigest()
if self.hash_dict.has_key(hash):
self.duplicates += 1
return False
else:
self.hash_dict[hash] = True
return True
def make_dir(self, folder_path):
#Make directories to put images in
try:
os.makedirs(folder_path)
except OSError:
None
def move_image(self, image_path, camera_type, count, exif):
#Get year and month from exif
date = self.get_year_month(exif)
year = date[0]
month = date[1]
#Make path from year and month
path = ''.join([self.dest_folder , "Photos/" , camera_type , "/" , str(year) , "/" , str(month)])
#Make folders in path - returns none if already exists
self.make_dir(path)
#Move image to related path
try:
shutil.copyfile(image_path, path + "/img_" + str(count) + ".jpg")
except:
print "Error moving image", image_path
def handle_error(self, image_path, count):
#Set path to error folder
path = self.dest_folder + "Photos/Errors"
#Make error folder.
self.make_dir(path)
#Move image to related path
try:
shutil.copyfile(image_path, path + "/img_" + str(count) + ".jpg")
except:
print "Error moving image", image_path
def is_allowed_filetype(self, filename):
filetype = filename.lower().split('.')
return (filetype[len(filetype) - 1] in self.allowed_types)
def run(self):
print "Running on folder:", self.root_folder
start = time.time()
self.set_dir()
count = 0
total_count = 0
error_count = 0
file_count=0
#Count all images.
print "Counting all files in directory..."
for (dirname, dirs, files) in os.walk('.'):
for filename in files:
file_count+=1
self.emit(SIGNAL("count_completed(qint32)"), file_count)
#Remember total count.
total_count = file_count
update_frequency = total_count / 25
print "Sorting initiated:"
for (dirname, dirs, files) in os.walk('.'):
for filename in files:
#Calculation for percent finished.
file_count -= 1
if file_count%update_frequency == 0:
percent = (((total_count-file_count)*100)/total_count)
print percent, "% finished"
self.emit(SIGNAL("update_progress(qint16)"), percent)
if self.is_allowed_filetype(filename):
tmp_path = os.path.join(dirname, filename)
try:
img = Image.open(tmp_path)
exif = self.get_exif(img)
if self.check_size(img) and self.check_duplicates(tmp_path):
camera_type = self.check_camera_type(exif)
#################################################################################################################
time.sleep(0.02)
#################################################################################################################
try:
self.move_image(tmp_path, camera_type, count, exif)
count += 1
except:
#print tmp_path, "raised error"
self.handle_error(tmp_path, error_count)
error_count += 1
except:
x=1
print count, "images were found and copied to the right directory"
print error_count, "errors were raised and moved to the errors folder"
print self.duplicates, "duplicates were found"
print "Exuction time", time.time() - start
def main():
app = QtGui.QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
main()