00001
00002
00003 import Tkinter
00004 import roslib
00005 roslib.load_manifest('face_contour_detector')
00006 import rospy
00007 import cv
00008 import cv_bridge
00009 import Image
00010 import ImageTk
00011 import tkMessageBox
00012 from face_contour_detector.srv import *
00013 from face_contour_detector.msg import *
00014 import face_contour_detector.image_information.sql_database as sql_database
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 BLUR_WIDTH_MIN = 1
00026
00027 BLUR_WIDTH_MAX = 50
00028
00029
00030 BLUR_HEIGHT_MIN = 1
00031
00032 BLUR_HEIGHT_MAX = 50
00033
00034
00035 CANNY_THRESHOLD1_MIN = 1
00036
00037 CANNY_THRESHOLD1_MAX = 30000
00038
00039
00040 CANNY_THRESHOLD2_MIN = 1
00041
00042 CANNY_THRESHOLD2_MAX = 30000
00043
00044
00045 SEARCH_RADIUS_MIN = 1
00046
00047 SEARCH_RADIUS_MAX = 20
00048
00049
00050 PIXEL_NUM_MIN = 1
00051
00052 PIXEL_NUM_MAX = 20
00053
00054
00055 SLIDER_LENGTH = 200
00056
00057
00058 current_image = None
00059
00060
00061
00062
00063
00064
00065 class JumpScale(Tkinter.Scale):
00066
00067
00068
00069
00070 def __init__(self, master=None, **options):
00071 Tkinter.Scale.__init__(self, master, options)
00072 dif = self["to"] - self["from"]
00073 self.factor = dif / self["length"]
00074 self.bind("<1>", self._jump)
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 def _jump(self, evt):
00086 x, y = evt.x, evt.y
00087 _id = self.identify(x, y)
00088 if _id == "trough1" or _id == "trough2":
00089 self.set(self.factor * x + self["from"])
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128 class ImageSettingsFrame(Tkinter.Frame):
00129
00130
00131
00132
00133 def __init__(self, master, filter_settings):
00134
00135 Tkinter.Frame.__init__(self, master)
00136
00137 self.master.title("Filter Settings")
00138 self.bridge = bridge = cv_bridge.CvBridge()
00139
00140 self.original_image = filter_settings.image
00141
00142 self.orignal_image_as_cvM = bridge.imgmsg_to_cv(self.original_image)
00143
00144 self.areas = areas = {}
00145 for area in filter_settings.areas:
00146 areas[area.area.name] = area
00147
00148 self.list_areas = filter_settings.areas
00149
00150
00151 area_frame = Tkinter.LabelFrame(self, text="Image Area")
00152 self.area_selection = area_selection = Tkinter.StringVar(master=self)
00153 area_selection.set(filter_settings.areas[0].area.name)
00154 area_box = apply(Tkinter.OptionMenu, (area_frame, area_selection) + tuple( [area.area.name for area in filter_settings.areas]))
00155 box_label = Tkinter.Label(area_frame, text="Selected Area: ")
00156
00157 area_box.grid(row=0, column=1, sticky="W")
00158 box_label.grid(row=0, column=0, sticky="W")
00159
00160
00161 blur_frame = Tkinter.LabelFrame(self, text="Gaussian Blur")
00162 canny_frame = Tkinter.LabelFrame(self, text="Canny")
00163 connect_frame = Tkinter.LabelFrame(self, text="Connect Edges")
00164 delete_frame = Tkinter.LabelFrame(self, text="Delete Short Lines")
00165 settings_frame = Tkinter.LabelFrame(self, text="Settings / Options")
00166
00167
00168 self.blur_width = blur_width = Tkinter.IntVar(master=self)
00169 self.blur_height = blur_height = Tkinter.IntVar(master=self)
00170
00171 blur_width_slider = JumpScale(blur_frame, from_=BLUR_WIDTH_MIN, to=BLUR_WIDTH_MAX, orient=Tkinter.HORIZONTAL, label="blur width", variable=blur_width, tickinterval=_slider_tickinterval(BLUR_WIDTH_MIN, BLUR_WIDTH_MAX), length=SLIDER_LENGTH, showvalue=False)
00172
00173 blur_width_txt_field = Tkinter.Entry(blur_frame, textvariable=blur_width, width=len(str(BLUR_WIDTH_MAX)) + 1)
00174
00175 blur_height_slider = JumpScale(blur_frame, from_=BLUR_HEIGHT_MIN, to=BLUR_HEIGHT_MAX, orient=Tkinter.HORIZONTAL, label="blur height", variable=blur_height, tickinterval=_slider_tickinterval(BLUR_HEIGHT_MIN, BLUR_HEIGHT_MAX), length=SLIDER_LENGTH, showvalue=False)
00176 blur_height_txt_field = Tkinter.Entry(blur_frame, textvariable=blur_height, width=len(str(BLUR_HEIGHT_MAX)) + 1)
00177
00178 blur_width_slider.grid(row=0, column=0)
00179 blur_width_txt_field.grid(row=1, column=0, sticky="W")
00180 blur_height_slider.grid(row=2, column=0, sticky="WE")
00181 blur_height_txt_field.grid(row=3, column=0, sticky="W")
00182
00183 self.c_threshold1 = c_threshold1 = Tkinter.IntVar(master=self)
00184 self.c_threshold2 = c_threshold2 = Tkinter.IntVar(master=self)
00185
00186
00187 c_threshold1_slider = JumpScale(canny_frame, from_=CANNY_THRESHOLD1_MIN, to=CANNY_THRESHOLD1_MAX, orient=Tkinter.HORIZONTAL, label="threshold 1", variable=c_threshold1, tickinterval=_slider_tickinterval(CANNY_THRESHOLD1_MIN, CANNY_THRESHOLD1_MAX, 2), length=SLIDER_LENGTH, showvalue=False)
00188 c_threshold1_txt_field = Tkinter.Entry(canny_frame, textvariable=c_threshold1, width=len(str(CANNY_THRESHOLD1_MAX)) + 1)
00189
00190 c_threshold2_slider = JumpScale(canny_frame, from_=CANNY_THRESHOLD2_MIN, to=CANNY_THRESHOLD2_MAX, orient=Tkinter.HORIZONTAL, label="threshold 2", variable=c_threshold2, tickinterval=_slider_tickinterval(CANNY_THRESHOLD2_MIN, CANNY_THRESHOLD2_MAX, 2), length=SLIDER_LENGTH, showvalue=False)
00191 c_threshold2_txt_field = Tkinter.Entry(canny_frame, textvariable=c_threshold2, width=len(str(CANNY_THRESHOLD2_MAX)) + 1)
00192
00193 c_threshold1_slider.grid(row=0, column=0)
00194 c_threshold1_txt_field.grid(row=1, column=0, sticky="W")
00195 c_threshold2_slider.grid(row=2, column=0)
00196 c_threshold2_txt_field.grid(row=3, column=0, sticky="W")
00197
00198
00199 self.s_radius = s_radius = Tkinter.IntVar(master=self)
00200 s_radius_slider = JumpScale(connect_frame, from_=SEARCH_RADIUS_MIN, to=SEARCH_RADIUS_MAX, orient=Tkinter.HORIZONTAL, label="search radius", variable=s_radius, tickinterval=_slider_tickinterval(SEARCH_RADIUS_MIN, SEARCH_RADIUS_MAX), length=SLIDER_LENGTH, showvalue=False)
00201 s_radius_txt_field = Tkinter.Entry(connect_frame, textvariable=s_radius, width=len(str(SEARCH_RADIUS_MAX)) + 1)
00202
00203 s_radius_slider.grid(row=0, column=0)
00204 s_radius_txt_field.grid(row=1, column=0, sticky="W")
00205
00206
00207 self.min_length = min_length = Tkinter.IntVar(master=self)
00208 min_length_slider = JumpScale(delete_frame, from_=PIXEL_NUM_MIN, to=PIXEL_NUM_MAX, orient=Tkinter.HORIZONTAL, label="min line length", variable=min_length, tickinterval=_slider_tickinterval(PIXEL_NUM_MIN, PIXEL_NUM_MAX), length=SLIDER_LENGTH, showvalue=False)
00209 min_length_txt_field = Tkinter.Entry(delete_frame, textvariable=min_length, width=len(str(PIXEL_NUM_MAX)) + 1)
00210
00211 min_length_slider.grid(row=0, column=0)
00212 min_length_txt_field.grid(row=1, column=0, sticky="W")
00213
00214
00215 self.immediately_apply = immediately_apply = Tkinter.IntVar(master=self)
00216 immediately_apply_box = Tkinter.Checkbutton(settings_frame, text="Immediately apply changes", variable=immediately_apply)
00217 apply_button = Tkinter.Button(settings_frame, text="Apply", command=self._apply_and_display)
00218
00219 commit_button = Tkinter.Button(settings_frame, text="Commit", command=self._commit)
00220
00221 immediately_apply_box.grid(row=0, column=0)
00222 apply_button.grid(row=1, column=0, sticky="W")
00223 commit_button.grid(row=1, column=1, sticky="W")
00224
00225
00226 blur_frame.grid(row=1, column=0)
00227 canny_frame.grid(row=1, column=1)
00228 connect_frame.grid(row=2, column=0, sticky="WE", padx=10, pady=10)
00229 delete_frame.grid(row=2, column=1, sticky="WE")
00230 settings_frame.grid(row=3, column=0, columnspan=2, pady=10, sticky="WE", padx=10)
00231 area_frame.grid(row=0, column=0, sticky="WE", columnspan=2, padx=10)
00232
00233 area_selection.trace("w", self._area_selection_callback)
00234
00235
00236 image_frame = self.image_frame = Tkinter.LabelFrame(self, text="Result Image")
00237
00238 image_label = self.image_label = Tkinter.Label(self.image_frame)
00239 image_label.bind("<Motion>", self._mouse_motion_callback)
00240 image_label.bind("<1>", self._mouse_click_callback)
00241 self._init_area_values()
00242 self._apply_and_display()
00243
00244
00245 raiting = self.raiting = Tkinter.IntVar(master=self, value=3)
00246 for i in range(1, 7):
00247 rb = Tkinter.Radiobutton(image_frame, text=str(i), variable=raiting, value=i)
00248 rb.grid(row=1, column=i-1)
00249 image_label.grid(row=0, column=0, columnspan=6)
00250 rate_button = Tkinter.Button(image_frame, text="Rate", relief=Tkinter.SUNKEN, command=self._rate_callback)
00251 rate_button.grid(row=2, column=0, columnspan=6, sticky="WE")
00252
00253 image_frame.grid(row=0, column=3, rowspan=4, padx=10)
00254
00255 self._set_display_image(self.as_py_image)
00256 immediately_apply.trace("w", self._area_selection_callback)
00257
00258 parameter_value_changed = lambda p_name : lambda *args : self._parameter_value_changed(p_name, args)
00259
00260 blur_width.trace("w", parameter_value_changed("blurwidth"))
00261 blur_height.trace("w", parameter_value_changed("blurheight"))
00262
00263 c_threshold1.trace("w", parameter_value_changed("threshold1"))
00264 c_threshold2.trace("w", parameter_value_changed("threshold2"))
00265
00266 s_radius.trace("w", parameter_value_changed("minNumPixels"))
00267
00268 min_length.trace("w", parameter_value_changed("searchRadius"))
00269
00270
00271 sql_db = self.sql_db = sql_database.SQLImageDatabase(bridge=self.bridge)
00272 try:
00273 sql_db.load_file()
00274 except:
00275 self.sql_db = None
00276 tkMessageBox.showerror(message="Unable to load / create image information database file. Raiting button will not work!", parent=self, title="Error")
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340 def _rate_callback(self):
00341 if self.sql_db is None:
00342 tkMessageBox.showerror(message="You don't have a image information database connection - so you can't rate images!", parent=self, title="Error")
00343 return
00344 try:
00345 self.sql_db.add_image(self.orignal_image_as_cvM, self.raiting.get(), self.list_areas)
00346 except Exception as e:
00347 tkMessageBox.showerror(message="Can't add new data to database.\nThis is really strange...", parent=self, title="Error")
00348 print(e)
00349 self.sql_db.get_images()
00350
00351
00352
00353 def _init_area_values(self):
00354 selected_area = self.areas[self.area_selection.get()]
00355 for _filter in selected_area.filters:
00356
00357 if _filter.name == "GaussianBlur":
00358 for parameter in _filter.parameters:
00359 if parameter.name == "blurwidth":
00360 self.blur_width.set(int(parameter.value))
00361 elif parameter.name == "blurheight":
00362 self.blur_height.set(int(parameter.value))
00363 elif _filter.name == "Canny":
00364 for parameter in _filter.parameters:
00365 if parameter.name == "threshold1":
00366 self.c_threshold1.set(parameter.value)
00367 elif parameter.name == "threshold2":
00368 self.c_threshold2.set(parameter.value)
00369 elif _filter.name == "DeleteShortLines":
00370 for parameter in _filter.parameters:
00371 if parameter.name == "minNumPixels":
00372 self.min_length.set(parameter.value)
00373 elif _filter.name == "EdgeConnector":
00374 for parameter in _filter.parameters:
00375 if parameter.name == "searchRadius":
00376 self.s_radius.set(parameter.value)
00377
00378
00379
00380
00381
00382
00383 def _adjust_positions(self, x_center, y_center):
00384 selected_area = self.areas[self.area_selection.get()]
00385 width, height = selected_area.area.width, selected_area.area.height
00386 half_width, half_height = int(width / 2.0), int(height / 2.0)
00387 left, right, top, down = x_center - half_width, x_center + half_width, y_center - half_height, y_center + half_height
00388 img_width, img_height = self.as_py_image.size
00389 if left < 0:
00390 left = 0
00391 right = width
00392 if top < 0:
00393 top = 0
00394 down = height
00395 if right > img_width - 1:
00396 right = img_width
00397 left = img_width - width
00398 if down > img_height - 1:
00399 down = img_height
00400 top = img_height - height
00401 return left, top, width, height
00402
00403
00404
00405
00406
00407
00408 def _mouse_click_callback(self, evt):
00409 left, top, width, height = self._adjust_positions(evt.x, evt.y)
00410 selected_area = self.areas[self.area_selection.get()]
00411 selected_area.area.x, selected_area.area.y = left, top
00412 self._apply_and_display()
00413
00414
00415
00416
00417
00418
00419
00420 def _mouse_motion_callback(self, evt):
00421 left, top, width, height = self._adjust_positions(evt.x, evt.y)
00422 blue = 0, 0, 255
00423 self._set_display_image( self._draw_area(self.as_py_image, left, top, width, height, blue))
00424
00425
00426
00427
00428
00429
00430 def _set_display_image(self, img):
00431
00432 tk_img = ImageTk.PhotoImage(img)
00433
00434 self.tk_img = tk_img
00435 self.image_label.configure(image=tk_img)
00436
00437
00438
00439
00440
00441
00442 def _area_selection_callback(self, *args):
00443 self._init_area_values()
00444 self._apply_and_display()
00445
00446
00447
00448
00449
00450 def _commit(self):
00451 self.master.destroy()
00452
00453
00454
00455
00456
00457
00458 def _set_parameter_values(self, parameter_name):
00459 selected_area = self.areas[self.area_selection.get()]
00460
00461 for _filter in selected_area.filters:
00462
00463 if _filter.name == "GaussianBlur":
00464 for parameter in _filter.parameters:
00465 if parameter_name == "blurwidth" and parameter.name == "blurwidth":
00466 parameter.value = str(self.blur_width.get()).encode("ascii")
00467 elif parameter_name == "blurheight" and parameter.name == "blurheight":
00468 parameter.value = str(self.blur_height.get()).encode("ascii")
00469 elif _filter.name == "Canny":
00470 for parameter in _filter.parameters:
00471 if parameter_name == "threshold1" and parameter.name == "threshold1":
00472 parameter.value = str(self.c_threshold1.get()).encode("ascii")
00473 elif parameter_name == "threshold2" and parameter.name == "threshold2":
00474 parameter.value = str(self.c_threshold2.get()).encode("ascii")
00475 elif _filter.name == "DeleteShortLines":
00476 for parameter in _filter.parameters:
00477 if parameter_name == "minNumPixels" and parameter.name == "minNumPixels":
00478 parameter.value = str(self.min_length.get()).encode("ascii")
00479 elif _filter.name == "EdgeConnector":
00480 for parameter in _filter.parameters:
00481 if parameter_name == "searchRadius" and parameter.name == "searchRadius":
00482 parameter.value = str(self.s_radius.get()).encode("ascii")
00483
00484
00485
00486
00487
00488
00489
00490 def _parameter_value_changed(self, parameter_name, *args):
00491 self._set_parameter_values(parameter_name)
00492 if self.immediately_apply.get():
00493 self._apply_and_display()
00494
00495
00496
00497
00498 def _apply_and_display(self, *args):
00499 self._apply_filters()
00500 self._draw_selected_area()
00501 self._set_display_image(self.as_py_image)
00502
00503
00504
00505
00506 def _apply_filters(self, *args):
00507
00508 f = rospy.ServiceProxy("apply_filters", ApplyFilters)
00509 resp = f(self.original_image, self.list_areas)
00510 res = resp.result_image
00511
00512 global current_image
00513 current_image = res
00514
00515 img = cv.CloneMat(self.bridge.imgmsg_to_cv(res))
00516 img = Image.fromstring("L", cv.GetSize(img), img.tostring())
00517 img = img.convert("RGB")
00518 self.as_py_image = img
00519
00520
00521
00522
00523
00524
00525 def _immediately_callback(self, *args):
00526 if self.immediately_apply.get():
00527 self._apply_filters(args)
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538 def _draw_area(self, img, x, y, width, height, color):
00539 copy = img.copy()
00540 x_range = range(x, x + width)
00541 y_range = range(y, y + height)
00542 for i in x_range:
00543 copy.putpixel((i, y), color)
00544 copy.putpixel((i, y + height - 1), color)
00545 for i in y_range:
00546 copy.putpixel((x, i), color)
00547 copy.putpixel((x + width - 1, i), color)
00548 return copy
00549
00550
00551
00552 def _draw_selected_area(self):
00553 selected_area = self.areas[self.area_selection.get()]
00554 x, y, width, height = selected_area.area.x, selected_area.area.y, selected_area.area.width, selected_area.area.height
00555 red = 255, 0, 0
00556 self.as_py_image = self._draw_area(self.as_py_image, x, y, width, height, red)
00557
00558
00559
00560
00561
00562 def _slider_tickinterval(min_, max_, tick_count=None):
00563 if tick_count is None:
00564 tick_count = 4
00565 dif = max_ - min_ + 1
00566 if dif < 0:
00567 return 1
00568 else:
00569 return int(dif / tick_count)
00570
00571
00572 def imgmsg_to_photo_image(imgmsg_img, bridge):
00573 cv_img = bridge.imgmsg_to_cv(imgmsg_img, "mono8")
00574 return ImageTk.PhotoImage( Image.fromstring("L", cv.GetSize(cv_img), cv_img.tostring()) )