From 97a456c837698b8e925f21a72831bc2fa6701cab Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Sat, 19 Mar 2016 13:41:48 -0400 Subject: [PATCH] Add copy color. The copy color function now works. It will automatically determine the best thresholds to use to track a particular color blob for you. --- usr/openmv-ide.glade | 3 +- usr/openmv-ide.py | 147 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/usr/openmv-ide.glade b/usr/openmv-ide.glade index 9f6b5a243..aab349808 100644 --- a/usr/openmv-ide.glade +++ b/usr/openmv-ide.glade @@ -100,6 +100,7 @@ Kwabena W. Agyeman False Copy Color True + @@ -342,7 +343,7 @@ Kwabena W. Agyeman False GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_STRUCTURE_MASK OpenMV IDE - 800 + 1024 600 diff --git a/usr/openmv-ide.py b/usr/openmv-ide.py index f4f3d05d6..2864e9136 100755 --- a/usr/openmv-ide.py +++ b/usr/openmv-ide.py @@ -11,6 +11,7 @@ from os.path import expanduser import gtksourceview2 as gtksourceview from glob import glob import urllib2, json +import numpy as np #import pydfu on Linux if platform.system() == "Linux": @@ -123,6 +124,26 @@ class OMVGtk: sourceview.set_auto_indent(True) sourceview.set_highlight_current_line(True) + fonts = [] + for font in sourceview.get_pango_context().list_families(): + fonts.append(font.get_name().lower()) + + if sys.platform.startswith("win"): + if "consolas" in fonts: + sourceview.modify_font(pango.FontDescription("consolas 10")) + else: + sourceview.modify_font(pango.FontDescription("courier new 10")) + elif sys.platform.startswith("darwin"): + if "monaco" in fonts: + sourceview.modify_font(pango.FontDescription("monaco 10")) + else: + sourceview.modify_font(pango.FontDescription("courier new 10")) + elif sys.platform.startswith("linux"): + if "dejavu sans mono" in fonts: + sourceview.modify_font(pango.FontDescription("dejavu sans mono 10")) + else: + sourceview.modify_font(pango.FontDescription("courier new 10")) + # configure gtksourceview buffer self.buffer = gtksourceview.Buffer() self.buffer.set_highlight_syntax(True) @@ -173,6 +194,7 @@ class OMVGtk: "on_save_file_as" : self.save_file_as, "on_about_dialog" : self.about_dialog, "on_pinout_dialog" : self.pinout_dialog, + "on_copy_color_activate" : self.copy_color, "on_save_template_activate" : self.save_template, "on_save_descriptor_activate" : self.save_descriptor, "on_ctrl_scale_value_changed" : self.on_ctrl_scale_value_changed, @@ -196,7 +218,7 @@ class OMVGtk: # set config parser self.config = configparser.ConfigParser() - config_valid = True + config_valid = True # check if config file exists if os.path.isfile(CONFIG_PATH): @@ -306,7 +328,7 @@ class OMVGtk: return # Set higher timeout after connecting for lengthy transfers. - openmv.set_timeout(2) + openmv.set_timeout(1*2) # SD Cards can cause big hicups. # add terminal update callback gobject.gobject.timeout_add(30, omvgtk.update_terminal) @@ -701,6 +723,127 @@ class OMVGtk: with open(self.file_path, "w") as file: file.write(self.buffer.get_text(self.buffer.get_start_iter(), self.buffer.get_end_iter())) + def copy_color(self, widget): + self.da_menu.hide() + x = self.x1 + y = self.y1 + w = self.x2-self.x1 + h = self.y2-self.y1 + + def rgb2lab(rgb): + + def lin(c): + return 100 * ((c/12.92) if (c<=0.04045) else pow((c+0.055)/1.055, 2.4)) + + def f(t): + return pow(t, (1/3.0)) if (t>0.008856) else ((7.787037*t)+0.137931) + + r_lin = lin(rgb[0] / 255.0) + g_lin = lin(rgb[1] / 255.0) + b_lin = lin(rgb[2] / 255.0) + + x = (r_lin * 0.4124) + (g_lin * 0.3576) + (b_lin * 0.1805); + y = (r_lin * 0.2126) + (g_lin * 0.7152) + (b_lin * 0.0722); + z = (r_lin * 0.0193) + (g_lin * 0.1192) + (b_lin * 0.9505); + + x = f(x / 095.047) + y = f(y / 100.000) + z = f(z / 108.883) + + l = int(round(116 * y)) - 16; + a = int(round(500 * (x-y))); + b = int(round(200 * (y-z))); + + return (l, a, b) + + def rgb2gry(rgb): + + def lin(c): + return 100 * ((c/12.92) if (c<=0.04045) else pow((c+0.055)/1.055, 2.4)) + + def f(t): + return (1.055*pow(t, (1/2.4)))-0.055 if (t>0.0031308) else 12.92*t + + r_lin = lin(rgb[0] / 255.0) + g_lin = lin(rgb[1] / 255.0) + b_lin = lin(rgb[2] / 255.0) + + y = f(((r_lin * 0.2126) + (g_lin * 0.7152) + (b_lin * 0.0722)) / 100.0); + return max(min(y * 255, 255), 0) + + def stats(buf, f): + new_buf = np.zeros((buf.shape[0], buf.shape[1]), int) + hist = np.zeros(384, int) + for i in range(buf.shape[0]): + for j in range(buf.shape[1]): + color = f(buf[i][j]) + new_buf[i][j] = color + hist[color + 128] += 1 + return (np.mean(new_buf), + np.median(new_buf), + np.argmax(hist) - 128, + np.std(new_buf), + np.amin(new_buf), + np.amax(new_buf), + np.percentile(new_buf, 25), + np.percentile(new_buf, 75)) + + buf = self.pixbuf.subpixbuf(x, y, w, h).get_pixels_array() + r_stats = stats(buf, lambda x: x[0]) + g_stats = stats(buf, lambda x: x[1]) + b1stats = stats(buf, lambda x: x[2]) + l_stats = stats(buf, lambda x: rgb2lab(x)[0]) + a_stats = stats(buf, lambda x: rgb2lab(x)[1]) + b2stats = stats(buf, lambda x: rgb2lab(x)[2]) + y_stats = stats(buf, lambda x: rgb2gry(x)) + out = [r_stats, g_stats, b1stats, l_stats, a_stats, b2stats, y_stats] + + rgb_thresholds = [max(r_stats[6] - r_stats[3]*3, 0), + min(r_stats[7] + r_stats[3]*3, 255), + max(g_stats[6] - g_stats[3]*3, 0), + min(g_stats[7] + g_stats[3]*3, 255), + max(b1stats[6] - b1stats[3]*3, 0), + min(b1stats[7] + b1stats[3]*3, 255)] + lab_thresholds = [max(l_stats[6] - l_stats[3]*3, 0), + min(l_stats[7] + l_stats[3]*3, 100), + max(a_stats[6] - a_stats[3]*3, -128), + min(a_stats[7] + a_stats[3]*3, 127), + max(b2stats[6] - b2stats[3]*3, -128), + min(b2stats[7] + b2stats[3]*3, 127)] + gry_thresholds = [max(y_stats[6] - y_stats[3]*3, 0), + min(y_stats[7] + y_stats[3]*3, 255)] + out.extend([rgb_thresholds, lab_thresholds, gry_thresholds]) + + self.buffer.begin_user_action() + iter = self.buffer.get_iter_at_mark(self.buffer.get_mark("insert")) + iter.forward_line() + self.buffer.insert(iter,\ + "# = RGB Color Space Stats ========================\n"\ + "# R: Mean %4d, Median %4d, Mode %4d, Stdev %4d\n"\ + "# Min %4d, Max %4d, LQ %4d, UQ %4d\n"\ + "# G: Mean %4d, Median %4d, Mode %4d, Stdev %4d\n"\ + "# Min %4d, Max %4d, LQ %4d, UQ %4d\n"\ + "# B: Mean %4d, Median %4d, Mode %4d, Stdev %4d\n"\ + "# Min %4d, Max %4d, LQ %4d, UQ %4d\n"\ + "# = LAB Color Space Stats ========================\n"\ + "# L: Mean %4d, Median %4d, Mode %4d, Stdev %4d\n"\ + "# Min %4d, Max %4d, LQ %4d, UQ %4d\n"\ + "# A: Mean %4d, Median %4d, Mode %4d, Stdev %4d\n"\ + "# Min %4d, Max %4d, LQ %4d, UQ %4d\n"\ + "# B: Mean %4d, Median %4d, Mode %4d, Stdev %4d\n"\ + "# Min %4d, Max %4d, LQ %4d, UQ %4d\n"\ + "# = GRY Color Space Stats ========================\n"\ + "# Mean %4d, Median %4d, Mode %4d, Stdev %4d\n"\ + "# Min %4d, Max %4d, LQ %4d, UQ %4d\n"\ + "# = Suggested Thresholds (LQ-Stdev*3, UQ+Stdev*3) \n"\ + "# Use LAB for RGB565 and GRY for GRAYSCALE images.\n"\ + "# RGB = (%4d, %4d, %4d, %4d, %4d, %4d)\n"\ + "# LAB = (%4d, %4d, %4d, %4d, %4d, %4d)\n"\ + "# GRY = (%4d, %4d)\n"\ + "# ================================================\n"\ + % tuple([i for sub in out for i in sub])) + self.buffer.end_user_action() + def save_template(self, widget): self.da_menu.hide() x = self.x1