SPE - Scorpion Python Examples Documentation Release XI

Tordivel AS

May 24, 2020

Contents

1 Python Primer 3 1.1 Variables and namespaces...... 3 1.2 A Script - An ordered collection of statements...... 6 1.3 Python and Scorpion...... 8

2 Examples 01-10 9 2.1 Example 01: Calculate Area...... 9 2.2 Example 02: Calculate mean value...... 9 2.3 Example 03: Manipulate the results from two LineFinders...... 9 2.4 Example 04: Dynamic Threshold...... 10 2.5 Example 05: Auto Exposure...... 10 2.6 Example 06: DrawLine...... 11 2.7 Example 07: Overlays...... 11 2.8 Example 08-A: Python Methods...... 12 2.9 Example 08-B: Python Objects...... 12 2.10 Example 09: Scorpion Timing...... 13 2.11 Example 10: Imaging Averaging...... 14

3 Examples 11-20 15 3.1 Example 11: Resample Image...... 15 3.2 Example 12: Constant Image Contrast...... 16 3.3 Example 13-A: Serial Communication using PythonWin...... 16 3.4 Example 13-B: Serial Communication in Scorpion...... 17 3.5 Example 14: Python Results...... 17 3.6 Example 15: Making a result string...... 18 3.7 Example 16: Automation by tool scripting...... 18 3.8 Example 17: Image Manipulation with Python...... 19 3.9 Example 18: Calculate Median Angle...... 20 3.10 Example 19: Iterating objects located by a blob...... 21 3.11 Example 20: Resampling using non-linear calibration...... 22

4 Examples 21-30 23 4.1 Example 21: Custom Scorpion Python extension...... 23 4.2 Example 22: Accessing Scorpion Image Pixels...... 24 4.3 Example 23: Implementing a tcp/ip Socket Server...... 25 4.4 Example 24: Setting ExternalReference from calculated four points...... 27 4.5 Example 25: Rotating a reference around in a circle...... 28

i 4.6 Example 26: Grabbing an image from an MOXA Video IP Server...... 29 4.7 Example 27: Toolbox Switch...... 29 4.8 Example 28: ColorMatcher Iteration...... 29 4.9 Example 29: Audio notification...... 30 4.10 Example 30: Resampling using non-linear calibration...... 30

5 Examples 31-40 33 5.1 Example 31: Client to tcp Socket Server...... 33 5.2 Example 32: Read / Write External Data from / to file...... 33 5.3 Example 33: Changing a tool’s ROI using ExecuteCmd...... 34 5.4 Example 34: Equalization...... 35 5.5 Example 35: Robust Adam 6060 scripts...... 36 5.6 Example 36: Bubble Sorting...... 38 5.7 Example 37: Element ...... 39 5.8 Example 38: Saving Scorpion 3D Image...... 40 5.9 Example 39 - Disabling Zoom in Image Windows...... 40 5.10 Example 40 - Filtering timeseries...... 41

6 Examples 41-50 43 6.1 Example 41: Scorpion Watchdog keep system running...... 43 6.2 Example 42: Binary Search...... 45 6.3 Example 43: Creating an ordered pointcloud...... 46 6.4 Example 44: UDP Socket Communication...... 46 6.5 Example 45: Creating an empty pointcloud...... 47

7 Using Arrlib from Python 49 7.1 Introduction...... 49 7.2 arrlibct versus pyArrlib...... 50 7.3 How to get help on arrlibct...... 51 7.4 Data types in ArrLib and arrlibct...... 51 7.5 Fundamental types...... 52 7.6 Small vectors...... 52 7.7 Supported operations...... 53 7.8 Small matrices...... 53 7.9 Supported operations...... 53 7.10 Poses...... 54 7.11 Supported operations...... 54 7.12 Ranges...... 54 7.13 Parametric lines...... 55 7.14 Supported operations...... 55 7.15 Circles and spheres...... 55 7.16 Color pixels...... 55 7.17 Important note on garbage collection...... 56 7.18 Geometric recipes...... 56 7.19 Planar geometry...... 56 7.20 Working with homogeneous lines and points in the plane...... 59 7.21 Fitting points to a straight line...... 60 7.22 Transformation of normal vectors and homogeneous lines...... 61 7.23 3D space geometry...... 61 7.24 3D point transformations...... 61 7.25 Merging two point clouds in Scorpion...... 62 7.26 Intersecting lines in 3D...... 62 7.27 Python Script Samples...... 63

8 Using ScorpionOpenCV 67 ii 8.1 Introduction...... 67 8.2 Image filters...... 68 8.3 ImageFilter examples...... 69 8.4 Code examples...... 69

9 Release notes 71

10 Indices and tables 73

iii iv SPE - Scorpion Python Examples Documentation, Release XI

The Scorpion Python Examples provides valuable insight in Scorpion Vision Python Scripting. Python is what has made Scorpion a powerful machine vision framework. Scorpion is extended by the important open-source libraries Numpy, Scipy and OpenCV. Most of these powerful API are exposed by a smooth python interface.

Fig. 1: Tool script automate candles processing

The Scorpion Python Modules provides the source code and documentation for all python modules with Scorpion Vision . Contents:

Contents 1 SPE - Scorpion Python Examples Documentation, Release XI

2 Contents CHAPTER 1

Python Primer

This section will provide a short introduction to namespaces in Scorpion and Python. These concepts are important for everybody who wants to exploit the power of Python in Scorpion.

1.1 Variables and namespaces

All variables in Python, and every other programming language, are created and exist in a namespace. What is a namespace? Using PythonWin, an integral part for Python Extension for Windows, or Idle - yet another Python environment, one can play with namespaces. An assignment is a statement of type a=b where a is a variable and b another variable, constant, constructor or function. a=100 b=1.03 c=’spam’ e=[] f={} g=eggs() are assignments where: a is assigned an integer value, b a floating point value, c a string e an empty list f an empty dictionary g the value of function or a reference to an instance of a class

When Python is initialized, a namespace is created. It is available for declaration of variables, functions and objects. Declarations:

3 SPE - Scorpion Python Examples Documentation, Release XI

a=100 def foo(b): print ‘a=’,a,’b=’,b

The statement: foo(200)

Will yield the output: a=100b=200

Python searches for a local definition of a in foo(). If not found Python search in the global namespace. If a local variable is defined in foo() the result will be as follows: Declarations: a=100 def foo(b): a=300 print ‘a=’,a,’b=’,b

The statement: foo(200)

Will yield the output: a=300b=200

The statement: print ‘a=’,a

Will yield the output: a=100

Functions and name spaces can be nested: a=100 def foo(b): a=300

def spam(b): a=400 print ‘a=’,a,’b=’,b spam(500) print ‘a=’,a,’b=’,b

The statement: foo(200)

Will yield the output

4 Chapter 1. Python Primer SPE - Scorpion Python Examples Documentation, Release XI

a=400b=500 a=300 200

The statement: print ‘a=’,a

Will yield the output: a=100

Lets modify this slightly: a=100 def foo(b):

def spam(b): print ‘a=’,a,’b=’,b

spam(500) print ‘a=’,a,’b=’,b

The statement: foo(200)

Will yield the output: a=100b=500 a=100b=200

The statement: print ‘a=’,a

Will yield the output: a=100

Python finds the value of a by searching in the nested namespaces. To change a global variable we can instruct Python to do so. a=100 def foo(b):

def spam(b): global a a=b print ‘a=’,a,’b=’,b

spam(500) print ‘a=’,a,’b=’,b

The statement: foo(200)

Will yield the output:

1.1. Variables and namespaces 5 SPE - Scorpion Python Examples Documentation, Release XI

a=500b=500 a=500b=200

The statement: print ‘a=’,a

Will yield the output: a=500

1.2 A Script - An ordered collection of statements

A script is an ordered collection of Python statements. Scripts are stored in a text format. This can be a file. The file is given the .py extension and is called a module. It can also be named a Script. To get access to the variable, classes and functions we use the import directive. When loading the module all definitions and assignments are executed. Functions, classes and variables must be imported to get into scope. The module cannot access methods and functions defined outside the module unless the import statement is used. How do we access the content of a module? We create a module named OneModule.py with content: a='One Module' z='Another Module' def foo(c): print'What happens next?'

Lets store the module in the directory c:\PythonPrimer. This is a directory Pyhton does not know. It can be added to the Python system path with the following statements: import sys sys.path.append(’c:\\pythonprimer’) sys is a standard module in Python. path is a property in the sys module. More information about sys and path is found in the Python Help file. Qualified Import import OneModule a = 100 print a 100 print OneModule.a

’OneModule’

We see that the two a variable are in different name spaces. We get access to OneModule.a using dot notation. Unqualified Import What happens if we import OneModule using the import * syntax?

6 Chapter 1. Python Primer SPE - Scorpion Python Examples Documentation, Release XI

from OneModul import *

print a

’One Module’

The global variable a is assigned by the OneModule script. Calling foo() will show that foo() is redefined or destroyed?

foo(100)

’What happens next?’

Unqualified import can be dangerous - it will redefine functions and possibly overwrite variables when names are identical. Hint: Use qualified import to avoid accidents Classes have their own namespaces A way to protect your functions and variables is to use a class to create a protected name space. Another benefit is that one can create multiple instances of the class - which is a powerful feature making life easier and more fun for a lazy programmer (lazy programmers = efficient programmers). We create another module AnotherModule.py. Then define a class named ASimpleClass and store it in C:\PythonPrimer

class ASimpleClass: def __init__(self): self.a=’A Simple Class’

def foo(self,b): print ‘a=’,self.a,‘b=’,b

Statements and output:

from AnotherModule import *

m=ASimpleClass() print m. a ’A Simple Class’ m.foo(’spam”) a=’A Simple Class’ b=’spam’ m.a=a print m.a a=100

You can protect variables using a class definition this way:

class Glob: a=100

glob=Glob()

print glob.a

100

glob.b=200 (continues on next page)

1.2. A Script - An ordered collection of statements 7 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) print glob.b

200

As you can see you can add new variables to the class - on the fly! - and get the protection and scoping you need.

1.3 Python and Scorpion

Python is integrated in Scorpion. You may write Python code in:

Tools Central Central.Start Central.Stop Central.Scripts

Central.Start is the starting point for all Python execution within Scorpion. Central.Start is run by Scorpion during startup which means that this is THE place to put global declarations of classes, functions and variables. Anything declared within Central.Start is available to PythonTools and Cen- tral.Scripts. Good programming practice in Scorpion: 1. Declare all variables, functions and classes that are ment to be global in Central.Start 2. Use the global directive in all scripts referring to global variables, even though if the variables are only ReadOnly. Tell yourself and any other user of your profile that you know exactly what you are doing. 3. Any variable declared in a PythonTool should be regarded as local to this script. They will become global after the first assignment - but this is bad practice. 4. Do not import your own modules with the from module import * statement if the module contains global assignments. The consequence is that previous declared global vaiables with equal names will be overwritten. Use the import statement instead. 5. Try rewriting modules containing variables and functions into classes.

8 Chapter 1. Python Primer CHAPTER 2

Examples 01-10

2.1 Example 01: Calculate Area

Calculate the area for a rim found be the ‘Rim’ tool. Set the result in the ‘Rimarea’ tool. Observe that function and tag names are case-dependent. radius= GetValue('Rim.Radius') area= radius * radius * 3.14 SetValue('Rimarea.Value',area)

2.2 Example 02: Calculate mean value

Calculates the gap middle value from two LineEdgeFinder tools and sets the value in the Gap ExternalScalar tool. gap1= GetFloatValue('Gap 1.Width'); gap2= GetFloatValue('Gap 5.Width'); SetFloatValue('Gap.Value',(gap2+gap1)/2);

2.3 Example 03: Manipulate the results from two LineFinders

''' script from an application establishing a set of results based on results from two lines in a LineFinder tool ''' from math import sqrt,pow;

NumberLines= GetValue('Check top filter.Number lines'); (continues on next page)

9 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) x1= GetValue('Check top filter.Line[1].p.x'); y1= GetValue('Check top filter.Line[1].p.y'); x2= GetValue('Check top filter.Line[2].p.x'); y2= GetValue('Check top filter.Line[2].p.y'); angle= GetValue('Check top filter.Angle[2]'); Width= sqrt(pow((x1-x2),2)+pow((y1-y2),2)) if NumberLines ==2:

SetValue('Topfilter Width.Value',Width) SetValue('Topfilter present.Value',1) SetValue('Topfilter angle.Value',angle) else:

SetValue('Topfilter Width.Value',-1) SetValue('Topfilter tilstede.Value',0) SetValue('Topfilter angle.Value',0)

2.4 Example 04: Dynamic Threshold

Dynamic Threshold setting for a blob-tool

#Find the ROI intensity i=GetValue('Sidelight.Intensity') print'Intensity=', i

#set new max-Threshold #new value must be converted to an integer string since #this is the SPB format if i>50: t=int(i)-20 SetConfigValue('Sideprofile.Threshold.Max',t) else: SetConfigValue('Sideprofile.Threshold.Max',30) print'Threshold =', GetValue('Sideprofile.Threshold.Max')

2.5 Example 05: Auto Exposure

Implement a simple auto exposure calculation

# retrieve intensity level level= GetValue('Level.Value') # read intensity level light1= GetValue('LightMeter.Intensity') count= count+1 (continues on next page)

10 Chapter 2. Examples 01-10 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) increaseExposure= ( count<20) and (light1< level) SetValue('CalculateExposure.Value',increaseExposure) if (increaseExposure ) : print'count -',count # set exposure on ieee-1394 camera ExecuteCmd('SetImageProp','imageno=1;Exposure='+str(count*25)) # camera support ˓→Exposure property else: count=0 # reset exposure on ieee-1394 camera ExecuteCmd('SetImageProp','imageno=1;Exposure=0')

2.6 Example 06: DrawLine

# Constructs a line from origo to a point # The line is drawn # Uses ScorpionGeometry.py from ScorpionGeometry import * line= Lin(Vec(0,0),center) ok= DrawLine('ArcialRef',line.p.x,line.p.y,line.v.x,line.v.y,'green',4,7)

# a marker is drawn at the end-point ok= DrawMarker("ArcialRef",center.x,center.y,'blue',2,22)

2.7 Example 07: Overlays

#------# Add an overlay to the image Valve # Uses Arr.pyd #------def InitOverlays(): #create a new overlay where we can visualize our stuff mgr=GetOverlayMgr('Valve') ovl=mgr.addOverlay('Custom') ovl.vertexStyle=0 #sets the default vertextyle to ovsNone, no visual marks ovl.edgeColor='Red' #sets the default color to red ovl.penStyle=0 #sets the default penstyle to psSolid def DrawROI(): import arr

#get the image and find the size of the image img=GetImageMatr('Valve') rows,cols=img.dim()

#define a ROI covering nearly all image (continues on next page)

2.6. Example 06: DrawLine 11 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) offs=20 roi=arr.xyfVec(4,'ROI') roi[0]=offs,offs roi[1]=offs,cols-offs roi[2]=rows-offs,cols-offs roi[3]=rows-offs,offs

ovl=GetOverlay('Valve','Custom') ovl.clear() ovl.add(roi).closed=1

2.8 Example 08-A: Python Methods

#------# Traditional procedural approach #------a=100 b=200 c=300 def spam(v): a=v def eggs(v): b=v def ham(v): c=v spam(10) eggs(20) ham(30) print a,b,c

# yields 10,20,30

2.9 Example 08-B: Python Objects

#------# object oriented approach #------class abc: def __init__(self,a=100,b=200,c=300): self.a=a self.b=b self.c=c

def spam(self,v): self.a=v

(continues on next page)

12 Chapter 2. Examples 01-10 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) def eggs(self,v): self.b=v

def ham(self,v): self.c=v myAbc=abc() print myAbc.a, myAbc.b, myAbc.c

# yields 100,200,300 myAbc.spam(10) myAbc.eggs(20) myAbc.ham(30) print myAbc.a, myAbc.b, myAbc.c

# yields 10,20,30 anotherAbc=abc(10,20,30) anotherAbc.eggs(40) print anotherAbc.a, anotherAbc.b, anotherAbc.c

# yields 10,40,30

2.10 Example 09: Scorpion Timing

This example is provided to show how to check the time consumption in Scorpion

# these values are defined in the Central Start script of Scorpion global time,min,max,mean

# init all values if time is zero if time ==0: # read a timer with milliseconds resolution t0= GetValue('System.MSecSinceMidnight') time= t0 min= 10000 max=0 mean=0 else: # read a timer with milliseconds resolution t0= GetValue('System.MSecSinceMidnight')

# calculates the times since this script was run in seconds cyclus= (t0-time)/1000.0

# calculates min and max values if cyclus> max: max= cyclus if cyclus< min: min= cyclus (continues on next page)

2.10. Example 09: Scorpion Timing 13 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) mean= mean *0.9+ cyclus *0.1 # caculates a running mean value # print cyclus and running mean to console windows print' cyclus -',t0- time,' mean -',mean # remember counter time= t0

2.11 Example 10: Imaging Averaging

This example is provided to show how to do image averaging using python and the arr module.

#------# An example that is part of a PythonScript tool #------import arr global image # contains the averaged image data global counter # counter is set to zero in the Central Start Script

# increment image counter counter+=1

# read 'Intensity' image data img= GetImageMatr('Intensity') if counter ==1: image=img # initial value else: # ------# an exponential image average is calculated # # image = image * 10/11 + img * 1/11 # # ------image=arr.toUint8(arr.toInt(image)*(10,11)+arr.toInt(img)*(1,11)) # note special ˓→fraction multiplication syntax if counter>2: # the Mean Image is set SetImageMatr('Mean',image) # ------

˓→------# image substraction # convert images to float - the rangeToUnit8 function # scales and handles image overflow to an 8 bit image with values from 0 to 255 #------

˓→------image1= arr.toFloat(image)-arr.toFloat(img) img= image1.rangeToUint8(-128,128) # set Subtract image SetImageMatr('Subtract',img)

14 Chapter 2. Examples 01-10 CHAPTER 3

Examples 11-20

3.1 Example 11: Resample Image

This example is provided to show how to resample a part of an image. Such resampling is especially interesting and useful when one combines resampling with the powerful reference systems. import arr,geom #------# Resample # resamples 20x20 mm of originalImage # - the origin is defined by the reference identified by referenceName # the resulting image is of size 60 x 60 pixels and set to image imageName #------def Resample(originalImage,referenceName,imageName):

sub_x= 20.0 # section of original image in millimeters - sub_y= 20.0 # dimensions given by the reference system res_x= 60 # size of resampled image res_y= 60 # in pixels # get pointer to Reference tool t= GetTool(referenceName) ref= geom.m33f() ref.data=t.childRefSys if ( t.result['Status'] ==1): # reference system is valid orgim= GetImageMatr(originalImage) # get image data # prepare resampling p= arr.gridPoints(res_x,res_y,1) # create regular grid matrix of size res_x by

˓→res_y, from (0,0) p= arr.toFix(p *geom.scal(sub_x/res_x,sub_y/res_y)*~ref) # overlay grid points on original image, using toFix for efficiency # resample image subim= arr.bilInterpolate(orgim,p) # extracts samples from orgim at positions

˓→given by p # set the resampled image to imageName (continues on next page)

15 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) SetImageMatr(imageName,arr.toUint8(subim))

In the Scrabble example a letter is placed under the camera. The size and orientation of this letter may vary.

By establishing a reference system that finds the letter and by resampling one can produce a clean letter image

This image has the same size and orientation no matter how the camera or letter is positioned.

3.2 Example 12: Constant Image Contrast

This example is provided to show how to adjust the contrast of an image

import arr def AdjustContrast(imageName,low,high): contrast= high- low # based on constrast a gain factor is given # the low intensity value of the adjusted image is 40 # the high intensity value is 40 + 170 = 210 lgain= 170.0/contrast lOffset= 40- low * lgain # adjust the contrast of Image im1= arr.toFloat(GetImageMatr(imageName)) im1 *= lgain im1+= lOffset SetImageMatr(imageName,im1.rangeToUint8(0,255))

3.3 Example 13-A: Serial Communication using PythonWin

This example outline how to interface any external rs-232 device from python using the pyserial module. PySerial is included in the Scorpion Vision Installer Extensions I

from serial import * c=Serial(0) c.write('com port support from Python') if c.inWaiting(): printc.read( c.inWaiting() ),'characters read' else: print'no reply' c.close()

#The code opened COM1 @ default setting 9600,8,N,0 from serial import * c=Serial(0,19200,8,'E',1) # com port opened at 19200 baud, 8 databits, even parity, 1 stopbit c.close()

Type help(Serial) in PytonWin or IDLE and you will get help on the syntax. Hint: ASCII data and Python A few words on ASCII data and Python.

16 Chapter 3. Examples 11-20 SPE - Scorpion Python Examples Documentation, Release XI

• use ‘x03’ for generating control characters or the chr() function. Make up your string to be str='\x0a'+'\x0b'+chr(13)

3.4 Example 13-B: Serial Communication in Scorpion

The serial script are often added to the Central section of Scorpion Central.Start: # initialise from serial import * com=Serial(0)

Central.Stop: # terminate com.close()

In Python or user defined script mystr='something' com.write( mystr )

3.5 Example 14: Python Results

A number of tools returns results as python string. These results contains array of scalars or points. A typical result string looks like this:

''' ((190.41,208.87),(190.61,225.7),(190.9,242.52),(191.18,259.35),(191.34,276.18)) ''' pointStr= GetValue('Edges.Edge points') print' python string :', pointStr points= eval(pointStr) print' points as list', points print' length of list :', len(points[0]) print' first element :',points[0][0] print'x:',points[0][0][0] print'y:',points[0][0][1]

Output in the console windows:

''' python string : (((190.41,208.87),(190.61,225.7),(190.9,242.52),(191.18,259.35),(191.

˓→34,276.18),(191.5,293.28),(191.85,310.3), (191.98,327.08),(192.08,343.8),(192.35,360.52),(192.59,377.24),(192.76,393.89),(192.

˓→86,410.55),(193.03,427.3),(193.34,444.04),(19 (continues on next page)

3.4. Example 13-B: Serial Communication in Scorpion 17 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) 3.61,460.77),(193.75,477.44),(193.95,494.18),(194.2,510.92),(194.42,527.67),(194.59,

˓→544.41),),)

points as list (((190.41, 208.87), (190.61000000000001, 225.69999999999999), (190.

˓→90000000000001, 242.52000000000001), (191.18000000000001, 259.35000000000002), (191.34, 276.18000000000001), (191.5, 293.

˓→27999999999997), (191.84999999999999, 310.30000000000001), 4 (191.97999999999999, 327.07999999999998), (192.08000000000001, 343.80000000000001),

˓→(192.34999999999999, 360.51999999999998), (192.59, 377.24000000000001), (192.75999999999999, 393.88999999999999), (192.

˓→86000000000001, 410.55000000000001), (193.03, 427.30000000000001), (193.34, 444.04000000000002), (193.61000000000001, 460.

˓→76999999999998), (193.75, 477.44), (193.94999999999999, 494.18000000000001), (194.19999999999999, 510.92000000000002),

˓→(194.41999999999999, 527.66999999999996), (194.59,544.40999999999997)),)

length of list : 21

first element : (190.41, 208.87)

x : 190.41 y : 208.87 '''

3.6 Example 15: Making a result string

The string “Position = (1043.6m-276.6) [mm] Angle = 132.4 [degrees]” is generated by the following Script contained in a PythonScript tool in the toolbox. The ExternalText tool PositionStr is connected statepanel as the parameter.

#The tool PickPoint contains the position of the valve x= GetValue('PickPoint.Point_x') y= GetValue('PickPoint.Point_y') # The tool PickReference contains the calculated angle of the valve a= GetValue('PickReference.Angle') str0='= ( %(x).1f,%(y).1f) [mm] Angle = %(a).1f [degrees]'%vars() SetValue('PositionStr.Text',str0)

3.7 Example 16: Automation by tool scripting

A very powerful feature in Scorpion is to run the tools from a script.

img=GetImageMatr('Image') #get the image ref=GetTool('Reference') #get the reference org=ref.result #store the origin (continues on next page)

18 Chapter 3. Examples 11-20 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) blob=GetTool('Blob') line=GetTool('Line') res=GetTool('Count') distance= GetTool('Distance') no=0 y0=-50 dSum=0 for i in range(16): blob.execute(img) #execute blob at first candle

y= ref.getValue('Value_y') cnt=blob.result['Count'] #get result cnt if (cnt>=1): no= no+1 line.execute(img) distance.execute(img) d= distance.getValue('Distance') dSum= dSum+d cx=blob.result['CenterOfGravity_x'] #get result x y1=blob.result['CenterOfGravity_y'] #get result y #print 'y1 : ',y1 res.setValue('Value',no) #update blobs found

ref.setValue('Value_y',y+50+y1) #offset to next candle #offset to next candle ref.execute(img) #recalculates reference print i,y,cx,y1 y0= y1 ref.result=org #restore origin result meanDistance= GetTool('MeanDistance') meanDistance.setValue('Value',dSum/no)

3.8 Example 17: Image Manipulation with Python

Copy a subimage to another image

import arr,geom im1= GetImageMatr('Image 1') im2= GetImageMatr('Image 2') box= geom.Box2i((100,100),(400,400)) # 300x300 sub= im1.subMatr(box) # extract submatrix im2.copyToSubMatr(150,250,sub) # paint submatrix in im2

SetImageMatr('Image 2',im2)

Create a white image - 8 bit black and white import arr im=arr.uint8Mat(,c) # r=rows,c=cols arr.setArr(im,255) # or any other pixel value

3.8. Example 17: Image Manipulation with Python 19 SPE - Scorpion Python Examples Documentation, Release XI

Create an empty 24bit - RGB image import arr rows= 1024 cols= 1280 imrgb= arr.Mat('RGB',rows,cols) # pixel values are all 0

Concatenating same size images import arr im= arr.concat((im0,im1,im2),(im3,im4,im5),(im6,im7,im8))

# Result is: # # +-----+-----+-----+ # | im0 | im1 | im2 | # +-----+-----+-----+ # | im3 | im4 | im5 | # +-----+-----+-----+ # | im6 | im7 | im8 | # +-----+-----+-----+ #

Resample an annulus (“doughnut”) to a square image import math,arr,geom im= GetImageMatr('Image') cx,cy= 408,424 # center in image r1,r2=1,50 # annulus inner and outer radius # # Resample a 100x300 image from inner to outer radius # around the full circle (-pi to pi radians). # The circle is centered at (cx,cy) in image # ann= arr.annulusSectorPoints(100,300,r1,r2,-math.pi,math.pi) ann *= geom.xlatf(cx,cy) im2= arr.toUint8(arr.bilInterpolate(im,ann)) SetImageMatr('2',im2) # # Visualise ROI in original image # DrawCircle('',cx,cy,r1) DrawCircle('',cx,cy,r2)

3.9 Example 18: Calculate Median Angle

# reads the line count from a linefinder lines=GetValue('R_Line.LineCount')

# creates list i=1 values=[] while i<= lines: values.append(GetValue('R_Line.Angle['+str(i)+']')) (continues on next page)

20 Chapter 3. Examples 11-20 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) i+=1 values.sort() print' R values -',values if i ==0: Median=0 if i ==1: Median= values[0] if i>1: Median= values[len(values)/2] print'R Median:',Median # set result to script value SetValue('R_Angle.Value',Median)

3.10 Example 19: Iterating objects located by a blob

# retrieve tool objects ref= GetTool('Reference') innerRadius= GetTool('FindInnerCircle') outerRadius= GetTool('FindOuterCircle') #get image handle img= GetImageMatr('Image') #init state values largeRing=0 smallRing=0 for no in range(int(GetValue('ObjectLocator.Count'))): # get results from blob x= GetValue('ObjectLocator.CenterOfGravity[ %.0f]_x'% (no)) y= GetValue('ObjectLocator.CenterOfGravity[ %.0f]_y'% (no)) # set position to reference ref.setValue('Value_x',x) ref.setValue('Value_y',y) ref.execute(img)

# execute radialarc finders innerRadius.execute(img) iRadius= innerRadius.getValue('Radius') outerRadius.execute(img) oRadius= outerRadius.getValue('Radius')

#print results print iRadius,oRadius

#classify results if not largeRing: largeRing= (iRadius< 4.75) and (oRadius> 7.0) if not smallRing: smallRing= (iRadius> 4.75) and (oRadius< 7.0)

# set external state SetValue('LargeRing.Value',largeRing) SetValue('SmallRing.Value',smallRing)

3.10. Example 19: Iterating objects located by a blob 21 SPE - Scorpion Python Examples Documentation, Release XI

3.11 Example 20: Resampling using non-linear calibration

The powerful reference system in Scorpion is also available to Python scripts. This example shows how a non-linear camera correction description (a Calibrator) can be utilized from Python to resample an image, eliminating the non- linear lens distortion. Note that in normal operation with Scorpion this is never necessary. All tools that refer to a Calibrator will have similar corrections done automatically. The test profile is named CalibResamp and is included with the Scorpion distribution. • The toolbox contains a Calibrator • Results from the calibrator are read in a Python script tool, and are used to resample the calibrator image

import arr,geom

#images name1='1' name2='2'

# calibrator object -- we need to get the calib file name from this calibTool='Calibrator'

# dot distance in mm for calibration dist= 30.0

# Starting UL corner in original image (in millimeters) - just outside blobs min_x=-dist/2 min_y=-dist/2

# size of resampled image pix_mm= 1.0 # pixels per millimeter size_x= 600 # height pixels size_y= 690 # width pixels

# Get calib object from file, and extract pinCal matrix calfile= GetValue('System.Profile')+'/'+GetConfigValue(calibTool+'.FileName') calib= arr.loadArr(calfile) pinCal= geom.m33f() pinCal.data= calib.pinCal[0]

# Create grid with requested pixels pr. mm grid= arr.gridPoints(size_x,size_y,1) scal= geom.scal(pix_mm,pix_mm) xlat= geom.xlat(min_x,min_y) grid *= (scal*xlat) grid= arr.toFix(arr.objToPix(calib,grid,pinCal))

# Grab image im1= GetImageMatr(name1)

# Resample im2= arr.toUint8(arr.bilInterpolate(im1,grid))

# Show resampled image SetImageMatr(name2,im2)

22 Chapter 3. Examples 11-20 CHAPTER 4

Examples 21-30

4.1 Example 21: Custom Scorpion Python extension

As a programmer, you can extend the Python language using modules written in C, C++ and even the .NET language C#, with the right glue in place. Python scripts in Scorpion have access to all data being processed within Scorpion, and this example shows how to ac- cess and process the raw image data from Scorpion in a Python extension module. A grayscale image is represented by 8-bit pixels, stored rowwise. A colour picture would use four bytes per pixel, in the order . If you create a Python module called “myext” (source code below) that exports a Python function “average”, you can do your own image processing as follows, in a Scorpion Python script tool: import arr,myext im= GetImageMatr('1') r,c= im.dim() format= im.elemtype() p= arr.getArrDataPointer(im) avg= myext.average(p,r,c,format) # avg now contains your result...

For instructions on how to create a Python module, please consult the Python docs, or “Python Essential Reference” by David M. Beazley, where you will find detailed documentation on how to create a Python extension. Below you will find the source code for the “myext” module, written in C. The “image processing” is done in the routine average, which gets a pointer to the image data. The routine _average is the Python interface. myext.h

/************************************************************************** * myext.h * * Skeleton Python extension **************************************************************************/ (continues on next page)

23 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page)

#include "Python.h"

#define byte unsigned char myext.c

/************************************************************************** * myext.c * * Skeleton Python extension **************************************************************************/

#define DLL_PREFIX __declspec(dllexport)

#include "myext.h"

// c image processing routine... static double average(byte *image,int rows,int cols) { double sum=0; intn= rows *cols; int i; for (i=0;i

4.2 Example 22: Accessing Scorpion Image Pixels

From Python you can manipulate the pixels of an image. Example 1: Retrieve pixel value of row=417 and col=459 import arr img= GetImageMatr('Image') # on RGB images use Image.I to retrieve intensity plane ROWS,COLS= img.dim() print'image size',ROWS,COLS row= 417 # row is x when measuring with pixels as reference system col= 459 # column is y pixel= img[row *COLS+col] print' row, column, value',row,col,pixel

ScorpionImage class simplifies image pixel access def CreateScorpionImage(rows,cols):

import arr

class ScorpionImage: def __init__(self,rows=0,cols=0): if rows * cols>0: (continues on next page)

24 Chapter 4. Examples 21-30 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) self.im=arr.uint8Mat(rows,cols)

# return number rows in image def Rows(self): R,C= self.im.dim() return R

# return number columns in image def Columns(self): R,C= self.im.dim() return C

# transfers image to scorpion image by name def SetImageMatr(self,name): SetImageMatr(name,self.im)

# reads scorpion image to class def GetImageMatr(self,name): self.im= GetImageMatr(name)

# resets image to zero def Reset(self): arr.setArr(self.im,0)

# set pixel of image to value def Set(self,row,col,value): R,C=self.im.dim() self.im[row*C+col]=value

# get image pixel value def Get(self,row,col): R,C=self.im.dim() return self.im[row*C+col]

return ScorpionImage(rows,cols)

Example 2: Manipulate a Scorpion Image using ScorpionImage instance image= CreateScorpionImage(0,0) image.GetImageMatr('Image') # on RGB images use Image.I to retrieve intensity plane for i0 in range(40): p= image.Get(i0 *10,i0*10) p+= 100 if p>255: p=255 image.Set(i0*10,i0*10,p) image.SetImageMatr('Image')

4.3 Example 23: Implementing a tcp/ip Socket Server

If you want to implement TCP/IP communication using sockets, you can use Python and the socket API. The socket API is a BSD implementation and is well documented in the Python help system. • Using sockets, you have to decide if your Scorpion application is going to be the server or the client.

4.3. Example 23: Implementing a tcp/ip Socket Server 25 SPE - Scorpion Python Examples Documentation, Release XI

• This example shows how you can be a server. • Example 31 shows a tcp/ip client. Note that the ScorpionSocket class implements a complete client server functionality. More about SPM - Scorpion Python Module Look in Central.Start for the initialization code which sets up the serversocket. Then look in the script socktrig for the code that checks if a request is inbound on the serversocket. If so, a clientsocket is used for subsequent operation. If the client socket is closed, a new incoming connection will be allowed. In this fashion the client can connect and close without any protocol handshakes. What should the client side code look like? In Python you can use this script: from socket import * def Trigger(): ip='localhost' port=9901 csock=socket(AF_INET,SOCK_STREAM) csock.settimeout(1.0) csock.connect( (ip,port) ) csock.send('PartPresent') print csock.recv(100) csock.close()

The example is contained in the example profile SocketServer.zip. The example requires python 2.3 or higher to be installed. Socket Server - server side Central Start import sys from socket import * ip='localhost' port=9901 ssock=socket(AF_INET,SOCK_STREAM) ssock.settimeout(0.2) ssock.bind ( (ip,port) ) ssock.listen(1) csock=0 connected=0 tries=0

Central Stop ssock.close() SockTrig

# called from scheduler def socktrig():

import socket

global csock global connected

(continues on next page)

26 Chapter 4. Examples 21-30 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) if connected: # socket is there try: msg=csock.recv(256) if msg=='': # socket closed print'Client disconnected' csock.close() csock=0 connected=0 elif msg==GetValue('Trigger.Text'): # contains PartPresent print'PartPresent' ExecuteCmd('GrabExecute','') else: print'Unknown command',msg except socket.timeout: pass #no data in buffer yet, no error condition except: try: csock,addr=ssock.accept() csock.settimeout(0.2) print'Client connected',addr connected=1 except: print'Timeout waiting for connection' else: # accept new connection if not connected try: csock,addr=ssock.accept() csock.settimeout(0.2) print'Client connected',addr connected=1 except: print'Still waiting for connection'

Sending response # sends message in external tool global csock if connected:csock.send(GetValue('ResponsePass.Text'))

4.4 Example 24: Setting ExternalReference from calculated four points

In this example automatic setting of the four local points in ExternalReference tool is shown. The following tools are present in the toolbox: • Calibration - ExternalLogic - hold the calibration state - 1 is calibration mode • CalibStatus - ExternalText - holds a message that is updated when running this tool • Point1, Point2, Point3, Point4 - PointFromLines - the actual local points to be added ExternalReference • ProfileCalibration - ExternalReference

4.4. Example 24: Setting ExternalReference from calculated four points 27 SPE - Scorpion Python Examples Documentation, Release XI

The located points in a laser profile calibration import SPB def SetProfileCalibration(): robCoor= GetTool('ProfileCalibration')

calib= GetValue('Calibration.Value')

if not calib : SetValue('CalibStatus.Text','Set Calibration Mode')

else: ok=1 for i0 in range(4): status= GetValue('Point'+str(int(i0+1))+'.Status') ok= ok and status ==1

if not ok : SetValue('CalibStatus.Text','Calibration failure')

if ok : spb=SPB.CreateSpb(robCoor.config) for i0 in range(4): indexStr= str(int(i0+1)) x= GetValue('Point'+indexStr+'.Point_x') y= GetValue('Point'+indexStr+'.Point_y') spb.setFloat('Local'+indexStr+'.x',x) spb.setFloat('Local'+indexStr+'.y',y) robCoor.config=spb.xml SetValue('CalibStatus.Text','Calibration Updated')

4.5 Example 25: Rotating a reference around in a circle

In this example we rotate a linefinder tool around a circle calculating the min and max radius of the circle. The following tools are present in the toolbox: • mref - MoveReference - use to rotate the reference system • lf - LineFinder - finds a straight line at the outer edges of the circle • d1 - PointDistanceTool - calcalulates the distance from radius center to the center of linefinder line

mref= GetTool('mref') lf= GetTool('lf') d1= GetTool('d1') img= GetImageMatr('Images') radius=[] for angle in range(0,360,10): # steps from 0 to 360 degrees in anglesteps of 10 SetConfigValue('mref.Rotation',angle) mref.execute(img) lf.execute(img) d1.execute(img) d= d1.getValue('Distance') if d>5: (continues on next page)

28 Chapter 4. Examples 21-30 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) radius.append(d) # appends valid radiuses to list print'angle -',angle,'-d=',d print' list',radius,' min -',min(radius),' max -',max(radius) SetValue('Min.Value',min(radius)) SetValue('Max.Value',max(radius))

4.6 Example 26: Grabbing an image from an MOXA Video IP Server

A MOXA Video IP Server has an integrated ftp server. Grabbing an image from the ip video server can be done with the following method def Grab(): from ftplib import FTP ftp=FTP('193.69.239.119','root','0002D10048BF') ftp.retrbinary('RETR video.jpg',open('/video.jpg','wb').write) # read the video

˓→image using ftp ftp.quit() ExecuteCmd('Grab','Filename=/video.jpg;convert=bw') # grabs the image from file

4.7 Example 27: Toolbox Switch

The Scorpion ToolList object facilities easy switching of toolbox. Central Start

# product type is stored in text register T0 # product defined to 'A' if undefined in Central Start oldProduct= GetValue('Register.T0') if oldProduct =="": SetValue('Register.T0','A')

SaveToolbox

#SaveToolbox performs toolbox switching by saving current toolbox and loading new

˓→toolbox def SaveToolbox(newProduct): GetToolList().save('toolbox.spb',GetValue('Register.T0')) # store SetValue('Register.T0',newProduct) GetToolList().load('toolbox.spb',GetValue('Register.T0'))

Toolbox Switch

SaveToolbox('B') # sets current toolbox for product "B"

4.8 Example 28: ColorMatcher Iteration

The methods calculates the coverage of the color “glue” within the points. • Below the glue on the pins on the chip is checks.

4.6. Example 26: Grabbing an image from an MOXA Video IP Server 29 SPE - Scorpion Python Examples Documentation, Release XI

• The check locations are stored in an ExternalPolygon tools.

#moving a color matcher along the path defined in a polygon img= GetImageMatr('Top') points= eval(GetValue('glue_pinspolygons.Polygon[1]')) name='glue_pins_cm' coverage= ProcessCM(img,name,points) SetValue('Coverage.glue_pins',coverage) Example1: Calculate the coverage of a color

# img - name of image # name - name of color matcher # points - path def ProcessCM(img,name,points): cm= GetTool(name) ref= GetTool(name+'_ref') count=0 for pt in points: try: #print 'ProcessCM - ',pt SetValue(name+'_ref.Value_x',pt[0]) SetValue(name+'_ref.Value_y',pt[1]) ref.execute(img) cm.execute(img) if cm.getValue('Color') =='glue': count= count+1 DrawMarker(name+'_ref',0,0,'Red',8,18) except: pass if len(points)>0: return count * 100/ len(points) else : return 0

4.9 Example 29: Audio notification

This example shows how to play a wav file. The winsound python module is a part of standard python - this means that python must be installed. def AudioNotification(): from winsound import PlaySound, SND_FILENAME try: PlaySound('ahooga.wav',SND_FILENAME) except: pass

4.10 Example 30: Resampling using non-linear calibration

Activate a modal windows MessageBox from within Scorpion. Requires : python and python for windows extentions to be installed.

30 Chapter 4. Examples 21-30 SPE - Scorpion Python Examples Documentation, Release XI

# note that a valid windows handle is retrieved from the result panel. import win32api,win32con win32api.MessageBox(GetResultPanel().handle,'Press OK to continue','Automatic

˓→Inspection Complete',win32con.MB_OK)

4.10. Example 30: Resampling using non-linear calibration 31 SPE - Scorpion Python Examples Documentation, Release XI

32 Chapter 4. Examples 21-30 CHAPTER 5

Examples 31-40

5.1 Example 31: Client to tcp Socket Server

This example uses the socket module to: • open a socket • send a string • wait for reply • close the socket from socket import * ip='localhost' port=9901 csock=socket(AF_INET,SOCK_STREAM) csock.settimeout(1.0) csock.connect( (ip,port) ) csock.send('PartPresent') reply= csock.recv(100) csock.close()

Note: Example 23 shows a socket server implementation

5.2 Example 32: Read / Write External Data from / to file

This example demonstrate file access for an ExternalData tool

33 SPE - Scorpion Python Examples Documentation, Release XI

def WriteToFile():

list=[] list.append(GetIntValue('Data.s1')) list.append(GetFloatValue('Data.s2')) list.append(GetStringValue('Data.t1')) list.append(GetBoolValue('Data.l1')) list.append(GetBoolValue('Data.l2')) list.append(GetFloatValue('Data.p1_x')) list.append(GetFloatValue('Data.p1_y')) print list f=open('storedtext.txt','w') #file stored in the Scorpion directory str0='%(list)s'%vars() f.write(str0) #overwite with the new text

f.close() def ReadFromFile():

#The script can be put in Central Start #for automatic reading when Scorpion is started

f=open('storedtext.txt','r') #file stored in the Scorpion directory l=f.readlines() printl a=eval(l[0]) #Make it readable as a Python list printa

SetValue('Data.s1',a[0]) SetValue('Data.s2',a[1]) SetValue('Data.t1',a[2]) SetValue('Data.l1',a[3]) SetValue('Data.l2',a[4]) SetValue('Data.p1_x',a[5]) SetValue('Data.p1_y',a[6])

f.close()

Example: content of file

[5, 9.0,'Hello!!', False, True, 2.5, 1.5]

5.3 Example 33: Changing a tool’s ROI using ExecuteCmd

This example demonstrates how to move or change a tools’ ROI position/size from a script Most tools are positioned in the image (the ROI) either as a rectangle with center/size/angle (e.g. LineFinder) or as a set of polygons (e.g. Blob3). The ROI can be changed as shown below. One or more points are given; • one point means move ROI center, • several points means reshape ROI. Example 1 - Move rectangle

34 Chapter 5. Examples 31-40 SPE - Scorpion Python Examples Documentation, Release XI

LF=GetTool('LF') # rectangle tool, e.g. LineFinder LF.executeCmd('set;object=roi;value=((200;200))') # moves the ROI center

Example 2 - Set rectangle

LF=GetTool('LF') # rectangle tool, e.g. LineFinder LF.executeCmd('set;object=roi;value=((100;100)(200;100)(200;150)(100;150))') #specify four corners of the ROI. The angle is calculated from the first two points

Example 3 - Move first polygon

B3=GetTool('B3') # polygon tool, e.g. Blob3 B3.executeCmd('set;object=roi;value=((200;200))') # moves blob3 first polygon to new center (defined as center of gravity for polygon)

Example 4 - Set first polygon

B3=GetTool('B3') # polygon tool, e.g. Blob3 B3.executeCmd('set;object=roi;value=((100;100)(200;100)(100;200))') # polygon defines ROI directly

Example 5 - Move another polygon

B3=GetTool('B3') # polygon tool, e.g. Blob3 B3.executeCmd('set;object=roi;number=2;value=((200;200))') # moves blob3 second polygon to new center (defined as center of gravity for polygon)

Note: The object=roi command is defined pr tool

5.4 Example 34: Histogram Equalization

This example calculates the histogram from an Image and performs Histogram Equalization on the same image. def HistEqualize(image,report=0):

im=GetImageMatr(image) rows,cols=im.dim() if report: print image,rows,cols

hist=range(0,256) for i in range(0,256): hist[i]=0 for i in range(0,rows*cols): hist[ im[i] ]+=1

nump= rows *cols alpha= 255.0/nump

def printhist(hist): max=0 for it in hist: if it>max:max=it for i in range(0,len(hist)): print' %03d %03d'% (i,hist[i]),' *' * int((hist[i]/(max*1.0)) * 100)

(continues on next page)

5.4. Example 34: Histogram Equalization 35 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) if report: printhist(hist) print'=' *30

cumfreq=range(0,256) cumfreq[0]=hist[0] cum=hist[0] cumfreq[0]=cum for i in range(1,256): cum+=hist[i] cumfreq[i]=cum

for i in range(0,rows*cols): v= int(cumfreq[im[i]]*alpha) if v>255:v=255 im[i]=v if report: for i in range(0,256): hist[i]=0 for i in range(0,rows*cols): hist[ im[i] ]+=1 printhist(hist)

SetImageMatr(image,im)

Note: Histogram Equalisation is also available from the STC-0035 ImageFilter

5.5 Example 35: Robust Adam 6060 scripts

This is an example on how you can make Adam 6060 modules handle disconnect without stopping

Note: In Scorpion Vision XI the SIO - Scorpion IO System provides functionality to avoid IO scripting

Central Start class Globals: pass globals=Globals() # globals used as property container from adam6060 import * #Adam6060.py must be in the Python directory try: globals.io=Adam6060('10.0.0.1') except: print'No connection' globals.IoOK=0 else: print'Adam connected' globals.IoOK=1 #Read Digital Input 0 ok,txt,val=globals.io.ReadDI(0) globals.Cur=val (continues on next page)

36 Chapter 5. Examples 31-40 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) #Reset all output relays #ResetOutputRelays()

Central Stop io.close()

Central Methods def IsTrigger(): if globals.IoOK: try: ok,txt,val=globals.io.ReadDI(0) if ok==0: globals.IoOK=0 print'lost connection to Adam' else: RisingEdge=val-globals.Cur globals.Cur=val if RisingEdge==1: print'trigger detected' ResetOutputRelays() ExecuteCmd('GrabExecute','')

except: globals.IoOK=0 print'lost connection to Adam'

if not globals.IoOK: ReconnectAdam(1) def ReconnectAdam(a): try: globals.io=Adam6060('10.0.0.1') print'try to connect' except: print' No connection with Adam6060' globals.IoOK=0 else: globals.IoOK=1 print' Adam reconnected','IoOK=',globals.IoOK

if globals.IoOK==1: if a ==0: ResetOutputRelays() if a ==1: IsTrigger() if a ==2: SetOutputRelays() def ResetOutputRelays(): if globals.IoOK: try: ok,txt,msg=globals.io.WriteRelay(0,0) ok,txt,msg=globals.io.WriteRelay(1,0) ok,txt,msg=globals.io.WriteRelay(2,0) (continues on next page)

5.5. Example 35: Robust Adam 6060 scripts 37 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) ok,txt,msg=globals.io.WriteRelay(3,0) ok,txt,msg=globals.io.WriteRelay(4,0) ok,txt,msg=globals.io.WriteRelay(5,0) if ok==0: globals.IoOK=0 else: print'Reset relays, output =0 0 0 0 0 0' except: globals.IoOK=0

if not globals.IoOK: ReconnectAdam(0) def SetOutputRelays(): a=int(GetValue('Pass1.Value')) b=int(GetValue('Fault1.Value')) c=int(GetValue('Pass2.Value')) d=int(GetValue('Fault2.Value')) e=int(GetValue('Pass3.Value')) f=int(GetValue('Fault3.Value')) if globals.IoOK: try: ok,txt,msg=globals.io.WriteRelay(0,a) ok,txt,msg=globals.io.WriteRelay(1,b) ok,txt,msg=globals.io.WriteRelay(2,c) ok,txt,msg=globals.io.WriteRelay(3,d) ok,txt,msg=globals.io.WriteRelay(4,e) ok,txt,msg=globals.io.WriteRelay(5,f) if ok==0: globals.IoOK=0 else: print'New relay output',a,b,c,d,e,f except: print' cannot connect' globals.IoOK=0

if not globals.IoOK: ReconnectAdam(2)

5.6 Example 36: Bubble Sorting

Bubble sort, sometimes shortened to bubblesort, also known as exchange sort, is a simple sorting algorithm. It works by repeatedly stepping through the list to be sorted, comparing two items at a time and swapping them if they are in the wrong order. The pass through the list is repeated until no swaps are needed, which means the list is sorted. The algorithm gets its name from the way smaller elements “bubble” to the top (i.e. the beginning) of the list via the swaps. Because it only uses comparisons to operate on elements, it is a comparison sort. This is the easiest comparison sort to implement. def bubblesort(l): "Sorts l in place and returns it." for passesLeft in range(len(l)-1,0,-1): for index in range(passesLeft): if l[index]< l[index+1]: (continues on next page)

38 Chapter 5. Examples 31-40 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) l[index], l[index+1]= l[index+1], l[index] return l

More information: http://en.wikipedia.org/wiki/Bubble_sort All text is available under the terms of the GNU Free Documentation License.

5.7 Example 37: Element Statistics

The python class is designed to calculate the Mean and the Standard Deviation of a collection of elements. def ElementStatistics(): import math

class ElementStat: def __init__(self): self.Reset()

def Add(self,x): print'x', x self.n= self.n+1 delta=x- self.mean self.mean= self.mean+ delta/ self.n self.S= self.S+ delta *(x- self.mean)

def N(self): return self.n

def Mean(self): return self.mean

def Variance(self): return self.S/(self.n-1)

def Std(self): return math.sqrt(self.Variance())

def Reset(self): self.n=0 self.mean=0 self.S=0

return ElementStat()

Example 1: Using Element Stat obp= GetTool('obp') mref= GetTool('mref') img= GetImageMatr('1') points= GetResultValue('polygons.Polygon[1]') statX= ElementStatistics() statY= ElementStatistics() statZ= ElementStatistics() for pos in points: mref.setConfigValue('OrigoX',pos[0],0) (continues on next page)

5.7. Example 37: Element Statistics 39 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) mref.setConfigValue('OrigoY',pos[1]) mref.execute(img) obp.execute(img) x= obp.getFloatValue('Position_x') y= obp.getFloatValue('Position_y') z= obp.getFloatValue('Position_z') dev= obp.getFloatValue('Deviation') if 0.0< dev< 1.0: statX.Add(x) statY.Add(y) statZ.Add(z) DrawMarker3D('1-3DRef',statX.Mean(),statY.Mean(),statZ.Mean(),'Red',6) DrawText3D('1-3DRef',statX.Mean(),statY.Mean(),statZ.Mean(), ' %.0f,%.0f,%.0f'% (statZ.Mean(),statZ.Std(),statZ.N()),'Red',16,'Arial')

More information: http://en.wikipedia.org/wiki/Standard_deviation

5.8 Example 38: Saving Scorpion 3D Image

The example show how to store a 3D Image to a comma separated format. def SavePointCloud(ImageName, Filename): img=GetImageMatr(ImageName) f=open(Filename,'w') if img<>None: if img.elemtype()=='XYZf' : fmt='%f,%f,%f\n' #3 element pr. point elif img.elemtype()=='XYZWVf': fmt='%f,%f,%f,%f,%f\n' #5 element pr. point

if img.isvec() and img.elemtype()=='XYZWVf': cnt=img.dim()[0] for i in range(cnt): f.write(fmt% img[i]) elif img.ismat() and img.elemtype()=='XYZWVf': rows,cols= img.dim() for r in range(rows): offs=r*cols for c in range(cols): f.write(fmt% (img[offs+c])) f.close() f.close()

Example 1: Save 3D Point Cloud with 5 elements

SavePointCloud('3D','image.csv')

5.9 Example 39 - Disabling Zoom in Image Windows

The example shows how to disable zoom in an image windows. def Handle_System_MouseDown(Image,Shift,X,Y): # # Image = VT_BSTR (continues on next page)

40 Chapter 5. Examples 31-40 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) # Shift = VI_I4 # X = VT_R4 # Y = VT_R4 # # Return 1 if handled by script # Return 0 if default handling # return 1

5.10 Example 40 - Filtering timeseries

This example contains two use filters for arrays: def MedianFilter(x,n): # median filter of array x, length n # filter behaves pretty well at the edges (shortens the length as needed) def med(x): x.sort() return x[len(x)/2] # force odd filter order (better filters then) if n%2==0: n+=1 nh=n/2 l= len(x) p= list(x) for i in range(nh): v= x[0:n-nh+i] p[i]= med(v) for i in range(nh,l-nh): v= x[i-nh:i-nh+n] p[i]= med(v) for i in range(l-nh,l): v= x[i-nh:l] p[i]= med(v) return p def LowpassFilter(p,N,C): # N-tap running average, run C times l= len(p) if (N%2): N+=1 # N odd: zero phase NH=N/2 r= list(p) for c in range(C): lpad= [r[0] for i in range(NH)] rpad= [r[l-1] for i in range(NH)] rr= lpad+r+ rpad r=[sum(rr[i:i+N])/N for i in range(l)] return r

5.10. Example 40 - Filtering timeseries 41 SPE - Scorpion Python Examples Documentation, Release XI

42 Chapter 5. Examples 31-40 CHAPTER 6

Examples 41-50

6.1 Example 41: Scorpion Watchdog keep system running

This example documents how to communicate with the Resuscitator program to create a watchdog for both Scorpion and a Proscilica camera which is controlled by Scorpion. • The Resuscitator program - also named Scorpion Watchdog - is a program managing tool and is available on the Scorpion CD. * This program can control startup, shutdowns sequence and be a watchdog for all kinds of programs. • This example shows how to use it to implement a fast watchdog if a program stops capturing images. Requirements: • Python • Python for Windows Extentions • Resuscitator Scorpion is started and controlled by Resuscitator and must be closed when it is started from Resuscitator. • The Resuscitator configuration file - Resuscitator.ini - is placed in the profile base directory. Scorpion and Resuscitator will communicate as follows: Scorpion will write two lines in Resuscitator.ini file. They are stated below

[AliveStatus] status=1

In our example Resuscitator will check if these two lines has been written every 10 seconds. • If they are present in the file, Resuscitator will delete them. • If not, Resuscitator will restart scorpion. It is important that Scorpion writes the program lines above more frequently than they are written (for instance every second)

43 SPE - Scorpion Python Examples Documentation, Release XI

Scorpion Setup: Central Start def KickResuscitatior(applicationName, iniFileName) import win32api profile= GetStringValue('System.Profile') # when the code is executed, profile will contain a string with the the path of the

˓→current scorpion profile. win32api.WriteProfileVal('AliveStatus',applicationName,1,profile+' \\'+

˓→iniFileName) #for writing the lines to the file using the win32api (for details read the win32

˓→api)

The script which calls the KickResuscitator function will must be executed both when the Scorpion is IDLE, and when Scorpion is RUNNING. If RUNNING we call Grab action, which gets a snapshot from the camera. When the camera has sent the snapshot, the action AfterInspect will be trigged. In AfterInspect we’ll call the KickResuscitator function, which then only is trigged if the camera is working properly. In sytem state idle, we won’t check if the camera is operating correctly, and call the KickResuscitator function directly. We will use Scorpion actions and events to enable this. For more information on Scorpion actions and events, klick here. This example implements the strategy above by defining two actions in the actions tab, and one event in the scheduler: CameraTrigger and AfterInspect. After inspect is a system defined event, and should be filled with one new command; command = Script, parameters= KickResuscitator(‘Scorpion’,’ resuscitator.ini’). In the camera trigger action we have added two new commands. The first one has the command=Grab and a guard=running. The second one has command = Script, parameters= KickResuscitator(‘Scorpion’,’ resuscitator.ini’), and guard = Inverted running. In the scheduler create a new Event. Give it the following attributes; name: nextImage, command: cameraTrigger, frequency: ‘every nth millisecond’, period: 200 Resuscitator Setup: Place a shortcut to Resuscitator in the startup group, edit the properties:

Target : C:\Program Files\Tordivel AS\Resuscitator for NT\FindResuscitator.exe Start in:"C:\Program Files\Tordivel AS\Scorpion 7\Test Prosilica Watchdog

In the Resuscitator program press configure button, and edit the properties for each application:

Under configure for applications: Shutdown

Program path: C:\Program Files\TORDIVEL AS\Resuscitator for NT\SHUTDOWN.EXE Start in: C:\Program Files\TORDIVEL AS\Resuscitator for NT Select checkbox: Run at shutdown only

Under configure for applications: Server control parameters

Program path: C:\Program Files\Tordivel AS\Resuscitator for NT\FindResuscitator.exe Parameters:-topmost Select checkbox: Watch this program

Fig. 1: Find Resuscitator

Under configure for applications: Scorpion

44 Chapter 6. Examples 41-50 SPE - Scorpion Python Examples Documentation, Release XI

Program path: C:\Program Files\TORDIVEL AS\Scorpion6\Scorpion.exe Parameters: system="Test Prosilica Watchdog" #This is the profile we want to open

˓→with scorpion Start in: C:\Program Files\TORDIVEL AS\Scorpion6\ Select checkbox: Watch this Program Select checkbox: Alive check

Fig. 2: Scorpion Application Configuration

6.2 Example 42: Binary Search

Binary Search is a lot faster than linear search Standard linear search def linear_search(itemList, searchFor, startIndex=0, endIndex=None): for item in itemList: if item == searchFor: return item return None

Fast binary search def binary_search(itemList, searchFor, startIndex=0, endIndex=None): if endIndex is None: endIndex= len(itemList) while startIndex< endIndex: mid= (startIndex+ endIndex)//2 midval= itemList[mid] if midval< searchFor: startIndex= mid+1 elif midval> searchFor: endIndex= mid else: return mid return None

Test script import random import time testList= [n for n in range(10000000)] testValue= random.randint(0,10000000) startTime= time.time() print linear_search(testList, testValue) elapse= time.time()- startTime print"Linear [ms]", elapse * 1000.0 startTime= time.time() print binary_search(testList, testValue) elapse= time.time()- startTime print"Binary [ms]", elapse * 1000.0

6.2. Example 42: Binary Search 45 SPE - Scorpion Python Examples Documentation, Release XI

Console output

6757414 Linear [ms] 326.999902725 6757414 Binary [ms] 0.0

6.3 Example 43: Creating an ordered pointcloud

The following script will create an ordered 3D pointcloud from arrlib import xyzwvfMat from random import random cloud= xyzwvfMat(10,10) #1x10 matrix cloud.tag='Example43' #image/overlay name for r in xrange(10): for c in range(10): cloud[r*10+c]=r-5,c-5,random()-0.5,0,0 SetImageMatr('3DModel',cloud)

6.4 Example 44: UDP Socket Communication

Script collection demonstrating UDP socket communication in Scorpion. Central Start: import socket receivedFromAddress= None # will be set when receiving data receivePort= 1501 # listen on this port sendToAddress= (sendToIPAddress, sendToPort) # target for sending data udp= socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) udp.bind(('', receivePort)) udp.settimeout(0.01)

Central Stop: udp= None

Scheduler

# to be called at short intervals from a timer # ensure no time-consuming tasks are done inside this function. def Receive(): global udp global receivedFromAddress if udp == None: print'no udp object' return bufsize= 16384 try: data, receivedFromAddress= udp.recvfrom(bufsize) except socket.timeout: (continues on next page)

46 Chapter 6. Examples 41-50 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) return # no data waiting in buffer except Exception, err: print'recv exception:', err return print'Received %d bytes from'% len(data), receivedFromAddress #use the data string

Send Data def Send(data): global udp sendToIPAddress='127.0.0.1' sendToPort= 1500 sendToAddress= (sendToIPAddress, sendToPort) if udp<> None: try: udp.sendto(data, sendToAddress) except Exception, err: print'Send exception:', err return else: print'Send: No socket object'

6.5 Example 45: Creating an empty pointcloud

The following script will create an empty pointcloud from arrlib import xyzfVec cloud=xyzfVec(1) #min vector lengt = 1 cloud.tag='Example45' #image/overlay name cloud.setLength(0) #set length to 0 - no data SetImageMatr('3DModel',cloud)

6.5. Example 45: Creating an empty pointcloud 47 SPE - Scorpion Python Examples Documentation, Release XI

48 Chapter 6. Examples 41-50 CHAPTER 7

Using Arrlib from Python

• Version : Scorpion Vision Version X • Date: 12apr2016 • Written by: Arne Sommerfelt and Thor Vollset

7.1 Introduction

ArrLib is a library written in c for handling numerical arrays in an efficient way. Included is also a large number of basic and advanced functions for numeric-, geometric- and image-processing. The Scorpion Vision Software relies on Arrlib and includes it in the form of a dynamically linked library - ArrLib.dll. In order to give expert users easy access to ArrLib we have created the Python module called arrlibct. The module is available from Scorpion Vision X. This module gives access to the full (more or less) set of functions and data types found in arrlib. In addition arrlibct provides convenient ways to convert data to and from Numpy’s array type. The arrlibct module is based on the standard library module ctypes in Python,- from which we have the ‘ct’ suffix of our module name. Revision History: • 12apr2016, TV: * all arlibct samples • 29jan2016, TV: C – Modify layout - copyright notice – Add Script samples section • 08nov2012, AS: B - First release version

49 SPE - Scorpion Python Examples Documentation, Release XI

7.2 arrlibct versus pyArrlib

The Scorpion platform also supports an older binding to ArrLib. This python module is imported simply as arrlib, but we usually refer to it as pyArrlib in order to distinguish it from the c-library itself. We realize that some confusion may arise from supporting two very similar python bindings to the same underlying library. Our motivation for creating a new arrlibct python binding are the following: 1. Maintainability • arrlibct is a very direct mapping onto the underlying functions and data structures of arrlib. This allows us to automatically generate large portions of the interface. • As a result, more functionality is available for manipulating geometric objects than in pyArrlib, and it is constantly growing together with the underlying library. • arrlibct can be used to do fast prototyping and testing of new c-functions in arrlib. The resulting python code will have many similarities with the eventual pure C implementation. • With arrlibct it is possible to extend arrlib with user functions in C, compiled into separate DLL’s. These may be linked with arrlib to give a c-programming environment that is rich in functions and data types. • arrlibct only relies on the arrlib.dll, as opposed to pyArrlib which uses an intermediate python DLL (pyArrlib.pyd) as interface to the arrlib DLL. • Documentation for the c-library may be used directly since data types and functions have the same names. 2. Flexibility • arrlibct is more complete and consistent in in the way data types are accessed and manipulated. • Memory management is not tied to python, thus arrays created with arrlibct are not strictly owned python. They may be passed around and stored in user defined ways. • arrlibct has built in functions for conversion of data to and from pyArrlib and numpy so you can always switch from one to the other. Our recommendation is that arrlibct is used for all new projects, and that arrays are converted to pyArrlib format whenever they are passed as parameters to Scorpion. import arrlibct as al

# Assuming a 3D-camera like Sick IVP, this call will return a vector of XYZf: pyPoints= GetImageMatr(...)

# The al.Arr constructor creates an arrlibct object giving access to # the points in pyPoints. Note that pyPoints still owns the data. # It must not be deleted until you have finished using # the points from arrlibct. Otherwise an error will result. points= al.Arr(pyPoints) # Conversion to arrlibct,- data are not copied. p2= points *Q # Do your transformations p2= p2.toArrlib() # Convert back to pyArrlib SetImageMatr(p2)

# Plot 3D points #Replace '3DModel' with your own image name below: view=GetImageView().getImageViewer('3DModel') (continues on next page)

50 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) view.pointColor='yellow' view.addPoints(p2)

7.3 How to get help on arrlibct

The standard python help function will provide a description of the parameters and their type for all functions:

import arrlibct as al help(al.nearestPntOnLin3d)

output

Help on function nearestPntOnLin3d in module arrlibct: nearestPntOnLin3d(p, l) p: XYZd l: Lin3d returns: double

In addition to the standard help we have created a simple topic search function:

al.info('nearest')

output

Help on function nearestPntOnLin2d in module arrlibct: nearestPntOnLin2d(p, l) p: XYd l: Lin2d returns: double Help on function nearestPntOnLin2f in module arrlibct: nearestPntOnLin2f(p, l) p: XYf l: Lin2f returns: float Help on function nearestPntOnLin3d in module arrlibct: nearestPntOnLin3d(p, l) p: XYZd l: Lin3d returns: double Help on function nearestPntOnLin3f in module arrlibct: nearestPntOnLin3f(p, l) p: XYZf l: Lin3f returns: float

7.4 Data types in ArrLib and arrlibct

Arrlib defines an extensive set of basic and geometric data types. Below we have listed the types with their original c-names. The corresponding arrlibct name is constructed simply by prepending c_. This is in accordance with the convention used by the ctypes module. Note that all help text containing type names is given without the c_ prefix, the user must remember to always add this when using the type names in python.

7.3. How to get help on arrlibct 51 SPE - Scorpion Python Examples Documentation, Release XI

7.5 Fundamental types

Note that many of these types are identical to standard C-types. • Some of the types like uint, are just convenient shorthands for standard C-types. • The types with a number postfix are intended for use when it is important to control the word length and representation. For instance when transferring files or data to other platforms.

Type Description char Standard C-type, platform and compiler dependant short Standard C-type, platform and compiler dependant int Standard C-type, platform and compiler dependant long Standard C-type, platform and compiler dependant uchar unsigned char, platform and compiler dependant ushort unsigned short, platform and compiler dependant uint unsigned int, platform and compiler dependant ulong unsigned long, platform and compiler dependant int8 Two’s complement signed 8 bit integer int16 Two’s complement signed 16 bit integer int32 Two’s complement signed 32 bit integer int64 Two’s complement signed 64 bit integer uint8 Unsigned 8 bit integer uint16 Unsigned 16 bit integer uint32 Unsigned 32 bit integer uint64 Unsigned 64 bit integer float Standard C-type, platform and compiler dependant double Standard C-type, platform and compiler dependant ldouble long double, platform and compiler dependant float32 IEEE-754 standard 32 bit floating point number float64 IEEE-754 standard 64 bit floating point number Complexf Complex number: struct{float r,i;} Complexd Complex number: struct{double r,i;} Complex32 IEEE-754 standard 2x32 bit complex floating point number Complex64 IEEE-754 standard 2x64 bit complex floating point number ptr Generic pointer, same as void*, platform dependant arr Generic array pointer, platform dependant bits A chunk of bits. Used in bit-set arrays, Same as uint fix An integer used for fast fixed-point calculations. Binary point in the middle.

7.6 Small vectors

Note that corresponding vector types exist for double, int and fix. Their names are found by simply exchanging the type suffix ‘f’ with one of ‘d’, ‘i’ or ‘x’ respectively.

Type struct Description XYf struct{float x,y;} General-purpose 2-element float struct XYZf struct{float x,y,z;} General-purpose 3-element float struct XYZWf struct{float x,y,z,w;} General-purpose 4-element float struct XYZWVf struct{float x,y,z,w,v;} General-purpose 5-element float struct

52 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

7.7 Supported operations

The following are supported expressions for small vectors of float and double:

Expression Left operand Right operand Result a+b Small vector Same type as a Vector sum a-b Same type as a Vector difference -a Small vector Negate vector a*b Small vector Same type as a Scalar product a*b Small vector Scalar Vector scaling a*b Scalar Small vector Vector scaling a*b Small vector Small matrix Matrix multiplication a*b Small vector Pose Rigid-body transform a/b Small vector Pose Inverse rigid-body transform a*~b Small vector Pose Inverse rigid-body transform abs(a) Small vector Length of a

7.8 Small matrices

The matrix types exist for f=float and d=double. • M**nk**f • M**nk**d Where n and k are from 2 to 4.

Type struct M22f struct{ float m11,m12,m21,m22;} M33f struct{ float m11,m12,m13,m21,m22,m23,m31,m32,m33;} M43f struct{ float m11,m12,m13,m21,m22,m23,m31,m32,m33,m41,m42,m43;} M44f struct{ float m11,m12,m13,m14,m21,m22,m23,m24,m31,m32,m33,m34,m41,m42,m43,m44;} M22d struct{ double m11,m12,m21,m22;} M33d struct{ double m11,m12,m13,m21,m22,m23,m31,m32,m33;} M43d struct{ double m11,m12,m13,m21,m22,m23,m31,m32,m33,m41,m42,m43;} M44d struct{ double m11,m12,m13,m14,m21,m22,m23,m24,m31,m32,m33,m34,m41,m42,m43,m44;}

7.9 Supported operations

The following are supported expressions for small matrices of float and double:

7.7. Supported operations 53 SPE - Scorpion Python Examples Documentation, Release XI

Expression Left operand Right operand Result a+b Small matrix Same type as a Matrix sum a-b Same type as a Matrix difference -a Small vector Negate matrix a*b Small matrix Same type as a Matrix multiplication a*b Small matrix Scalar Matrix scaling a*b Scalar Small matrix Matrix scaling a*b Small vector Small matrix Matrix multiplication a*b Small vector Matrix extra row Homogeneous multiplication a/b Small vector Square matrix Matrix Division a*~b Small vector Square matrix same as a/b abs(a) Square matrix Determinant

7.10 Poses

These are similar to the 4x3 matrices above, but are meant to represent rigid body transformations. These are supported by a set of functions that exploit the fact that the matrix is unitary and thus trivial and singularity free to invert.

Type struct Description Pose3f struct{M33f U; XYZf t;} 3D rigid body transformation

7.11 Supported operations

The following are supported expressions for poses of float and double:

Expression Left operand Right operand Result a*b Pose Same type as a Rigid-body transform multiply a/b Pose Same type as a Inverse rigid-body multiply ~a Pose Inverse a*b Small vector Pose Rigid-body transform multiply a/b Small vector Pose Inverse rigid-body transform. a*~b Small vector Pose same as a/b a*b Line Pose Rigid-body transform multiply a/b Line Pose Inverse rigid-body transform. a*~b Line Pose same as a/b

7.12 Ranges

Note that corresponding range types exist for double, int and fix. Their names are found by simply exchanging the type suffix ‘f’ with one of ‘d’, ‘i’ or ‘x’ respectively.

Type struct Description Box1f struct{float min, max;} 1D range Box2f struct{XYf min, max;} 2D range Box3f struct{XYZf min, max;} 3D range

54 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

7.13 Parametric lines

Note that corresponding line types exist for double. Their names are found by simply exchanging the type suffix ‘f’ with ‘d’.

Type struct Description Lin2f struct{ XYf p,v;} 2D parameterized line with starting point p and direction vector v. Lin3f struct{ XYZf p,v;} 3D parameterized line with starting point p and direction vector v.

Some functions in Arrlib operate on 2D homogeneous lines. These are represented as small 3D vectors as listed above.

7.14 Supported operations

The following are supported expressions for lines of float and double:

Expres- Left Right Result sion operand operand a+b Line Scalar Parametric point at b on a - same as a.p+a.v*b a-b Line Scalar Parametric point at -b on a - same as a.p-a.v*b -a Line Reverse direction vector a*b Line Scalar Scale direction vector a*b Line Small matrix Matrix multiply as though line’s end points were separately multi- plied. a*b Line Pose Rigid-body transform multiply a/b Line Pose Inverse rigid-body transform. Same as a*~b abs(a) Line Length of line, same as abs(a.v)

7.15 Circles and spheres

The following datatypes are defined.

Type struct Description Cir2f struct{XYf c; float r;} A circle Cir3f struct{XYZf c; float r;} A sphere

7.16 Color pixels

The following datatypes are defined.

Type struct Description RGB struct{uint8 b,g,r,pad;} Red, green and blue color HSI struct{uint8 h,s; int16 i;} Hue, saturation and intensity.

RGB is compliant with with Windows bitmaps - 32 bit including 8 bit padding.

7.13. Parametric lines 55 SPE - Scorpion Python Examples Documentation, Release XI

7.17 Important note on garbage collection

There is no automatic recycling of allocated arrays behind the scenes in ArrLib. This is unlike python where objects are automatically deleted when they become unreferenced. ArrLib uses a simple and semi-automatic scheme that needs some attention by the programmer. The basic principles are: • All arrays are by default linked into a chain when they are allocated. • ArrLib maintains a stack of these chains. Only topmost chain is used at any particular time. • In a local scope, typically at the start of a function we start a new chain. • At the end of the function (or scope) we mark the arrays we want to keep and delete all others in the chain The typical code pattern is as follows: al.newArrChain() ... grid= al.gridPoints(a,b, False) keepArr(grid) deleteUnkept(1)

All arrays allocated after the call to newChain will be deleted by deleteUnkept except arrays that have been explicitly been marked for keeping by a call to keepArr, in this case only the array ‘grid’ will survive.

7.18 Geometric recipes

The operator overloadings described in the previous section lead to a number of simple solutions to basic geometric problems in the following we have tried to make a practical collection of examples.

7.19 Planar geometry

Assume that p,q,v are 2D points (or vectors) and l,l1,l2,m are 2D lines and M a 2x2 matrix. We use the float version of the data types in these examples. Make a point p= c_XYf(1,2) p= c_XYf([1,2])

Vector from p to q v=q-p

Make a line starting at p with direction v l= c_Lin2f(p,v) l= c_Lin2f([(1,2),(3,4)])

Make line from p to q l= c_Lin2f(p,q-p) l= pntToLin2f(p,q)

56 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

Unit vector from vector u= unitVec2f(v)

Length of vector s= vecLen2f(v)

Distance between two points s= pntDistance2f(p,q)

Starting point on a line p=l.p p=l+0

Endpoint of line q=l+1

Midpoint on line p=l+0.5

Changing the endpoints of a line l= resizeLin2f(l,start,end)

Make direction vector a unit vector l= normalizeLin2f(l)

Middle normal to line m= c_Lin2d(l+0.5,normalVec2f(l.v)) m= middleNormal2f(l)

Translate a point by addition p+=q

Translate a point by homogeneous transformation matrix p=p *xlat2f(q) p=p *xlat3f(c_XYZf(1,2,3))

Rotate and translate a point by homogeneous transformation matrix p=p *rot2f(pi/4)*xlat2f(q) p=p *rot3f(c_XYZf(0,0,pi/4))*xlat3f(q)

Scaling a vector by homogeneous transformation matrix p=p *scal2f(c_XYf(1,2)) p=p *scal3f(c_XYZf(1,2,3)) polygonCenterOfGravity2f

7.19. Planar geometry 57 SPE - Scorpion Python Examples Documentation, Release XI

x,y= polygonCenterOfGravity2f(polygon2f)

Constructing 3x3 transformation matrices

M= c_M33f(1,2,3,4,5,6,7,8,9) M= c_M33f([1,2,3,4,5,6,7,8,9]) M= c_M33f([[1,2,3],[4,5,6],[7,8,9]])

Special matrix constructions print c_M33f(1)

''' 1.00 0.00 0.00 0.00 1.00 0.00 0.00 0.00 1.00 ''' print c_M33f(1,2,3)

''' 1.00 0.00 0.00 0.00 2.00 0.00 0.00 0.00 3.00 '''

Rotation matrix constructed from the angle of a vector

M= vecRot2f(c_XYf(1,2))

Intersection of two lines t= c_XYf() # Allocate return variable for solution d= linIntersectLin2f(l1,l2,t) # The returned value is the determinant of the # underlying equation system # It will be non-zero if an intersection is found, i.e. # when lines are not parallel: if d: # t.x is the parameter position of the intersection on l1 # t.y is the parameter position of the intersection on l2 # When the parameter is in the range [0,1] the intersection # point is inside the line segment. Otherwise it is beyond # its ends. In any case the intersection point can be # computed by: p= l1+t.x # The intersection point

Nearest point on line t= nearestPntOnLin2f(p, l) # Finds parametric position on l q=l+t # The nearest point itself

Finding the intersection points of a circle and a line t1= c_float() # Allocate variables t2= c_float() # to be returned by reference d= intersectLinCir2f(l, cir, t1, t2) (continues on next page)

58 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) # d<0 : no intersection # d=0 : line is tangent and t1=t2 # d>0 : two intersections at l+t1 and l+t2

7.20 Working with homogeneous lines and points in the plane

The use of homogeneous coordinates provides a elegant and alternative way to work with points and lines in the plane. In homogeneous coordinates a 2D point (x,y) is represented by a 3D vector (x,y,1). Homogeneous lines have, unlike parametric lines, no notion of starting point or end point and are represented by a 3D vector (a,b,c) such that any point (x,y) on the line satisfies ax+by+c=0 Conversion between homogeneous and ordinary coordinates: p= hom2f(p) # Make 3D homogeneous vector from 2D point p= unhom3f(p) # Project 3D homogeneous vector back to 2D # NOTE: That if p.z=0 an error is generated # because p is at infinity.

Computing the homogeneous line through two points h= cross3f(p,q)

Computing the intersection point of two homogeneous lines p= cross3f(h1,h2) # If lines are parallel then p.z=0

Distance from a point to a line h= normalizeHomlin2f(h) # Makes (h.x,h.y) a unit vector d=h *p # Distance is now a simple scalar product

Bisecting non parallel lines. The lines will generally form one acute and one obtuse angle we have created one function for each case h= bisectAcuteHomlin2f(h1, h2) h= bisectObtuseHomlin2f(h1, h2)

Conversion to/from parametric lines l= homlinToLin2f(h) #Startpoint l.p is the point on h closest to origin h= linToHomlin2f(l)

Finding the intersection points of a circle and a line p1= c_XYf() # Allocate variables p2= c_XYf() # to be returned by reference d= intersectHomlinCir2f(hl, cir, p1, p2) # d<0 : no intersection # d=0 : line is tangent and p1=p2 # d>0 : two intersections p1,p2

7.20. Working with homogeneous lines and points in the plane 59 SPE - Scorpion Python Examples Documentation, Release XI

7.21 Fitting points to a straight line

In the examples below assume that a set of points has been read as a list of tuples from Scorpion, and then converted to an ArrLib array. Note that we use the numpy module as our main tool for array handling. Assume that numpy has been imported as np:

import numpy as np import arrlib as al points = np.asarray(GetResultValue(‘mytool.points’)).astype(np.float32) points = al.Arr(points ,’XYf’) # Convert numpy array to Arrlib array.

Least squares fitting of a set of points to a homogeneous line The rms parameter is optional, replace it with None when not needed.

rms= al.c_float() # Allocate variable to be returned by reference h= al.fitHomlin(points, rms) # points is an array of XYf

Robust fitting of a set of points to a homogeneous line using the RANSAC method The nGoodPoints parameter tells the function the minimum number points that may be assumed to be good (i.e. non-outliers). Since the algorithm is based on random sampling of point pairs without trying all combinations, there is a risk that a solution is never found. • This is controlled by the risk parameter. Setting the risk to 0.001 means that the search will have an average failure rate of 1/1000. • Setting the risk low results in a longer search and slower execution of the function. • The fitTolerance determines threshold between points that are considered inlier or outlier. • Remember that when reducing the fitTolerance , nGoodPoints should be decreased accordingly based on your á priori knowledge of the distribution. • This will also in general lengthen the search in order to maintain the same risk of failure.

rms = al.c_float() # Allocate variable to be returned by reference h = al.robustFitHomlin(points, nGoodPoints, risk, fitTolerance, None, rms) if h.x==0 and h.y==0: # A null normal vector means failure print ‘No line found’

There is an optional parameter that can be used to get information about the set of inlier points that were fitted to the line. • iFit and returns an index vector with the indices of the inlier points.

iFit= al.c_arr() # Allocate variable that will return inlier indices rms= al.c_float() # Allocate variable to be returned by reference h= al.robustFitHomlin(points, nGoodPoints, risk, fitTolerance, iFit, rms) if iFit.vecSize==0: # No points fitted means failure print'rms',rms.value else: inliers= al.indexedSubVec(p,iFit) # Make vector containing only inliers.

60 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

7.22 Transformation of normal vectors and homogeneous lines.

In general, to preserve perpendicularity of a normal vector n through a matrix multiplication it should be multiplied by the inverse transpose matrix. n=n *~transp33f(M)

We have created special functions for normal vector transformations n= muln2x33f(n, M)

Homogeneous lines are the projection of 3D plane normals, and should be transformed by the normal multiplication functions. h= muln3x33f(h,M)

7.23 3D space geometry

Many of the planar (2D) line functions described above are available in 3D. Simply change part of the function name from ..2f to ..3f for point-functions and ..Lin2f to ..Lin3f for line functions. A notable difference going from 2D to 3D is that there are no homogeneous lines in 3D. The closest analogy is planes, but unfortunately these do not support the same set of elegant and useful operations as homogeneous lines do.

7.24 3D point transformations

Below is an example on how to do basic transformations on 3D points using python and Scorpions built-in numerical library arrlib. import arrlibct as al # Import python bindings for arrlib points=[(1.2,3.4,4.5),(6.7,8.9,9.0)] # some points points= al.Arr(points,'XYZd') # Convert points to arrlib vector of (XYZd) point

˓→structures.

R= al.rotx33d(0.1) *al.roty33d(0.2)*al.rotz33d(0.3) # Create a rotation matrix from a ˓→series of primitive rotations t= al.c_XYZd(10,20,30) # A translation vector Q= al.c_Pose3d(R,t) # Construct a rigid-body transformation structure called a 'pose' p2= points *Q # Transform your points by post-multiplying with pose p2.prt() # Print contents of resulting arrlib array p3= p2 *~Q # Do reverse transformation p3.prt() # Print contents of resulting arrlib array print p3[1].x # The points may be accessed by index and field (x,y,z) p4= p3.toNumpy() # Convert arrlib array to numpy array print p4 p5= p4.tolist() # Convert from numpy array to python list print p5

7.22. Transformation of normal vectors and homogeneous lines. 61 SPE - Scorpion Python Examples Documentation, Release XI

7.25 Merging two point clouds in Scorpion import arrlibct as alc # Mark beginning of garbage collection scope. # To be used in Scorpion python script tools. # (The newArrChain/try/finally/deleteUnkept pattern below # is not needed in BaseTool scripts.) alc.newArrChain() try: # Get pointclouds from Scorpion 3D images as pyArrlib vectors A= GetImageMatr('3D-ImageA') B= GetImageMatr('3D-ImageB') # Convert to arrlibct arrays A= alc.Arr(A) B= alc.Arr(B) # If point cloud A needs to be aligned with B through a rigid-body # transformation this can be done as follows: # Create a rotation matrix from a series of primitive rotations R= alc.rotx33f(0.1) *alc.roty33f(0.2)*alc.rotz33f(0.3) t= alc.c_XYZf(10,20,30) # Make a translation vector Q= alc.c_Pose3f(R,t) # Make Pose Q,- a rigid-body transformation structure. A=A *Q # Transform point-cloud A by multiplying with the pose # Merge the points into a single vector # Make AB,- a new array with room for both clouds AB= alc.newVec('XYZf',len(A)+len(B)) # Copy A and B into AB alc.copyToSubVec(A,AB,0) alc.copyToSubVec(B,AB,len(A)) AB=AB.toArrlib() # Pass ownership of arrlibct array to pyArrlib # Put merged cloud back into a Scorpion 3D image. ok= SetImageMatr('3D-Merged',AB) print('failed','ok')[ok] finally: # Delete all arrlibct arrays allocated since # last call to newArrChain(). # Arrays given to pyArrlib, like AB above are unaffected. deleteUnkept(1)

Note: that arrlibct arrays can be printed by A.prt() method, but make sure that you turn on arrlib messages in Scorpion under General/Options/Show Arrlib messages. Alternatively you may want to se just the header info,- then use A.prh().

7.26 Intersecting lines in 3D

In general 3D lines do not intersect, but each line has a unique point that is closest to the other line. In the following example we compute the closest point on each line and also the parametric line between them. l1= c_Lin3f((-2,0,0),(4,0,0)) l2= c_Lin3f((1,1,1),(1,2,0)) t= c_XYf() k= c_Lin3f() print linIntersectLin3f(l1,l2,t,k), t , k

(continues on next page)

62 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) ''' 64.0 (0.63 -0.50) ((0.50 0.00 0.00) (0.00 0.00 1.00)) '''

The number returned by linIntersectLin3f is non-zero when the lines are not parallel and a valid solution is found. Note that t returns the parametric position of the points on each line. This can be used to compute the closest point on l1 and l2 as follows: print l1+t.x, l2+t.y

''' (0.50 0.00 0.00) (0.50 0.00 1.00) '''

The distance between the lines is computed by: print abs(k)

''' 1.0 '''

7.27 Python Script Samples

Sample 1 - Data conversion - Scorpion - arrlib - numpy import numpy A= arrlibct.Arr(ImageA).toNumpy() # Conversion to numpy arrays via arrlibct B= arrlibct.Arr(ImageB).toNumpy() AB= np.concatenate([A,B],axis=0) z= AB[:,2] ABfiltered= AB[(zMin

Sample 2 - Color Image to HSI import arrlibct as al im_pyarr=GetImageMatr('Image1') rgb= al.Arr(im_pyarr) hsi= al.rgb2hsiMatr(rgb,4) # Convert entire image x=100 y=200 print hsi[x,y] # or just convert a single pixel print al.rgb2hsiFast(rgb[x,y])

Sample 3 - Convert 2d tuple to Arr ‘XYf’ import arrlibct as al import numpy as np

(continues on next page)

7.27. Python Script Samples 63 SPE - Scorpion Python Examples Documentation, Release XI

(continued from previous page) if not isinstance(plg,al.Arr): plg= al.Arr(np.array(plg,np.float32),'XYf')

Sample 4 - Filtering items based on various property values Let us assume that we are working in the environment of a BaseTool in Scorpion. Furthermore assume that some GUI-elements have been defined and linked to a set of tag values of the tool. The tag names adhere to a naming convention as follows: Assume we have two properties called ‘length’ and ‘angle’. We would then have defined the tag values: • lengthMin • lengthMax • lengthMinEnable • lengthMaxEnable • lengthInvert • angleMin • angleMax • angleMinEnable • angleMaxEnable • angleInvert def getConstraints(propNames): ''' Retrieves constraints parameters from BaseTool GUI in a format suitable for use by propertyFilter. Input a list of property names that must be found among the user defined tag values of the enclosing tool. For each property name ‘prop’ there must be two scalar tags propMin,propMax, and three boolean tags propMinEnable, propMaxEnable and propInvert. If the property value should be converted by a function (e.g. from degrees to radians) the property can be specified as a tuple giving the name and the function (‘prop’,func). ''' constr= al.newVec('Box1f',len(propNames)) enabl= al.newVec('XYu',len(propNames)) invert= al.newVec('uint',len(propNames)) for i,pn in enumerate(propNames): if isinstance(pn,tuple): pn,fn=pn else: fn= lambda x:x

constr[i].min= fn(self.getDynamicFloat(pn+'Min',-np.inf)) constr[i].max= fn(self.getDynamicFloat(pn+'Max',+np.inf)) enabl[i].x= tool.getIntValue(pn+'MinEnable') enabl[i].y= tool.getIntValue(pn+'MaxEnable') invert[i]= tool.getIntValue(pn+'Invert') return constr,enabl,invert

Here is an example of the calling code

64 Chapter 7. Using Arrlib from Python SPE - Scorpion Python Examples Documentation, Release XI

# Some artificial data for the purpose of this example: lengths= al.vec_float(10,100,102,103,95) # Some properties in an array. angles= al.vec_float(0.01,-0.02,0.5,0.04,-0.01) # Some more properties.

# Collect all properties in an array of arrays. # Note: All arrays must have same length. properties= al.vec_arr(lengths,angles)

# Get the constraints from the GUI constr,enab,inv= getConstraints(('length',('angle',np.radians)))

# Do the filtering I= al.propertyFilter(properties,constr,enab,inv) # The result is an array of indices of the items that have survived # the filtering. To apply the filter you may do: lengths= lengths[I] # Keep only the angles= angles[I] # accepted items.

In the above case the data are grouped by property. In other cases the data a grouped by item, i.e. the properties of each item come in chunks. A common case is a 3D point of type XYZf. Below is a slightly contrived code snipped exemplifying this: prop=vec_XYZf((1,4,7),(2,5,8),(3,6,9)) # An array of three 3D points. filt= vec_Box1f((1,3),(5,7),(7,10)) # Constraints for the x-, y- and z-coord. enab= vec_XYu((1,1),(1,1),(1,1)) # Three pairs of enable switches. inv= vec_uint(0,0,0) # No inversion. Setting inv=None does the same. I= propertyFilter(prop,filt,enab,inv)

NOTE: That propertyFilter currently is only implemented for float data.

7.27. Python Script Samples 65 SPE - Scorpion Python Examples Documentation, Release XI

66 Chapter 7. Using Arrlib from Python CHAPTER 8

Using ScorpionOpenCV

Image Filtering in Scorpion

Fig. 1: Normalize is part of ImageFilter ScorpionOpenCV is threaded

ScorpionOpenCV key features • multithreaded • uses on Scorpion Arrlib structures In Scorpion 12 - the Scorpion Tool Component - STC-0135-ImageFilter2 is an indispensable package based on OpenCV, ScorpionOpenCV and Numpy.

8.1 Introduction

ScorpionOpenCV is a Scorpion encapsulation of OpenCV 1.1, primarily filter functions. These filters are a valuable part of OpenCV that is a part of the Scorpion Vision Software framework. ScorpionOpenCV is multithreaded while standard OpenCV 1.1 single threaded. Working with large images the per- formance gain is significant. The following methods are implemented: • imageFilter • median

67 SPE - Scorpion Python Examples Documentation, Release XI

• smooth • canny • imageMerge ScorpionOpenCV works on Scorpion Arrlib’s data structures, there is no need for transferring Scorpion structures to Numpy as for standard OpenCV. The python module ScorpionOpenCV.py is distributed with the Scorpion Vision installer and is located in the Python sub-folder.

8.2 Image filters

Scorpion Vision supports a image filter command strings to perform image filtering to an image, either pre-filter or post-filter. These filters was first invented in the STC-0011-ImageFilter. This is STC or Scorpion Tool Components. The filter commands strings make it easy to create customs filter where parameter settings and filter sequence are defined by command string. c40,200,3m3 # canny followed by a median smooth filter

These filters are supported by various tools like TemplateFinder3 and ColorSegmentor. The filter format is given by a command string where filter type is given by a single letter followed by one or more parameters.

<,p2><,pn>

Multiple filters can be combined in any sequence. Some filters are optimized by threading, this applies mainly for larger images (>VGA) and may be tuned due to image size, filter aperture and no of cpu’s.

8.2.1 Supported filters:

The following subset of filters are supported by ScorpionOpenCV imageFilter command.

Filter Pa- rameters Canny c<,threshold2=200><,aperturesize=3><,threads=4> Dilate d<,threads=n> # example - d2 Erode e<,threads=n> # example - e4 Close C<,threads=n>,# example - C2 Open O<,threads=n> # example - O4 Laplace l<,threads=n>, # example - l3 Adap- t<,adaptive_method=MEAN(0)><,thresh_type=BIN(0)><,block_size=3><,param1=5> tiveThresh- old Gaussian g<,size2=0><,sigma1=0><,sigma2><,threads=n> Blur b<,threads=n> Median m<,threads=n> Sobel s<,yorder=1><,aperture_size=3><,threads=n> Threshold H<,maxvalue=255><,threshold_type=0><,threads=n> Normalize N<,scale=255><,percentile=50><,threads=n> percentile is currently fixed at 50 but has to be specified if threads is to be specified Threads ‘|’ no of threads used for following commands, ‘|’ - PIPE chacter. All functions uses by default 4 threads

68 Chapter 8. Using ScorpionOpenCV SPE - Scorpion Python Examples Documentation, Release XI

8.3 ImageFilter examples

Example 1: Canny and erode, using 8 threads for erode c40|8e2

Example 2: Image blur b5

Example 3: Adaptive Threshold t125,0,0,5

Example 4: Normalize image

N101,200,50

8.4 Code examples

Example 1 - Using imageFilter inside a BaseTool The example is from the Scorpion Vision Toolbox inside the BaseTool’s execute method. It is required that the tool has implemented a data input configuration • Filter # contains image filter string • OutputImage # contains image filter destination image def execute(self,image): # use self.name to get this tool instance # tool=GetTool(self.name) # image parameter is a python image matrix due to ROI # Return 1 for pass, 0 for fail import ScorpionOpenCV tool=GetBaseTool(self.name) #get this tool instance dst=image #allocate destination of same type and size -

˓→critical ScorpionOpenCV.imageFilter(tool.arrState,image,dst,tool.getString('Filter')) SetImageMatr(tool.getString('OutputImage'),dst) return 1

8.3. ImageFilter examples 69 SPE - Scorpion Python Examples Documentation, Release XI

70 Chapter 8. Using ScorpionOpenCV CHAPTER 9

Release notes

Documentation release notes

0.9.0.3,02may2016, RL: add example 45

0.9.0.3,09apr2016, RL: added Using ScorpionOpenCV

0.9.0.2,31dec2015, TV: created added all python examples from Scorpion Help Web added Using Arrlib from Python

71 SPE - Scorpion Python Examples Documentation, Release XI

72 Chapter 9. Release notes CHAPTER 10

Indices and tables

• genindex • modindex • search

73