Examples 21-30

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 access 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 <blue><green><red><pad>.

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
**************************************************************************/

#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;
  int n = rows*cols;
  int i;
  for (i=0;i<n;i++) sum += *image++;
  return sum/n;
}

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:
        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')

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.
  • 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

  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'))

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
_images/laser_profile_calibration.gif

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')

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
_images/rotated_circle.gif
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 :
    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))

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

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"

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.
  • The check locations are stored in an ExternalPolygon tools.
_images/example28.gif
#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)
Example 1: 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

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

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.

# 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)