# -*- coding: utf-8 -*-
"""
This module holds the plotting and data-visualization tools. Motto: don't know how it is interpreted? i'll show you!
#Plotim example
filename = "t2.jpg"
win = "test"
img = cv2.resize(cv2.imread(filename), (400, 400)) # (height, width)
plot = Plotim(win,img)
plot.show()
"""
# http://docs.opencv.org/2.4.9/modules/highgui/doc/highgui.html
# https://docs.python.org/3/library/string.html
# http://stackoverflow.com/questions/101128/how-do-i-read-text-from-the-windows-clipboard-from-python
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
from builtins import zip
from builtins import chr
from builtins import range
from past.builtins import basestring
from builtins import object
from .config import FLOAT,FLAG_DEBUG
import copy # copy lists
import sys
import os
import time
import traceback
import cv2
import numpy as np
from .root import formatConsume
from .arrayops.basic import overlayXY,convertXY,overlay,padVH,anorm,get_x_space,isnumpy, histogram
from .arrayops.convert import dict2keyPoint
from .arrayops.filters import sigmoid, bilateralFilter, FilterBase
import matplotlib.animation as animation
from multiprocessing import Process
import matplotlib.pyplot as plt
import matplotlib.axes
import matplotlib.figure
from matplotlib import gridspec
from string import Formatter
servertimeout = None # seconds to show an image
#from fastplt import fastplt as _fastplt
wins = [0] # keeps track of image number through different processes
#plt.rcParams['image.cmap'] = 'gray' # change default colormap
[docs]def fastplt(image, cmap = None, title ="visualazor", win = None, block = False, daemon = False):
"""
Fast plot.
:param image: image to show
:param cmap: "gray" or None
:param title: title of subplot
:param win: title of window
:param block: if True it wait for window close, else it detaches (Experimental)
:param daemon: if True window closes if main thread ends, else windows
must be closed to main thread to end (Experimental)
:return: plt
.. note::
This is a wrapper of the module fastplt.
"""
# FIXED: incompatibility with Qt application. It seem the server X works with sockets and can't be accessed at the same time
# UPDATE 07/04/16: made another library with the same name and images are sent though sockets
#from RRtoolbox.lib.image import plt2bgr
#image = plt2bgr(image) # once miniprogram is made this should work -> UPDATED 07/04/16
wins[-1] += 1
if FLAG_DEBUG: print("fastplt received image type: ",type(image))
def myplot():
if isinstance(image, matplotlib.axes.SubplotBase):
f = image.figure
elif isinstance(image, matplotlib.figure.Figure):
f = image
else:
f = plt.figure()
# Normally this will always be "Figure 1" since it's the first
# figure created by this process. So do something about it.
plt.imshow(image,cmap)
if title: plt.title(title)
plt.xticks([]), plt.yticks([])
#plt.colorbar()
#wins[0]+=1
if win: f.canvas.set_window_title(win)
else:f.canvas.set_window_title("Figure {}".format(wins[-1]))
if FLAG_DEBUG: print("showing now...")
#plt.ion()
plt.show()
if FLAG_DEBUG: print("showed...")
if block:
myplot()
elif __name__ == "__main__": # if called from shell or directly
if FLAG_DEBUG: print("multiprocessing...")
p = Process(target=myplot) # FIXME i shoud call a miniprogram
p.daemon = daemon
p.start()
else: # Workaround to solve problem multiprocessing with matplotlib this sends to shell
from .serverServices import generateServer,sendPickle
s,addr = generateServer()
props = ['python "{script}"'.format(script = os.path.abspath(__file__))]
props.append("{}:{}".format(*addr))
if FLAG_DEBUG: print("generated server at {}".format(addr))
d = dict(cmap=cmap,title=title,win=win,num=wins[0])
props.extend(['--{} "{}"'.format(key,val) for key,val in list(d.items()) if val is not None])
if block: props.append("--block")
if daemon: props.append("--daemon")
txt = " ".join(props)
sendPickle(image,s,timeout=servertimeout, threaded = True)
if FLAG_DEBUG: print("sending",txt)
def myplot(): os.system(txt) # FIXME under windows this cannot be pickled thus multiprocessed
p = Process(target=myplot) # FIXME i shoud call a miniprogram
p.daemon = daemon
p.start()
if FLAG_DEBUG: print("left fastplt...")
[docs]def graph_filter(filters, levels=None, titles=None, win=None,
single = True, legend = True, annotate = True,
cols = 3, scale = 0.07, show = True, lxp = None, lyp=None):
"""
Graph filter with standard data to watch response.
:param filters: list of filters
:param levels: numpy array with values. if None tries to fit data or assumes from 0 to 255
:param titles: list of titles for each filter in filters. if None creates the titles
:param win: window name
:param single: True to plot all filters in one plot. else separate each filter in a plot.
:param legend: True to add legends.
:param annotate: True to add annotations.
:param cols: number of columns to create plots
:param scale: factor from maximum to draw annotations
:param show: to show the figure
:return: figure
"""
def getFilterName(filter):
try:
return filter.name
except:
try:
return type(filter).__name__
except:
try:
return filter.__name__
except:
return ""
def joints(iterator):
iterator = list(iterator)
if len(iterator)==1:
return ", ".join(iterator)
elif len(iterator)>0:
c = ":"
if ":" in iterator[0]:
c = ","
return "{}{} ".format(iterator[0],c)+", ".join(iterator[1:])
else:
return ""
def safeReplace(name, params = None):
if params is None: params = {}
if isinstance(name, basestring):
try:
name = formatConsume(name,params)
if "*" in name:
parts = name.split("*")
f = ["{key}: {{{key}}}".format(key=key) for key in list(params.keys())]
return safeReplace([[part,f] for part in parts],params)
return name
except Exception as e:
#print e
return
else:
return joints(j for j in [safeReplace(i,params) for i in name] if j)
def getTitle(name, params):
if params:
return name + ", ".join("{}: {}".format(key, val) for key, val in list(params.items()))
else:
return name + " filter"
if isinstance(filters,FilterBase):
if win is None: win = (getFilterName(filters) + " filter").title()
filters = [filters]
elif titles is not None and len(filters) != len(titles) and not isinstance(titles,basestring):
raise Exception("Titles len {} does not match with filters len {}".format(len(titles),len(filters)))
if win is None: win = "Filters Response"
# calculate filter
if single:
if levels is None:
try:
level = get_x_space(filters) # try to fit to filters
if level.size == 0:
print("in cero")
raise Exception
except:
level = np.linspace(0, 256,256) # assume it is for an image
xmin,xmax = np.min(level),np.max(level)
xsize = (xmax+abs(xmin))*scale
elif isnumpy(levels):
level = levels
xmin,xmax = np.min(level),np.max(level)
xsize = (xmax+abs(xmin))*scale
else:
level = None
# create grid
rows = len(filters)//cols
if rows == 0:
rows = 1
cols = len(filters)
gs = gridspec.GridSpec(rows, cols) # grid for each plot
# create figure
if win is False:
fig = plt.gcf()
elif isinstance(win,basestring):
fig = plt.figure(win)
else:
fig = win # this should be a handle
# variables
lines,axtitles = [],[] # all lines, all titles
ymins,ymaxs = [],[] # overall ymin and ymax
for i,f in enumerate(filters):
if single:
ax = plt.gca()
if levels is not None and not isnumpy(levels):
level = levels[i]
xmin,xmax = np.min(level),np.max(level)
xsize = (xmax+abs(xmin))*scale
else:
ax = fig.add_subplot(gs[i])
if levels is None:
try:
level = get_x_space([f]) # try to fit to filters
if level.size == 0:
print("in cero")
raise Exception
except:
level = np.linspace(0, 256,256) # assume it is for an image
elif isnumpy(levels):
level = levels
else:
level = levels[i]
xmin,xmax = np.min(level),np.max(level)
xsize = (xmax+abs(xmin))*scale
y = f(level)
title,name = "",""
ymin,ymax = np.min(y),np.max(y)
ysize = (ymax+abs(ymin))*scale
params = {}
if isinstance(f,FilterBase):
# get the parameters in a dictionary
# TODO implement getattr() instead with a list of parameters from annotate
params = {}
for key,val in list(f.__dict__.items()):
if val is not None:
if key.startswith("_"):
key = str(key)[1:]
params[key] = val
if annotate:
items = sorted([(key,val) for key,val in list(params.items()) if key.startswith("beta")],
key= lambda x:x[1])
for j,(key,val) in enumerate(items):
del params[key] # consumes parameters
xp,yp = val,f(val)
if lxp is not None:
desc = lxp[i][j]
else:
desc = not j%2
if isinstance(desc,bool):
if desc:# and len(items)>1: #xp < (xmax+abs(xmin))/2: # label position at x
lx = xp - xsize - 15 # to the left and add shift for the text box
else:
lx = xp + xsize # to the right
else:
lx = xp + desc # user defined
ly = yp # to a side
if lyp is not None:
desc = lyp[i][j]
else:
desc = ly < ymin+ysize
if isinstance(desc,bool):
if desc: # label position at y
ly = yp + ymax*scale # slightly up
elif ly > ymax-ysize:
ly = yp - ymax*scale # slightly down
else:
ly = yp + desc # user defined
ax.annotate(u'{}\n{}'.format(key,val).title(), xy=(xp,yp), textcoords='data',
xytext=(lx,ly),
arrowprops=dict(facecolor='black', shrink=0.05))
if "name" in params:
name = params["name"]
else:
name = getFilterName(f) # name of filter
params["name"] = name
if titles is None:
if annotate:
if legend:
title = name+ " filter" # append filter to name
name = safeReplace(["{name}","*"],params)
else:
title = safeReplace(["{name} filter","*"],params)
else:
if single:
title = name
name = safeReplace(["{name}","*"],params)
else:
title = safeReplace(["{name} filter","*"],params)
if not name:
name = getFilterName(f)
if not name: name = "Filter {}".format(i)
if not title:
if titles is None:
title = name
elif not isinstance(titles,basestring):
title = safeReplace(titles[i],params)
line, = ax.plot(level,y,label=name)
if single:
lines.append(line)
axtitles.append(title)
ymins.append(ymin)
ymaxs.append(ymax)
else:
lines = [line]
axtitles = [title]
ymins = [ymin]
ymaxs = [ymax]
if not single or single and i == len(filters)-1:
if single and isinstance(titles,basestring):
plt.title(titles.title())
elif not isinstance(titles,basestring):
plt.title("; ".join(axtitles).title())
plt.xlim([-1+xmin,xmax+1])
plt.ylim([-0.1+np.min(ymins),0.1+np.max(ymaxs)])
if legend:
ax.legend(handles=lines,loc = 'upper left')
#ax.legend(name, handles=lines,bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
if show: plt.show()
return fig
[docs]def plotPointsContour(pts, ax= None, lcor="k", pcor=None,
deg = None, annotate = True, width= 0.004,
label = 'pt{pt}({x}, {y}, {a})', arrowprops = None):
"""
Plots points and joining lines in axes.
:param pts: points. [(x0,y0)...(xN,yN)]
:param ax: axes handle to draw points.
:param lcor: color of joining lines.
:param pcor: color of points. If specified uses lines, else vectors.
:param deg: angle of vertex, if True in degrees, if False in radians, if None do not add.
:param annotate: (True) True to annotate
:param width: adjust width of lines
:param label: string to format point labels. add the point with {pt},
x and y coordinates with {x} and {y}, and angle with {a}. By default
label is 'pt{pt}({x}, {y}, {a})'.
:param arrowprops: dictionary to modify array properties
:return: axes
"""
# http://stackoverflow.com/a/12267492/5288758
label = u"{}".format(label) # needs to be unicode
from .arrayops.basic import relativeVectors,vertexesAngles
ax = ax or plt.gca() # get axes, creates and show figure if interactive is ON, disable with plt.ioff()
if pcor is not None:
ax.plot(pts[:, 0], pts[:, 1], markerfacecolor = pcor , marker='o') # plot points
ax.plot(pts[:, 0], pts[:, 1], color = lcor) # and lines
ax.plot([pts[-1, 0],pts[0, 0]], [pts[-1, 1],pts[0, 1]], 'k') # add last line
if deg is None:
deg_list = [""]*len(pts)
else:
op = u""
if deg is True:
op = u"°"
deg_list = [u"{:1.1f}{}".format(i,op) for i in vertexesAngles(pts, deg=deg)]
for i,((x,y),(u,v),a) in enumerate(
zip(pts, relativeVectors(pts), deg_list)): # annotate each point
if pcor is None: ax.quiver(x,y,u,v,angles='xy',
scale_units='xy',scale=1,width=width,color = lcor)
if annotate:
x_str = u"{:1.1f}".format(x)
y_str = u"{:1.1f}".format(y)
ax.annotate(label.format(pt=i,x=x_str,y=y_str,a=a),
xy=(x,y), textcoords='data', arrowprops = arrowprops)
# adjust axes
r=0.1
xmin,ymin = np.min(pts,0)
xmax,ymax = np.max(pts,0)
yr = (ymax-ymin)*r
xr = (xmax-xmin)*r
plt.ylim([ymin-yr,ymax+yr])
plt.xlim([xmin-xr,xmax+xr+xr*0.15*len(label)])
return ax
[docs]def echo(obj):
"""
Printer (used when user wants to print an object from Plotim)
:param obj: object
"""
print(obj)
[docs]def background(color,x=1,y=1,flag=0):
"""
Creates background rectangle.
:param color: main color.
:param x: x pixels in axis x.
:param y: y pixels in axis y.
:param flag: Not implemented.
:return: image of shape y,x and ndim == color.ndim.
"""
try:
back = np.zeros((y,x,len(color)), np.uint8)
except:
back = np.zeros((y,x), np.uint8)
back[:,:]= color
return back
[docs]def limitaxis(c,maxc,minc=0):
"""
Limit value in axis.
:param c: value
:param maxc: max c value.
:param minc: min c value.
:return: limited c value c E [minc,maxc]
"""
if c > maxc:
c = maxc
if c < minc:
c = minc
return c
[docs]def convert2bgr(src, bgrcolor = None):
"""
Tries to convert any image format to BGR.
:param src: source image.
:param bgrcolor: background or transparent color.
:return: BGR array image.
"""
if not np.max(src)>1:
im = src*255
im = im.astype("uint8")
else:
im = src.astype("uint8")
#ERROR: Source image must have 1, 3 or 4 channels in function cvConvertImage
if len(im.shape)==2: # 1 channel
if np.max(im)>1:
im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
else:
im = cv2.cvtColor(im*255, cv2.COLOR_GRAY2BGR)
elif len(im.shape)==3 and im.shape[2]==2: # 2 channels
im = cv2.cvtColor(im, cv2.COLOR_BGR5552BGR)
elif len(im.shape)==3 and im.shape[2]==4: # 4 channels
if bgrcolor is None:
im = cv2.cvtColor(im, cv2.COLOR_BGRA2BGR)
else:
temp=im.shape
data = np.zeros((temp[0],temp[1],3), np.uint8)
data[:,:,:] = bgrcolor
im = overlay(data,im)
# else 3 channels or error for 2 channels)
return im
[docs]def convert2bgra(src, bgracolor = None,transparency = None):
"""
Tries to convert any image format to BGRA.
:param src: source image.
:param bgracolor: background or transparent color.
:param transparency: mask or A channel.
(typically source image has not A channel, so user can provide it)
:return: BGRA array image.
"""
if not np.max(src)>1:
im = src*255
im = im.astype("uint8")
else:
im = src.astype("uint8")
#ERROR: Source image must have 1, 3 or 4 channels in function cvConvertImage
if len(im.shape)==2: # 1 channel
if np.max(im)>1:
im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGRA)
else:
im = cv2.cvtColor(im*255, cv2.COLOR_GRAY2BGRA)
elif len(im.shape)==3 and im.shape[2]==2: # 2 channels
im = cv2.cvtColor(im, cv2.COLOR_BGR5552BGRA)
elif len(im.shape)==3 and im.shape[2]==3: # 3 channels
im = cv2.cvtColor(im, cv2.COLOR_BGR2BGRA)
elif len(im.shape)==3 and im.shape[2]==4: # 4 channels
if bgracolor is not None:
temp=im.shape
data = np.zeros((temp[0],temp[1],4), np.uint8)
data[:,:] = bgracolor
im = overlay(data,im)
if transparency is not None:
im[:,:,3] = transparency
return im
[docs]class Plotim(object):
# FIXME: this code now is buggy, nothing to do. consider replacing it with wxpython or pyqt
'''
Show and image with events, animations, controls, internal
commands and highly customizable by code.
:param win: window name
:param im: image of numpy array
:param bgrcolor: default color to use for transparent or background color.
.. warning:: Plotim is deprecated and will be replaced in the future (it was made to
test concepts). Originally it was made for windows but some functions
were removed to let it be multi-platform.
'''
# for rendered visualization
def __init__(self, win, im = np.array([[1]]), bgrcolor = (250,243,238)):
# clipboard has to be imported internally due to incompatibility with pyplot or Process
# it just simply creates a weird IOError
try:
import pyperclip as clipboard
except:
import clipboard
self._rx1=0
self._ry1=0
self._rx2=0
self._ry2=0
self._textbackground = None
self._errorbackground = None
self.usecontrol = False
self.clipboard = clipboard
# for rendered visualization
self.rW = 600
self.rH = 400
self.rWmax = 5000
self.rHmax = 5000
self.limitrender = False # limit render value
## Image data ##
self.data = im # image
self.sample = im # reference to image to get coordinates
self.win = win # window name
self.wintype = cv2.WINDOW_NORMAL # window to create
self.interpolation=cv2.INTER_LINEAR # render's interpolation
self.bgrcolor = bgrcolor # Plotim background's color
## Flow control ##
self.limitaxes = True # correct axes if out of bound
self.showcoors = True # to show coordinates
self.showcontrol = True # to show control data
self.showpixel = True # to show x,y pixel color
self.showpixelvalue = True # to show x,y pixel value
self.staticcoors = False # to move coordinates
self.controlText= [["zoom(x{self.rxZoom}({self.rx1}-{self.rx2}), y{self.ryZoom}({self.ry1}-{self.ry2})) "]]
self.cmdlist = ["limitrender","limitaxes","showcoors","showcontrol","usecontrol","toggle all",
"showpixel","staticcoors","showpixelvalue","pixels","cmdformatter"] # filter commands
self.cmdeval = {"pixels":["showpixel","showpixelvalue"],
"toggle all":["showpixel","showpixelvalue","showcoors","showcontrol"],
"reset":["self.init()"],
"end":"self.updaterenderer()"} # commands with eval operations
self.cmdbuffer = [] # defines ctr_z behavior
self.cmdcache = [] # cache successful commands
self.cmdfiltered = [] # cache filtered list
self.cmdcache_choice = 0 # selected item in self.cmdcache
self.cmdfiltered_choice = 0 # selected item in self.cmdfiltered
## refresh handles ##
self.delayplot = 1000 # if delay 0 waits for key infinitely
# FIXME for compatibility reason dalyplot can't be 0, if user closes the window it blocks program
# (it was solved knowing when the window was closed but it was not a multiplatform solution)
# for Zoom and move
self.zoomstep = 4 # step to zoom in and out, if 1 zoom is cancelled
self.minzoom = 2 # minimal zoom
self.controlkey = cv2.EVENT_FLAG_CTRLKEY # key to press to activate builtincontrol
self.zoominbutton = cv2.EVENT_LBUTTONDOWN # mouse button for zooming in
self.zoomoutbutton = cv2.EVENT_RBUTTONDOWN # mouse button for zooming out
self.movebutton = cv2.EVENT_MBUTTONDOWN # mouse button for moving render
self.resetbutton = cv2.EVENT_RBUTTONDBLCLK # key for resetting plot
# for coordinates
self.fontFace=cv2.FONT_HERSHEY_SIMPLEX
self.fontScale=self.rH*0.001
self.thickness=int(self.fontScale)+1 # ensures that thickness is > 1
self.textcolor = (0,0,0,255)
self.textbackground = (255,255,255,100)
self.errorbackground = (0,0,255,100)
self.coordinateText = [["xy({self.x},{self.y})"]]
# for time
self._wait = 0
self._iswaiting = False
self._oldt = 0
self._timeitems = []
self._timebgrcolor = None
self._cmditmes = []
self._cmdbgrcolor = self.textbackground
self.cmdformatter = True
# initialize
self.init()
[docs] def clean(self):
"""
Attempt to clean the plotter dictionary for an error in
garbage collection.
:return:
"""
for k in list(self.__dict__.keys()):
try:
del self.__dict__[k]
except:
pass
[docs] def init(self): # Init control variables!
"""
Pseudo __init__. it is used to restart default
values without destroying configurations.
"""
## Image data ##
self.img = self.data.copy() # image
temp = self.img.shape
self.rxZoom = self.maxY = temp[0] # maximum
self.ryZoom = self.maxX = temp[1]
self.minY = 0
self.minX = 0
## Flow control ##
self.close = False # True to close plot
self.pressedkey = None # initialize to no key pressed
self.cmd = ""
self.iscmd = False
self.cmderror = False
# for rendered visualization
self.mousemoved = False
self.event = 0
self.flags = 0
# for coordinates
self.rx = None
self.ry = None
self.x = None
self.y = None
self.rx2=self.maxX
self.ry2=self.maxY
self.rx1=0
self.ry1=0
self.updaterenderer() # update render
[docs] def help(self, showAll = False):
"""
function to print the quick help for the user with all the commands
"""
lines = []
lines.extend(Plotim.__doc__.splitlines())
lines.append("\nList of command flags: ")
for i in self.cmdlist:
lines.append(i)
lines.append("\nList of execution commands: ")
for i in self.cmdeval:
lines.append(i)
if showAll:
lines.append("\nList of hidden command flags:")
for key in dir(self):
obj = getattr(self,key,None)
if isinstance(obj,bool):
lines.append(key)
for i in lines:
print(i)
return lines
@property
def textbackground(self):
return self._textbackground
@textbackground.setter
def textbackground(self,value):
try:
value.copy()
except:
value = background(value)
self._textbackground = value
@textbackground.deleter
def textbackground(self):
del self._textbackground
@property
def errorbackground(self):
return self._errorbackground
@errorbackground.setter
def errorbackground(self,value):
try:
value.copy()
except:
value = background(value)
self._errorbackground = value
@errorbackground.deleter
def errorbackground(self):
del self._errorbackground
@property
def rx1(self):
return self._rx1
@rx1.setter
def rx1(self,value):
if self.limitrender:
self._rx2 = limitaxis(self.rx2,min(self.rWmax+value,self.maxX),value)
self.rxZoom = self.rx2-value
self._rx1 = value
@rx1.deleter
def rx1(self):
del self._rx1
@property
def ry1(self):
return self._ry1
@ry1.setter
def ry1(self,value):
if self.limitrender:
self._ry2 = limitaxis(self.ry2,min(self.rHmax+value,self.maxY),value)
self.ryZoom = self.ry2-value
self._ry1 = value
@ry1.deleter
def ry1(self):
del self._ry1
@property
def rx2(self):
return self._rx2
@rx2.setter
def rx2(self,value):
if self.limitrender:
self._rx1 = limitaxis(self.rx1,value,max(value-self.rWmax,self.minX))
self.rxZoom = value-self.rx1
self._rx2 = value
@rx2.deleter
def rx2(self):
del self._rx2
@property
def ry2(self):
return self._ry2
@ry2.setter
def ry2(self,value):
if self.limitrender:
self._ry1 = limitaxis(self.ry1,value,max(value-self.rHmax,self.minY))
self.ryZoom = value-self.ry1
self._ry2 = value
@ry2.deleter
def ry2(self):
del self._ry2
[docs] def show(self, frames = None, block = True, daemon = False, clean = True):
"""
Show function. calls buildinwindow, handles key presses and close events.
:param frames: show number of frames and close.
:param block: if True it wait for window close, else it detaches (Experimental)
:param daemon: if True window closes if main thread ends,
else windows must be closed to main thread to end (Experimental)
:return:
"""
def _show(frames = None):
self.builtinwindow() # make window
while not self.close: # checkLoaded close after processing
self.pressedkey = cv2.waitKey(self.delayplot) #& 0xFF # wait for key
if self.closefunc(): break # close by closing function
self.keyfunc() # do stuff on key
self.mousemoved = False
if frames is not None:
frames -= 1
if frames<1: break
cv2.destroyWindow(self.win) # close window
if clean: self.clean()
if block:
_show(frames)
elif isinstance(self, Plotim): # if called from shell or directly #FIXED __name__ == "__main__" does not work when pickled
if FLAG_DEBUG: print("multiprocessing...")
p = Process(target=_show,args=(frames,)) # FIXME i shoud call a miniprogram
p.daemon = daemon
p.start()
else: # Workaround to solve problem multiprocessing with matplotlib this sends to shell
from .serverServices import generateServer,sendPickle
s,addr = generateServer()
props = ["python '{script}'".format(script = os.path.abspath(__file__))]
props.append("{}:{}".format(*addr))
if FLAG_DEBUG: print("generated server at {}".format(addr))
if block: props.append("--block")
if daemon: props.append("--daemon")
if frames: props.append("--frames")
txt = " ".join(props)
sendPickle(self,s,timeout=servertimeout, threaded = True)
if FLAG_DEBUG: print("sending",txt)
def myplot(): os.system(txt)
p = Process(target=myplot) # FIXME i shoud call a miniprogram
p.daemon = daemon
p.start()
return self
[docs] def mousefunc(self):
"""
Decoupled mouse function for Plotim (replace self.mousefunc).
:param self: Plotim instance
"""
if self.builtincontrol():
self.updaterenderer()
if self.y is not None and self.x is not None:
self.builtinplot(self.sample[int(self.y),int(self.x)])
[docs] def keyfunc(self):
"""
Decoupled key function for Plotim (replace self.keyfunc).
:param self: Plotim instance
"""
if self.builtincmd():
if self.y is not None and self.x is not None:
self.builtinplot(self.sample[int(self.y),int(self.x)])
else:
self.builtinplot()
[docs] def closefunc(self):
"""
Decoupled close function for Plotim (replace self.closefunc).
:param self: Plotim instance
"""
# do stuff before closing #
return self.pressedkey == 27 or self.close# close if ESC or Close button
[docs] def windowfunc(self):
"""
Decoupled window function for Plotim (replace self.windowfunc).
:param self: Plotim instance
"""
cv2.namedWindow(self.win,self.wintype) # create window
#cv2.setWindowProperty(self.win,cv2.WND_PROP_FULLSCREEN,cv2.cv.CV_WINDOW_FULLSCREEN)
#cv2.resizeWindow(self.win,self.rW,self.rH)
[docs] def showfunc(self,img=None):
"""
Decoupled show function for Plotim (replace self.showfunc).
:param self: Plotim instance
:param img: image to show
"""
if img is None:
cv2.imshow(self.win,self.rimg)
else:
cv2.imshow(self.win,img)
[docs] def cmdfunc(self,execute = False):
"""
command function and decoupled cmd solver for Plotim. (repalce self.cmdfunc)
:param self:
:param execute: True, enable execution of commands, False, disable execution.
"""
def evalcommand(self,cmd,showresult=True,wait=3):
if type(cmd) is list: # this configuration lets the user use custom variables
# FIXME, this is dangerous, it is better to not let any execution to eval
for command in cmd:
try:
if "=" in command or "(" in command or "." in command:
result = locals()
exec("_ = {}".format(command), result) # ,globals() do not use it
if showresult:
self.plotintime(items=[[command+" executed"]],wait=wait)
if result["_"]:
self.plotintime(items=[[result["_"]]],wait=wait)
else:
obj = getattr(self,command)
if isinstance(obj,bool):
setattr(self,command,not obj)
if showresult:
if getattr(self,command):
self.plotintime(items=[[command+" is ON"]],wait=wait)
else:
self.plotintime(items=[[command+" is OFF"]],wait=wait)
elif callable(obj):
result = obj()
if showresult:
self.plotintime(items=[["{}".format(result)]],wait=wait)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
msg = lines[-1] # Log it or whatever here
self.plotintime(items=[["Error executing "+command+": "+msg]],wait=wait,bgrcolor=self.errorbackground)
print(msg)
return False
if showresult: self.plotintime(items=[[self.cmd+" executed"]],wait=wait)
return True
else:
return evalcommand(self,[cmd],showresult,wait)
text = []
if execute and self.cmd != "": # in execution
choice = [x for x in self.cmdlist if x.startswith(self.cmd)]
if choice != []:
self.cmd = choice[0]
command = self.cmdeval.get(self.cmd)
if command is None:
if self.cmdformatter: command = self.formatcmd(self.cmd)
else: command = self.cmd
if evalcommand(self,command):
command2 = self.cmdeval.get("end")
if command2 is not None:
evalcommand(self,command2,False)
self.cmderror = False
if not self.cmdcache.count(self.cmd): self.cmdcache.append(command)
self.cmd = ""
else:
self.cmderror = False
else:
self.cmderror = True
else:
text.extend([["cmd: "+self.cmd]])
#self.cmdfiltered = [[i] for i in filter(lambda x: x.startswith(self.cmd), self.cmdlist)]
#if self.cmdfiltered != []:
# text.extend(self.cmdfiltered)
if self.cmderror and self.cmd != "":
self._cmdbgrcolor = self.errorbackground
else:
self.cmderror = False
self._cmdbgrcolor = self.textbackground
self._cmditmes = text
[docs] def plotatpointer(self, items, img=None, x=0, y=0, flag=6, xpad=0, ypad=0, bgrcolor = None, alpha = None, pixel=None):
"""
Plot message where mouse pointer is.
:param items: list of items supported by :func:`self.makeoverlay`
:param img: image to place in items. If None it uses self.remg
:param x: x position
:param y: y position
:param flag: flag for position (default=0).
* flag==0 : foreground to left up.
* flag==1 : foreground to left down.
* flag==2 : foreground to right up.
* flag==3 : foreground to right down.
* flag==4 : foreground at center of background.
* flag==5 : XY 0,0 is at center of background.
* flag==6 : XY 0,0 is at center of foreground.
* flag==7 : XY 0,0 is at right down of foreground.
:param xpad: padding in x
:param ypad: padding in y
:param bgrcolor: background color
:param alpha: alpha mask or value for transparency
:param pixel: color to add as item im items,
:return:
Also see:: :func:`convertXY`, :func:`self.makeoverlay`
"""
text = copy.deepcopy(items)
if x is not None and y is not None:
if img is None:
img = self.rimg
if x>self.rW/2:
if y>self.rH/2:
quartile = 2
if pixel is not None: text[0].append(pixel)
else:
quartile = 3
if pixel is not None: text[0].append(pixel)
else:
if y>self.rH/2:
quartile = 1
if pixel is not None: text[0].insert(0,pixel)
else:
quartile = 4
if pixel is not None: text[0].insert(0,pixel)
foretext = self.makeoverlay(text, xpad, ypad, bgrcolor, alpha)[0]
tx,ty = convertXY(x,y,img.shape,foretext.shape,flag,quartile=quartile) # translation
else:
text[0].append(pixel)
foretext = self.makeoverlay(text, xpad, ypad, bgrcolor, alpha)[0]
tx,ty = convertXY(0,0,img.shape,foretext.shape,4) # translation
self.showfunc(overlayXY(tx,ty,back=img,fore=foretext)) # show window
[docs] def plotatxy(self, items, img=None, x=0, y=0, flag=0, xpad=0, ypad=0, bgrcolor = None, alpha = None):
"""
Plot message in xy position.
:param items: list of items supported by :func:`makeoverlay`
:param img: image to place in items. If None it uses self.remg
:param x: x position
:param y: y position
:param flag: flag for position (default=0).
* flag==0 : foreground to left up.
* flag==1 : foreground to left down.
* flag==2 : foreground to right up.
* flag==3 : foreground to right down.
* flag==4 : foreground at center of background.
* flag==5 : XY 0,0 is at center of background.
* flag==6 : XY 0,0 is at center of foreground.
* flag==7 : XY 0,0 is at right down of foreground.
:param xpad: padding in x
:param ypad: padding in y
:param bgrcolor: background color
:param alpha: alpha mask or value for transparency
:return:
"""
if img is None:
img = self.rimg
foretext = self.makeoverlay(items, xpad, ypad, bgrcolor, alpha)[0]
tx,ty = convertXY(x,y,img.shape,foretext.shape,flag)
self.showfunc(overlayXY(tx,ty,back=img,fore=foretext)) # show window
[docs] def plotintime(self,items=None,wait=2,img=None,bgrcolor = None):
"""
plots messages and events.
:param items: list of items supported by :func:`makeoverlay`
:param wait: time of message.
:param img: image to place in items. If None it uses self.remg
:param bgrcolor: color of message.
:return:
"""
if items is None:
if img is not None and self._iswaiting:
if self._wait > time.time()-self._oldt:
#print self._iswaiting, time.time()-self._oldt
if len(self._timeitems)>6: # don't show more than 6 items at a time
self._timeitems = self._timeitems[1:]
self.plotatxy(self._timeitems,img,flag=4,bgrcolor=self._timebgrcolor)
else:
self._timeitems = []
self._iswaiting = False
else:
self._wait = wait
self._iswaiting = True
self._oldt = time.time()
self._timeitems.extend(items)
self._timebgrcolor = bgrcolor
[docs] def updaterenderer(self,img=None,zoom=True):
"""
update renderer when called.
:param img: image to update in renderer, if None use self.img
:param zoom: True to enable zoom, else updates with original img.
:return: None
"""
if img is None:
img = self.img
if zoom:
self.rimg = cv2.resize(convert2bgr(img[self.ry1:self.ry2,self.rx1:self.rx2].copy(),self.bgrcolor),
(self.rW, self.rH),interpolation=self.interpolation)
else:
self.rimg = cv2.resize(convert2bgr(img.copy(),self.bgrcolor),
(self.rW, self.rH),interpolation=self.interpolation)
if self.showcontrol:
foretext = self.makeoverlay(self.controlText,bgrcolor=self.textbackground)[0]
overlayXY(*convertXY(0,0,self.rimg.shape,foretext.shape,1),back=self.rimg,fore=foretext)
@staticmethod
[docs] def onmouse(event, x, y, flags, self):
"""
Mouse event function for Plotim. (replace self.mousefunc)
:param event: mouse event
:param x: x position
:param y: y postion
:param flags: mouse flag to use in control (it represents clicks)
:param self: Plotim object
:return:
"""
#print event, x, y, flags, param
if self.rx!= x or self.ry != y:
self.mousemoved = True
self.rx = x # x rendered position
self.ry = y # y rendered positiom
# correct x,y coordinates from render and zoom
x = self.rx1+x*(self.rx2-self.rx1)/self.rW
y = self.ry1+y*(self.ry2-self.ry1)/self.rH
if self.limitaxes:
# assign x
#self.x = limitaxis(x,self.maxX-1,self.minX)
self.x = limitaxis(x,self.rx2-1,self.rx1)
# assign y
#self.y = limitaxis(y,self.maxY-1,self.minY)
self.y = limitaxis(y,self.ry2-1,self.ry1)
else:
self.x = x
self.y = y
self.event = event
self.flags = flags
self.mousefunc()
[docs] def builtinwindow(self):
"""
loads windowfunc, showfunc, starts window thread and mousecallback.
"""
self.windowfunc() # user customization
self.showfunc() # show window
cv2.startWindowThread()
cv2.setMouseCallback(self.win,self.onmouse,self) # bind mouse events
[docs] def builtincontrol(self,control=False):
"""
Internal control. use self.usecontrol = True to activate.
:param control: if True, use control key.
:return:
"""
controlled = False
if self.usecontrol:
if not control: # if not control wait for controlkey
control = self.controlkey
if control is None or self.flags & control:
# Zoom system #
if self.event == self.zoominbutton: # Zoom in
mx = my = self.minzoom
if self.rxZoom!=mx or self.ryZoom!=my:
xdiff = np.min((self.rxZoom,self.ryZoom))/self.zoomstep+1
ydiff = xdiff
controlled = True
if self.event == self.zoomoutbutton: # Zoom out
if self.rxZoom!=self.maxX or self.ryZoom!=self.maxY:
mx = my = self.minzoom
xdiff = np.max((self.rxZoom,self.ryZoom))*self.zoomstep+1
ydiff = xdiff
controlled = True
# Move system #
if self.event == self.movebutton:
mx = self.rxZoom
my = self.ryZoom
xdiff = mx/2
ydiff = my/2
controlled = True
if controlled: # update render parameters
self.rx2 = limitaxis(self.x+xdiff,self.maxX,self.minX+mx)
self.rx1 = limitaxis(self.x-xdiff,self.rx2-mx,self.minX)
self.ry2 = limitaxis(self.y+ydiff,self.maxY,self.minY+my)
self.ry1 = limitaxis(self.y-ydiff,self.ry2-my,self.minY)
# resetting plot #
if self.event == self.resetbutton:
self.init()
controlled = True
return controlled
[docs] def builtincmd(self):
"""
Internal cmd control
"""
self.iscmd = False
if self.pressedkey is not None and self.pressedkey != 255 and self.pressedkey != 27: # is normal
def updatelist():
if not self.cmdfiltered:
if self.cmd == "":
self.cmdfiltered = self.cmdlist
else:
self.cmdfiltered = [i for i in [x for x in self.cmdlist if x.startswith(self.cmd)]]
if self.pressedkey == 2490368: # if up key
text = []
updatelist()
mylist = self.cmdfiltered
if mylist:
index = self.cmdfiltered_choice
index = limitaxis(index -1,len(mylist)-1)
self.cmdfiltered_choice = index
choice = mylist[index]
self.cmd = choice
toshow = [[i] for i in mylist]
toshow[index] = ["* "+choice]
text.extend([["cmd: "+self.cmd]])
text.extend(toshow)
self.cmdfiltered = mylist
self._cmditmes = text
self.iscmd = True
return True
elif self.pressedkey == 2621440: # if down key
text = []
updatelist()
mylist = self.cmdfiltered
if mylist:
index = self.cmdfiltered_choice
index = limitaxis(index +1,len(mylist)-1)
self.cmdfiltered_choice = index
choice = mylist[index]
self.cmd = choice
toshow = [[i] for i in mylist]
toshow[index] = ["* "+choice]
text.extend([["cmd: "+self.cmd]])
text.extend(toshow)
self.cmdfiltered = mylist
self._cmditmes = text
self.iscmd = True
return True
elif self.pressedkey == 2555904: # if right key
text = []
mylist = self.cmdcache
if mylist:
index = self.cmdcache_choice
index = limitaxis(index +1,len(mylist)-1)
self.cmdcache_choice = index
choice = mylist[index]
self.cmd = choice
toshow = [[i] for i in mylist]
toshow[index] = ["* "+choice]
text.extend([["cmd: "+self.cmd]])
text.extend(toshow)
self._cmditmes = text
self.iscmd = True
return True
elif self.pressedkey == 2424832: # if left key
text = []
mylist = self.cmdcache
if mylist:
index = self.cmdcache_choice
index = limitaxis(index -1,len(mylist)-1)
self.cmdcache_choice = index
choice = mylist[index]
self.cmd = choice
toshow = [[i] for i in mylist]
toshow[index] = ["* "+choice]
text.extend([["cmd: "+self.cmd]])
text.extend(toshow)
self._cmditmes = text
self.iscmd = True
return True
elif self.pressedkey == 8: # if backslash
self.cmd = self.cmd[:-1]
elif self.pressedkey == 22: # ctrl+v
buff = self.cmdbuffer
if buff != [] and self.cmd != self.cmdbuffer[-1]:
self.cmdbuffer.append(self.cmd)
else:
self.cmdbuffer.append(self.cmd)
self.cmd += self.clipboard.paste()
pass
elif self.pressedkey == 3: # ctrl+c
self.clipboard.copy(self.cmd)
elif self.pressedkey == 24: # ctrl+x
buff = self.cmdbuffer
if buff != [] and self.cmd != self.cmdbuffer[-1]:
self.cmdbuffer.append(self.cmd)
else:
self.cmdbuffer.append(self.cmd)
self.clipboard.copy(self.cmd)
self.cmd = ""
elif self.pressedkey == 26: # ctrl+z
if self.cmdbuffer == []:
self.cmd = self.cmd[:-1]
else:
self.cmd =self.cmdbuffer.pop()
elif self.pressedkey not in (13,0,-1) and self.pressedkey<255: # append to cmd and filter commands
try:
self.cmd += chr(self.pressedkey)
except:
pass
#print("keystroke not supported...")
if self.pressedkey == 13: # if enter
buff = self.cmdbuffer
if buff != [] and self.cmd != self.cmdbuffer[-1]:
self.cmdbuffer.append(self.cmd)
else:
self.cmdbuffer.append(self.cmd)
self.cmdfunc(True)
self.iscmd = True
return self.iscmd
elif self.cmd != "":
text = []
text.extend([["cmd: "+self.cmd]])
mylist = [i for i in [x for x in self.cmdlist if x.startswith(self.cmd)]] # pattern list
if mylist:
toshow = [[i] for i in mylist]
text.extend(toshow)
self._cmditmes = text
self.iscmd = True
else:
if self._cmditmes != None:
self.iscmd = True # why is this variable again?
self._cmditmes = None
return self.iscmd
[docs] def builtinplot(self, pixel=None, useritems = None, flag=1, xpad=0, ypad=0, bgrcolor = None, alpha = None):
"""
Internal plot.
:param pixel: pixel color where mouse is placed (placed for better control). Color can be from
real image, showed image, original image or rendered image, or any color.
:param useritems: items to show.
:param flag: flag for position (default=0).
* flag==0 : foreground to left up.
* flag==1 : foreground to left down.
* flag==2 : foreground to right up.
* flag==3 : foreground to right down.
* flag==4 : foreground at center of background.
* flag==5 : XY 0,0 is at center of background.
* flag==6 : XY 0,0 is at center of foreground.
* flag==7 : XY 0,0 is at right down of foreground.
:param xpad: padding in x
:param ypad: padding in y
:param bgrcolor: background color
:param alpha: alpha mask or value for transparency
:return:
"""
items = [[]]
if useritems is None: # assign user items
if self.showcoors:
items = copy.deepcopy(self.coordinateText)
else:
items = copy.deepcopy(useritems)
if pixel is not None and self.showpixelvalue: # show pixel values
items[0].extend([str(pixel)])
if not self.showpixel: # show pixel color
pixel = None
elif pixel is not None and type(pixel) is not int:
if pixel.shape != ():
pixel = tuple(pixel)
else:
pixel = int(pixel)
if self.iscmd: # show commands
if self._cmditmes: items.extend(self._cmditmes)
bgrcolor = self._cmdbgrcolor
# coordinate system #
if items != [[]] or pixel is not None: # if items to plot or pixel color to show
img = self.rimg.copy()
self.plotintime(img=img)
if self.staticcoors:
if pixel is not None: items[0].insert(0,pixel)
if self.showcontrol and flag==1:
foretext = self.makeoverlay(self.controlText, xpad, ypad, bgrcolor, alpha)[0]
self.plotatxy(items, img, x=foretext.shape[1], flag=flag, xpad=xpad, ypad=ypad, bgrcolor=bgrcolor,
alpha=alpha)
else:
self.plotatxy(items, img, flag=flag, xpad=xpad, ypad=ypad, bgrcolor=bgrcolor, alpha=alpha)
else:
self.plotatpointer(items, img, self.rx, self.ry, pixel=pixel, xpad=xpad, ypad=ypad, bgrcolor=bgrcolor,
alpha=alpha)
elif self._iswaiting:
img = self.rimg.copy()
self.plotintime(img=img)
else:
self.showfunc() # show window
[docs] def makeoverlay(self, items, xpad=0, ypad=0, bgrcolor = None, alpha = None):
"""
overlay items over image.
:param self: instance
:param items: list of object to overlay
:param xpad: pad in x
:param ypad: pad in y
:param bgrcolor: background color
:param alpha: transparency color
:return: overlayed
"""
def writetext(text,bgr=None):
"""
place string into an image
:param text: string
:param bgr: background
:return: image
"""
if bgr is None:
bgr = self.textbackground
textbox, baseLine = cv2.getTextSize(text, self.fontFace, self.fontScale, self.thickness) # text dimensions
bx=(textbox[0],int(textbox[1]*2))
foretext = cv2.resize(bgr,bx) # text image
textOrg= (foretext.shape[1] - textbox[0])//2,(foretext.shape[0]//2 + textbox[1]//2) # force int, center text
cv2.putText(foretext,text, textOrg, self.fontFace, self.fontScale, self.textcolor,self.thickness)
return foretext
def pixelgraph(pixelval,bgr= None,m=None):
"""
draws a rectangle with color.
:param pixelval: color of rectangle
:param bgr: background color
:param m: size
:return: image
"""
if bgr is None:
bgr = self.textbackground
if m is None:
textbox, baseLine = cv2.getTextSize("ss", self.fontFace, self.fontScale, self.thickness) # text dimensions
m=int(textbox[1]*2)
n = int(m/6)
graph = cv2.resize(bgr,(m,m)) # text image
graph[n:-n,n:-n] = convert2bgra(background(pixelval))[0,0]
return graph
def evaluate(imgs,items,bgr = None):
"""
Tries to transform items to images.
:param imgs: list of images.
:param items: items to convert to image
:param bgr: background color
:return: None
"""
if isinstance(items,basestring): # if string
imgs.append(writetext(items.format(self=self),bgr))
elif isnumpy(items): # if is numpy
imgs.append(items)
elif isinstance(items,list): # if iteration
if items != []:
imgs.append([])
for i in range(len(items)):
evaluate(imgs[-1],items[i],bgr)
elif items is not None: # something else
try:
imgs.append(pixelgraph(items,bgr)) # just try to get pixel
except:
imgs.append(writetext("{}".format(items),bgr)) # try to convert to string
imgs = []
r = 0
for i in range(len(items)): # rows
if items[i]==[]: # discarding empty lists
r+=1
else:
imgs.append([])
for j in range(len(items[i])): # columns
if type(bgrcolor) is list:
evaluate(imgs[i-r],items[i][j],bgrcolor[i][j])
else:
evaluate(imgs[i-r],items[i][j],bgrcolor)
if type(bgrcolor) is list:
return padVH(imgs, ypad, xpad, bgrcolor[-1], alpha)
else:
return padVH(imgs, ypad, xpad, bgrcolor, alpha)
[docs] def render2real(self, rx, ry, astype = np.int32):
"""
from rendered coordinates get real coordinates.
:param rx: rendered x
:param ry: rendered y
:param astype: (np.int32) return as the specified type
:return: real x, real y
"""
x = self.rx1+rx*(self.rx2-self.rx1)/self.rW
y = self.ry1+ry*(self.ry2-self.ry1)/self.rH
if astype: return astype(rx),astype(ry)
return x,y
[docs] def real2render(self, x, y, astype = None):
"""
from real coordinates get rendered coordinates.
:param x: real x
:param y: real y
:param astype: (np.int32) return as the specified type
:return: rendered x, rendered y
"""
rx = self.rW*(x-self.rx1)/(self.rx2-self.rx1)
ry = self.rH*(y-self.ry1)/(self.ry2-self.ry1)
if astype: return astype(rx),astype(ry)
return rx,ry
[docs] def save(self,strname=None,ext=".png",name="img"):
"""
Save image (save image if not Qt backend is installed).
:param strname: name to save, a label with {win} can be used to be replaced with the plot win name
:param ext: (".png") extension.
:param name: ("img") name of image object from self. default is "img" that is self.img
(it allows better control to get custom image)
:return: True if saved, False if not saved (possibly because folder does not exists)
"""
if not strname:
strname = self.win
elif "{win}" in strname:
strname = strname.format(win=self.win)
strname+=ext
r = cv2.imwrite(strname,getattr(self,name))
if FLAG_DEBUG and r: print(name, "from Plotim saved as",strname)
return r
[docs]class MatchExplorer(Plotim):
"""
Draws a set of keypoint pairs obtained on a match method of a descriptor
on two images imgf and imgb. (backend: Plotim).
:param win: window's name (str)
:param img1: image1 (numpy array)
:param img2: image2 (numpy array)
:param kp_pairs: zip(keypoint1, keypoint2)
:param status: obtained from cv2.findHomography
:param H: obtained from cv2.findHomography (default=None)
:param show: if True shows Plotim using block and daemon, else do not show
:param block: if True it wait for window close, else it detaches
:param daemon: if True window closes if main thread ends, else windows must be closed to main thread to end
:return: Plotim object with visualization as self.rimg (image with matching result) (default=None)
.. note:: It supports BGR and gray images.
"""
def __init__(self, win, img1, img2, kp_pairs=(), status = None, H = None, show=True, block= True, daemon=True):
## GET INITIAL VISUALIZATION
if len(img1.shape)<3:
img1 = cv2.cvtColor(img1,cv2.COLOR_GRAY2BGR)
if len(img2.shape)<3:
img2 = cv2.cvtColor(img2,cv2.COLOR_GRAY2BGR)
h1, w1 = img1.shape[:2] # obtaining image1 dimensions
h2, w2 = img2.shape[:2] # obtaining image2 dimensions
# imgf and imgb will be visualized horizontally (left-right)
vis = np.zeros((max(h1, h2), w1+w2,3), np.uint8) # making visualization image
vis[:h1, :w1] = img1 # imgf at the left of vis
vis[:h2, w1:w1+w2] = img2 # imgf at the right of vis
if status is None:
status = np.ones(len(kp_pairs), np.bool_) # making sure every pair of keypoints is graphed
kp_pairs = [(dict2keyPoint(i),dict2keyPoint(j)) for i,j in kp_pairs]
p1 = FLOAT([kpp[0].pt for kpp in kp_pairs]) # pair of coordinates for imgf
p2 = FLOAT([kpp[1].pt for kpp in kp_pairs]) + (w1, 0) # pair of coordinates for imgb
if H is not None:# does the same as getTransformedCorners
corners = FLOAT([[0, 0], [w1, 0], [w1, h1], [0, h1]])
self.corners = np.int32(cv2.perspectiveTransform(corners.reshape(1, -1, 2), H).reshape(-1, 2) + (w1, 0))
self.status = status
self.H = H # Transformation Matrix
self.p1 = p1 # points in image 1
self.p2 = p2 # points in shifted image 2
self.kp_pairs = kp_pairs # key-points pairs
self.showgoods = True # show good keypoints
self.showbads = False # show bad keypoints
from .image import colors
for key,val in list(colors.items()):
if not hasattr(self,key): # ensures that there is not conflicts
setattr(self,key,val) # insert available colors
defColor = (0, 255, 0)
self.goodcolor = colors.get('green',defColor) # color for good matches
self.badcolor = colors.get('red',defColor) # color for bad matches
self.kpcolor = colors.get('orange',defColor) # color for key-points
self.framecolor = colors.get('blue',defColor) # color of the proyected corners
super(MatchExplorer, self).__init__(win, vis)
self.cmdlist.extend(["showgoods","showbads","framecolor","kpcolor","badcolor","goodcolor"])
self.drawline()
if show: # show window
self.show(block= block, daemon=daemon)
#self.rimg # return coordinates
@staticmethod
[docs] def randomColor():
return (np.random.randint(0,255), np.random.randint(0,255), np.random.randint(0,255))
[docs] def updaterenderer(self,img=None,zoom=True):
"""
update renderer when called.
:param img: image to update in renderer, if None use self.img
:param zoom: True to enable zoom, else updates with original img.
:return: None
"""
super(MatchExplorer, self).updaterenderer(img, zoom)
self.drawline()
[docs] def mousefunc(self):
if self.builtincontrol():
self.updaterenderer()
self.drawrelation()
[docs] def keyfunc(self):
if self.builtincmd():
self.drawrelation()
[docs] def drawline(self):
"""
Draws background visualization without interaction
"""
vis = self.rimg.copy() # detach anything previously in rendered image
self.thick = int(sigmoid(vis.shape[0] * vis.shape[1], 1723567, 8080000, 5, 1))
if self.H is not None: # enclosing object
rcorners = np.array([self.real2render(corner[0],corner[1]) for corner in self.corners],np.int32)
cv2.polylines(vis, [rcorners], True, self.framecolor) # draw rendered TM encasing
rp1,rp2 = [],[]
r = self.thick
gcol = self.goodcolor
bcol = self.badcolor
for i,((x1, y1), (x2, y2), inlier) in enumerate(zip(self.p1, self.p2, self.status)): # draw points
rx1,ry1 = self.real2render(x1,y1,np.int32) # real to render
rx2,ry2 = self.real2render(x2,y2,np.int32) # real to render
rp1.append((rx1,ry1))
rp2.append((rx2,ry2))
kp1, kp2 = self.kp_pairs[i]
kp1.pt = (rx1, ry1)
kp2.pt = (rx2, ry2)
if self.showgoods and inlier: # drawing circles (good keypoints)
cv2.circle(vis, (rx1, ry1), r, gcol, -1) # for left keypoint (imgf)
cv2.circle(vis, (rx2, ry2), r, gcol, -1) # for right keypoint (imgf)
if self.showbads and not inlier: # drawing x marks (wrong keypoints)
thickness = r+5
# for left keypoint (imgf)
cv2.line(vis, (rx1-r, ry1-r), (rx1+r, ry1+r), bcol, thickness)
cv2.line(vis, (rx1-r, ry1+r), (rx1+r, ry1-r), bcol, thickness)
# for right keypoint (imgf)
cv2.line(vis, (rx2-r, ry2-r), (rx2+r, ry2+r), bcol, thickness)
cv2.line(vis, (rx2-r, ry2+r), (rx2+r, ry2-r), bcol, thickness)
# drawing lines for non-onmouse event
self.vis0 = vis.copy() # detach state of the visualization for onmouse event, this only contains points
self.rp1 = np.int32(rp1)
self.rp2 = np.int32(rp2)
# get rendered kp_pairs
#self.kp_pairs2 = apply2kp_pairs(self.kp_pairs,self.real2render,self.real2render)
# drawing lines for non-onmouse event
for (rx1, ry1), (rx2, ry2), inlier in zip(rp1, rp2, self.status): # draw lines
if inlier and self.showgoods:
cv2.line(vis, (rx1, ry1), (rx2, ry2), gcol,r)
self.vis = vis # visualization with all inliers and points
[docs] def drawrelation(self):
"""
Draw keypoints where pointer is placed and pressed
"""
if self.flags & cv2.EVENT_FLAG_LBUTTON:
x,y = self.rx, self.ry
cur_vis = self.vis0.copy() # actual visualization of only points
r = self.thick + 8 # proximity to keypoint
m = (anorm(self.rp1 - (x, y)) < r) | (anorm(self.rp2 - (x, y)) < r)
idxs = np.where(m)[0] # get indexes near pointer
kp1s, kp2s = [], []
for i in idxs: # for all keypints near pointer
(rx1, ry1), (rx2, ry2) = self.rp1[i], self.rp2[i] # my keypoint
col = (self.badcolor, self.goodcolor)[int(self.status[i])] # choosing False=red,True=green
cv2.line(cur_vis, (rx1,ry1), (rx2,ry2), col, self.thick) # drawing line
# keypoints to show on event
kp1, kp2 = self.kp_pairs[i]
kp1s.append(kp1)
kp2s.append(kp2)
# drawing keypoints near pointer for imgf and imgb
cur_vis = cv2.drawKeypoints(cur_vis, kp1s, flags=4, color=self.kpcolor)
cur_vis = cv2.drawKeypoints(cur_vis, kp2s, flags=4, color=self.kpcolor)
self.rimg = cur_vis
else:
self.rimg = self.vis
if self.y is not None and self.x is not None:
self.builtinplot(self.sample[int(self.y),int(self.x)])
else:
self.builtinplot()
[docs]class Edger(Plotim):
"""
Test visualization for edges
self.edge -> the edges in processed image
self.img -> the processed image
self.sample -> the rendered precessed image
"""
def __init__(self,img,
isSIZE = True,
isEQUA = False,
isCLAHE = False,
isBFILTER = False):
# http://stackoverflow.com/a/22859788/5288758
self.load(img,False)
super(Edger, self).__init__("Edger " + self.pathdata[2], self.data2)
self._th1 = 3000
self._th2 = 6000
self._maxth = 10000
self._showgray = False
self._isSIZE = isSIZE
self._isEQUA = isEQUA
self._isCLAHE = isCLAHE
self._isBFILTER = isBFILTER
self._size = (400,400)
self.edgecolor = (0, 255, 0)
self.initname = "edge_"
self.cmdlist.extend(["isSIZE","isEQUA","isBFILTER","isCLAHE","showgray"])
# best guess: (50,100,10), opencv: (9,75,75), d=-1 is filter distance until sigma
self.d,self.sigmaColor,self.sigmaSpace =10,20,20
self.clipLimit,self.tileGridSize=2.0,(8,8)
self.apertureSize,self.L2gradient= 7,True
self.computeAll()
[docs] def getParameters(self,params = ("d","sigmaColor","sigmaSpace","clipLimit","tileGridSize",
"isSIZE","isEQUA","isCLAHE","isBFILTER","th1","th2","size",
"apertureSize","L2gradient")):
if isinstance(params,basestring):
params = (params,)
p = {}
for param in params:
p[param]=getattr(self,param)
return p
[docs] def save(self,strname=None,ext=".png",name="img"):
if not strname:
temp = "E"+str(self.th1)+"_"+str(self.th2)+"_"+str(self.apertureSize)+"_"+str(self.L2gradient)+"_"
name = self.pathdata[:2]+self.initname+self.savename+[temp]+self.pathdata[2:]
strname = "".join(name)
return super(Edger, self).save(strname, ext, name)
[docs] def load(self,img,compute=True):
if isinstance(img,basestring):
from .directory import getData
self.pathdata = getData(img) # [drive,body,name,header]
self.data2 = cv2.imread(img)
else:
self.pathdata = ["","","img",".png"] # [drive,body,name,header]
self.data2 = img
if compute: self.computeAll()
[docs] def computeAll(self):
self.savename = [""]
if self.isSIZE:
size = self._size
img = cv2.resize(self.data2.copy(),size)
self.savename.append("SIZE"+str(size[0])+"_"+str(size[1])+"_")
else:
img = self.data2.copy()
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
if self.isEQUA:
gray = cv2.equalizeHist(gray)
self.savename.append("EQUA_")
if self.isCLAHE:
clipLimit,tileGridSize= self.clipLimit,self.tileGridSize
clahe = cv2.createCLAHE(clipLimit=clipLimit, tileGridSize=tileGridSize)
gray = clahe.apply(gray)
self.savename.append("CLAHE"+str(clipLimit)+"_"+str(tileGridSize[0])+"_"+str(tileGridSize[1])+"_")
if self.isBFILTER:
d,sigmaColor,sigmaSpace = self.d,self.sigmaColor,self.sigmaSpace
gray = bilateralFilter(gray,d,sigmaColor,sigmaSpace)
self.savename.append("FILTER"+str(d)+"_"+str(sigmaColor)+"_"+str(sigmaSpace)+"_")
self.bgr = img
self.gray = gray
if self.showgray:
self.sample = self.gray.copy()
self.data = cv2.cvtColor(self.sample,cv2.COLOR_GRAY2BGR)
else:
self.sample = self.bgr.copy()
self.data = self.sample
self.init()
try:
self.computeEdge()
except:
pass
@property
def size(self):
return self.gray.shape
@size.setter
def size(self, value):
if bool(self._size!=value): # on change
self._size = value
if self.isSIZE: self.computeAll()
@size.deleter
def size(self):
del self._size
@property
def showgray(self):
return self._showgray
@showgray.setter
def showgray(self, value):
if bool(self._showgray!=value): # on change
self._showgray = value
if self._showgray:
self.sample = self.gray.copy()
self.data = cv2.cvtColor(self.sample,cv2.COLOR_GRAY2BGR)
else:
self.sample = self.bgr.copy()
self.data = self.sample
self.init()
try:
self.computeEdge()
except:
pass
@showgray.deleter
def showgray(self):
del self._showgray
@property
def isSIZE(self):
return self._isSIZE
@isSIZE.setter
def isSIZE(self, value):
if bool(self._isSIZE!=value): # on change
self._isSIZE = value
self.computeAll()
@isSIZE.deleter
def isSIZE(self):
del self._isSIZE
@property
def isEQUA(self):
return self._isEQUA
@isEQUA.setter
def isEQUA(self, value):
if bool(self._isEQUA!=value): # on change
self._isEQUA = value
self.computeAll()
@isEQUA.deleter
def isEQUA(self):
del self._isEQUA
@property
def isCLAHE(self):
return self._isCLAHE
@isCLAHE.setter
def isCLAHE(self, value):
if bool(self._isCLAHE!=value): # on change
self._isCLAHE = value
self.computeAll()
@isCLAHE.deleter
def isCLAHE(self):
del self._isCLAHE
@property
def isBFILTER(self):
return self._isBFILTER
@isBFILTER.setter
def isBFILTER(self, value):
if bool(self._isBFILTER!=value): # on change
self._isBFILTER = value
self.computeAll()
@isBFILTER.deleter
def isBFILTER(self):
del self._isBFILTER
@property
def th1(self):
return self._th1
@th1.setter
def th1(self, value):
if bool(self._th1!=value): # on change
self._th1 = value
if self.isActiveWindow():
cv2.createTrackbar('th1', self.win, value, self.maxth, self.onTrackbar1)
self.computeEdge()
@th1.deleter
def th1(self):
del self._th1
@property
def th2(self):
return self._th2
@th2.setter
def th2(self, value):
if bool(self._th2!=value): # on change
self._th2 = value
if self.isActiveWindow():
cv2.createTrackbar('th2', self.win, value, self.maxth, self.onTrackbar2)
self.computeEdge()
@th2.deleter
def th2(self):
del self._th2
@property
def maxth(self):
return self._maxth
@maxth.setter
def maxth(self, value):
if bool(self._maxth!=value): # on change
self._maxth = value
if self.isActiveWindow():
if self.th1>value: self.th1 = value
else: cv2.createTrackbar('th1', self.win, self.th1, self._maxth, self.onTrackbar1)
if self.th2>value: self.th2 = value
else: cv2.createTrackbar('th2', self.win, self.th2, self._maxth, self.onTrackbar2)
self.computeEdge()
@maxth.deleter
def maxth(self):
del self._maxth
[docs] def computeEdge(self):
edge = cv2.Canny(self.gray, self.th1, self.th2,apertureSize=self.apertureSize,L2gradient=self.L2gradient)
vis = self.data.copy()
vis[edge != 0] = self.edgecolor
self.edge = edge
self.img = vis
self.updaterenderer()
if self.y is not None and self.x is not None:
self.builtinplot(self.sample[int(self.y),int(self.x)])
[docs] def onTrackbar1(self,*args):
self.th1 = cv2.getTrackbarPos('th1', self.win)
[docs] def onTrackbar2(self,*args):
self.th2 = cv2.getTrackbarPos('th2', self.win)
[docs] def isActiveWindow(self):
try:
cv2.getTrackbarPos('th1', self.win)
return True
except:
return False
[docs] def windowfunc(self):
cv2.namedWindow(self.win,self.wintype) # create window
cv2.createTrackbar('th1', self.win, self.th1, self.maxth, self.onTrackbar1)
cv2.createTrackbar('th2', self.win, self.th2, self.maxth, self.onTrackbar2)
cv2.resizeWindow(self.win,self.rW,self.rH)
self.computeEdge()
[docs]class Imtester(Plotim):
"""
Used to test some concepts as thresholds and filters
"""
def __init__(self,img, win="Imtester plot"):
super(Imtester, self).__init__(win, img, (191, 191, 191))
self.rW = 1080
self.rH = 700
self.showcoors = 0
#self.delayplot=300
self.info = ""
self.textbackground = (191,191,191,200)
self.errorbackground = (0,0,255,150)
self.data0 = img.copy()
# for filters
self.d = 9
self.sigmaColor = 75
self.sigmaSpace = 75
self.ksize = (5,5)
self.filter=5
# for thresholds
self.blocksz1=11
self.c1=30
self.blocksz2=11
self.c2=30
self.maximum=255
self.th1= 255/2
self.th2 = 0
# types of threshold
self.type1=cv2.THRESH_BINARY
self.type2=cv2.THRESH_BINARY
self.binary_inv = cv2.THRESH_BINARY_INV
self.binary = cv2.THRESH_BINARY
self.tozero = cv2.THRESH_TOZERO
self.tozero_inv = cv2.THRESH_TOZERO_INV
self.trunc = cv2.THRESH_TRUNC
# control
self.showimg=0
self.showhist=0
self.portablehist=0
self.thtoggle1=1
self.thtoggle2=1
self.adaptoggle1=0
self.adaptoggle2=0
self.showchannel = 0
self.alfa = 10
self.beta = 10
self.sigmoide = 0
self.doubleth=0
self.operation=1
self.overlay = 1
sz = self.data0.shape
if len(sz)==2:
self.channels = ["channel gray"]
else:
self.channels = ["channel b","channel g","channel r","channel c"]
self.show()
[docs] def windowfunc(self):
cv2.namedWindow(self.win,self.wintype) # create window
cv2.resizeWindow(self.win,self.rW,self.rH)
self.builtcmd() # preparing cmd: use when replacing image
self.computefunc() # computing operations
[docs] def detectType(self,type,i="",info=""):
if type==self.binary:
info += ", type"+i+"= binary"
elif type==self.binary_inv:
info += ", type"+i+"= binary inv"
elif type==self.tozero:
info += ", type"+i+"= tozero"
elif type==self.tozero_inv:
info += ", type"+i+"= tozero inv"
elif type==self.trunc:
info += ", type"+i+"= truncate"
return info
@staticmethod
[docs] def applythresh(img,type,adaptativetoggle,threshtoggle,th,blocksz,c,i="",ti="",info="",title=""):
# here any threshold is made to img
if adaptativetoggle:
if threshtoggle: # Mean img,maximum,cv2.ADAPTIVE_THRESH_MEAN_C,type,blocksz,C
thresh = cv2.adaptiveThreshold(img,1,cv2.ADAPTIVE_THRESH_MEAN_C,type,blocksz,c)
title += ". Mean"+ti+": "
else: # Gauss img,maximum,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,type,blocksz,C
thresh = cv2.adaptiveThreshold(img,1,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,type,blocksz,c)
title += ". Gauss"+ti+": "
info += "blocksz"+i+"= "+str(blocksz)+", c"+i+"= "+str(c)
else:
if threshtoggle: # normal img,thresh,maximum,type
thresh = cv2.threshold(img,th,1,type)[1]
title += ". Normal"+ti+": "
else: # otsu img,thresh,maximum,type+cv2.THRESH_OTSU
thresh = cv2.threshold(img,th,1,type+cv2.THRESH_OTSU)[1]
title += ". Otsu"+ti+": "
info += "th"+i+"= "+str(th)
return thresh,info,title
@staticmethod
[docs] def visualize(self):
# here self.data is passed to the render
## Image data ##
self.img = self.data.copy() # image
self.sample = self.img
temp = self.img.shape
self.maxY = temp[0] # maximum
self.maxX = temp[1]
self.minY = 0
self.minX = 0
# for rendered visualization
self.y = 0
self.x = 0
self.rx2=self.maxX
self.ry2=self.maxY
self.rx1=0
self.ry1=0
# for coordinates
self.mousemoved = False
self.event = 0
self.flags = 0
self.updaterenderer() # update render
[docs] def updatevisualization(self,image,channel,th = None,items=None,thresh1=None,thresh2 = None):
# here self.data is updated
if th is not None : th = th*self.maximum
# UPDATE PLOT #
if th is not None and self.overlay:
image = overlay(image.copy(), th, alpha=th)
sz = image.shape
# PREPARE ITEMS TO PLOT
if self.showhist and not self.portablehist:
from .image import fig2bgra
data = [fig2bgra(histogram(image,False))]
else:
data = []
if self.showimg or th is None:
data.append(image)
if self.showchannel:
data.append(channel)
if th is not None: data.append(th)
if self.showhist and self.portablehist:
from .image import fig2bgra
hst = fig2bgra(histogram(image,False))
sz = hst.shape[1]/2,hst.shape[0]/2#(self.rW/2,self.rH)
if items is None: items = [[cv2.resize(hst,sz)]]
else: items.insert(0,[cv2.resize(hst,sz)])
if items is None:
items = [["zoom(x{self.rxZoom}({self.rx1}-{self.rx2}), y{self.ryZoom}({self.ry1}-{self.ry2})) "]]
self.controlText= items
if thresh1 is not None and thresh2 is not None:
self.data = padVH([data,[thresh1*self.maximum,thresh2*self.maximum]],100,100)[0] # MAKE VISUALIZATION
else:
self.data = padVH([data],100,100)[0] # MAKE VISUALIZATION
self.visualize() # SHOW VISUALIZATION
[docs] def builtcmd(self):
# here commands for cmd are built
cmdlist = ["limitrender","limitaxes","showcoors","showcontrol",
"showpixel","staticcoors","showpixelvalue","pixels",
"showimg","showhist","adaptoggle1","adaptoggle1","thtoggle1",
"thtoggle2","portablehist","cmdformatter","doubleth",
"operation","overlay","sigmoide","showchannel"] # filter commands
for i in cmdlist:
if i not in self.cmdlist: self.cmdlist.append(i)
#self.doubleth # "self._timeitems"
binary=["if self.doubleth: self.type2=self.binary","if not self.doubleth: self.type1=self.binary"]
binary_inv=["if self.doubleth: self.type2=self.binary_inv","if not self.doubleth: self.type1=self.binary_inv"]
tozero=["if self.doubleth: self.type2=self.tozero","if not self.doubleth: self.type1=self.tozero"]
tozero_inv=["if self.doubleth: self.type2=self.tozero_inv","if not self.doubleth: self.type1=self.tozero_inv"]
truncate=["if self.doubleth: self.type2=self.trunc","if not self.doubleth: self.type1=self.trunc"]
normal = ["if self.doubleth: self.thtoggle2=1","if self.doubleth: self.adaptoggle2=0",
"if not self.doubleth: self.thtoggle1=1","if not self.doubleth: self.adaptoggle1=0"]
otsu = ["if self.doubleth: self.thtoggle2=0","if self.doubleth: self.adaptoggle2=0",
"if not self.doubleth: self.thtoggle1=0","if not self.doubleth: self.adaptoggle1=0"]
mean = ["if self.doubleth: self.thtoggle2=1","if self.doubleth: self.adaptoggle2=1",
"if not self.doubleth: self.thtoggle1=1","if not self.doubleth: self.adaptoggle1=1"]
gaussian = ["if self.doubleth: self.thtoggle2=0","if self.doubleth: self.adaptoggle2=1",
"if not self.doubleth: self.thtoggle1=0","if not self.doubleth: self.adaptoggle1=1"]
cmdeval = {"pixels":["showpixel","showpixelvalue"],
"+1th1":"self.th1=(1+self.th1)%256",
"1th/2":"self.th1=(self.th1/2)%256",
"-1th":"self.th1=(self.th1-1)%256",
"+2th":"self.th2=(1+self.th2)%256",
"2th/2":"self.th2=(self.th2/2)%256",
"-2th":"self.th2=(self.th2-1)%256",
"and":"self.operation=1",
"or":"self.operation=0",
# FILTERS
"convolution":"self.filter=0", "gaussian filter":"self.filter=1",
"blur":"self.filter=2", "median filter":"self.filter=3",
"bilateral filter":"self.filter=4", "no filter":"self.filter=5",
# TYPES
"binary":binary, "binary inv":binary_inv,
"tozero":tozero, "tozero inv":tozero_inv,
"truncate":truncate,
# TYPES 2
"normal threshold":normal, "otsu threshold":otsu,
"adaptive mean threshold":mean, "adaptive gaussian threshold":gaussian,
"end":["self.computefunc()"]}
self.cmdeval.update(cmdeval)
sz = self.data0.shape
if len(sz)==2:
self.channel = self.data0.copy()
self.cmdeval[self.channels[0]] = "self.channel=self.data0.copy()"
elif len(sz)==3:
self.channel = self.data0[:,:,0].copy()
for i in range(sz[2]):
self.cmdeval[self.channels[i]] = "self.channel=self.data0[:,:,"+str(i)+"].copy()"
self.cmdlist.extend(list(self.cmdeval.keys()))
[docs] def computefunc(self,image=None):
# this function is designed to work with countless initializations of image after self.builtcmd()
if image is None:
image = self.data0
# DETECT CHANNEL
sz = image.shape
info = "unknown channel"
if len(sz)==2:
if np.array_equal(self.channel,image):
info = self.channels[0]
elif len(sz)==3:
for i in range(sz[2]):
if np.array_equal(self.channel,self.data0[:,:,i]):
info = self.channels[i]
break
chimg = self.channel
if self.sigmoide:
info += ". Sigmoide: alfa= "+str(self.alfa)+", beta= "+str(self.beta)
chimg = sigmoid(chimg.astype("float"),self.alfa,self.beta).astype("uint8")
# FILTERS
flags = self.filter
ksize = self.ksize
if flags==0 or type(ksize) is np.ndarray: # convolution
if type(ksize) is not np.ndarray: # d is divisor
ksize = np.ones(ksize,np.float32)/self.d
chimg = cv2.filter2D(chimg,-1,ksize)
info += ". Convolution: ksize= "+str(tuple(ksize.shape))
elif flags==1: # gaussian filter: highly effective in removing Gaussian noise
chimg = cv2.GaussianBlur(chimg,ksize,0) # src, ksize, sigmaX
info += ". Gaussian F: ksize= "+str(ksize)
elif flags==2: # blur: averaging
chimg = cv2.blur(chimg,ksize)
info += ". Blur F: ksize= "+str(ksize)
elif flags==3: # median filter: does not change colors: highly effective in removing salt-and-pepper noise
chimg = cv2.medianBlur(chimg,self.d) # d is ksize
info += ". Median F: d= "+str(self.d)
elif flags==4: # bilateral filtering: highly effective at noise removal while preserving edges
chimg = cv2.bilateralFilter(chimg,self.d,self.sigmaColor,self.sigmaSpace) # d, sigmaColor, sigmaSpace
info += ". Bilateral F: d= "+str(self.d)+", sigmaColor= "+str(self.sigmaColor)+", sigmaSpace= "+str(self.sigmaSpace)
else:
info += ". No Filter"
# APPLY THRESHOLD
info += ". Maximum: "+str(self.maximum)
if self.doubleth:
thresh1,info1,title1 = self.applythresh(chimg,self.type1,self.adaptoggle1,self.thtoggle1,self.th1,self.blocksz1,self.c1,i="1",ti="1")
thresh2,info2,title2 = self.applythresh(chimg,self.type2,self.adaptoggle2,self.thtoggle2,self.th2,self.blocksz2,self.c2,i="2",ti="2")
if self.operation:
self.th = thresh1 & thresh2
op = " AND"
else:
self.th = thresh1 | thresh2
op = " OR"
# DETECT TYPE
info1 = self.detectType(self.type1,i="1",info=info1)
info2 = self.detectType(self.type2,i="2",info=info2)
info += title1+info1+op+ title2[1:]+info2
else:
self.th,info1,title1 = self.applythresh(chimg,self.type1,self.adaptoggle1,self.thtoggle1,self.th1,self.blocksz1,self.c1,i="1",ti="1")
# DETECT TYPE
info += title1+self.detectType(self.type1,i="1",info=info1)
thresh1 = None
thresh2 = None
self.info = info
items = [["zoom(x{self.rxZoom}({self.rx1}-{self.rx2}), y{self.ryZoom}({self.ry1}-{self.ry2})) "]]
items.extend(self.formatinfo(info, words=5))
# UPDATE PLOT #
self.updatevisualization(image,chimg,self.th,items,thresh1,thresh2)
if __name__ == "__main__":
# FIXME: not working for relative imports use fastplt(block=True)
import argparse
from .serverServices import parseString as _parseString
#import sys
#if FLAG_DEBUG: print sys.argv
parser = argparse.ArgumentParser(description='fast plot of images.')
parser.add_argument('image', metavar='N', #action='append',
help='path to image or numpy string',nargs="+")
parser.add_argument('-m','--cmap', dest='cmap', action='store',
help='map to use in matplotlib')
parser.add_argument('-t','--title', dest='title', action='store',default="visualazor",
help='title of subplot')
parser.add_argument('-w','--win', dest='win', action='store',
help='title of window')
parser.add_argument('-n','--num', dest='num', action='store',type = int, default=0,
help='number of Figure')
parser.add_argument('-f','--frames', dest='frames', action='store',type = int, default=None,
help='number of Figure')
parser.add_argument('-b','--block', dest='block', action='store_true', default=False,
help='number of Figure')
parser.add_argument('-d','--daemon', dest='daemon', action='store_true', default=False,
help='number of Figure')
args = parser.parse_args()
images = _parseString(args.image, servertimeout)
wins[-1] = args.num-1
for image in images:
if type(image).__name__ == Plotim.__name__: # pickled, so normal comparisons do not work
image.show(args.frames, args.block, args.daemon)
else:
fastplt(image, args.cmap, args.title, args.win, args.block, args.daemon)
if FLAG_DEBUG: print("leaving plotter module...")