This tutorial illustrates how to make a standalone python program, or program script, that can also be used like a linux command or as a python module. First, basic file formats used in the python-verse are outlied, then the develeopment of a python script with v4 command line pasrsing is presetned. Then, the modification of this program for use as a linux commadn and as a python module is presented.
Conventional python programs and modules are contained in text files with a .py extension. The following popular variations to this with their assoicated file extensions are given below:
Container | File Extension | Description |
---|---|---|
Python | .py | convetnional python program or script |
Ipython | .ipy | interactive python program. The Ipython interpreter is useful for develoing python programs in a terminal environment. Ipython supports the command extension terminal execution "!" and magic commands with the prefix "%" that are useful for interactive program development. |
Jupyter Notebook | .ipynb | jupyter Ipython workspace, may contain Ipython program cells, makdown cells, variable values and a progam execution state |
Liunx command | (optional) | A conventional python program (.py) whith two modifications: (a) a first line python comment to inform Linux that the program is a python script and that it is to be executed with the python interpreter and (b) an execution file setting that informs Linux that the file is a script that can be executed. Linux command execution ignores file name extensions |
Note, standalone programs that are executed by the python interpreter such as conventional Python programs or Linux commands, should not contain the command extensions of Ipython that are also available in Jupyter Notebooks.
Python programs can be written as independent commands (like other basic Linux commands). These progams do not require an interactive environemnt (like jupyter notebook). In this tutorial, a simple application (vcorner) is used to illustrate the development of a standalone program. The program is discussed with the following sections:
Following the program description the convertion to a Linux/macOS command is descibed and the full progam is shown
All python progarm should start with a program description. For packages, this program only requires the v4.vx module and numpy.
"""
vcorner.py: copy (crop) the lower left corner of a byte image
This demonstration program reads a single byte image and
returns a cropped part of it selected from the lower left corner
The size of the returned image may be dspecified on the command line.
The default size is 10 x 10 pixels
"""
import numpy as np
from v4 import vx
Python provides command line argument values in the order specified in a list called sys.argv. Any standalone program should provide a convenient mechanism for argument specification such as python argparse or v4 vaparse. Both of these provide a mechanism for documenting the parameters that the program accepts.
# parse command parameters
vargs = vx.vaparse("if= of= s= -")
if '-' in vargs:
print ("vcorner: copy lower left corner")
print ("if= input file")
print ("of= output file")
print ("s= size of the result (default 10)")
exit()
$ python vcorner.py -vcorner: copy lower left corner
if= input file
of= output file
s= size of result (default 10)
#read input image
if 'if' in vargs:
inimage = vx.Vx(vargs['if'])
else:
print ( 'vcorner error: if= must be specified')
exit()
im = inimage.i
if im.dtype != 'uint8' :
print ("vcorner error: image not byte type", file=sys.stderr)
exit(1)
# check for output file name
if 'of' not in vargs:
print ( 'vpcrop error: of= must be specified')
exit()
# set output image size
if 's' in vargs:
s = vargs['s']
else:
s = 10
# Compute offset to get lower corner
yoffset = np.shape(im)[0] - np.shape(tm)[0]
for y in range(tm.shape[0]):
for x in range(tm.shape[1]):
tm[y][x] = im[y+yoffset][x]
# Write the output file
tmimage.write(vargs['of'])
Note, the two 'for loop' method presented above is for individual pixel exposition. Python programmers would, in general, choose to use a vector or array operation that may be more efficieint, such as:
tmimage.i = np.copy(im[yoffset:,:tm.shape[1]])
The usual way to excute a python program is to run it with python, for example:
python vcorner.py if=infile of=outfile
If we convert the program to a linux command then the command would be:
vcorner if=infile of=outfile
Conversion is accoplished by changing the file name (optional), adding a comment line to the begining of the program, and changing the program file persmission setting. In detail, these three steps are:
Remove .py from the program name, for example with the Linux mv command:
$ mv corner.py vcorner
Add the following comment to the very first line of the program
#!/usr/bin/env python
Modify the file permisions of the program file so that the system recognises it as a file that can be executed as a program with the Linux chmod command:
$ chmod +x vcorner
The first step, to change the file name to the style of linux commands, is optional. Once steps 2 and 3 have been taken the program can be givn any name with any extension, including .py, and it will still behave as a command.
#create a test image
testim = vx.Vx()
testim.i = np.reshape(np.arange(70, dtype='uint8'), (7, 10))
print (testim .i)
[[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19] [20 21 22 23 24 25 26 27 28 29] [30 31 32 33 34 35 36 37 38 39] [40 41 42 43 44 45 46 47 48 49] [50 51 52 53 54 55 56 57 58 59] [60 61 62 63 64 65 66 67 68 69]]
# Testing
result = vx.Vx()
size=4
exec(vx.vxsh( 'vcorner if=$testim of=$result s=$size' ))
print(result.i)
size=6
exec(vx.vxsh( 'vcorner if=$testim of=$result s=$size' ))
print(result.i)
[[30 31 32 33] [40 41 42 43] [50 51 52 53] [60 61 62 63]] [[10 11 12 13 14 15] [20 21 22 23 24 25] [30 31 32 33 34 35] [40 41 42 43 44 45] [50 51 52 53 54 55] [60 61 62 63 64 65]]
#!/usr/bin/env python
"""
vcorner.py: copy (crop) the lower left corner of a byte image
This demonstration program reads a single byte image and
returns a cropped part of it selected from the lower left corner.
The size of the returned image may be dspecified on the command line.
The default size is 10 x 10 pixels
"""
import numpy as np
from v4 import vx
# parse command parameters
vargs = vx.vaparse("if= of= s= -")
if '-' in vargs:
print ("vcorner: copy lower left corner")
print ("if= input file")
print ("of= output file")
print ("s= size of result (default 10)")
exit()
# read input image
if 'if' in vargs:
inimage = vx.Vx(vargs['if'])
else:
print ( 'vcorner error: if= must be specified')
exit()
im = inimage.i
if im.dtype != 'uint8' :
print ("vcorner error: image not byte type", file=sys.stderr)
exit(1)
# check for output file name
if 'of' not in vargs:
print ( 'vcorner error: of= must be specified')
exit()
# set output image size
if 's' in vargs:
s = int(vargs['s'])
else:
s = 10
# create output image with the size of the selected region
tmimage = vx.Vx()
tm=np.zeros((s,s), dtype='uint8')
tmimage.i = tm
# Compute offset to get lower corner
yoffset = np.shape(im)[0] - np.shape(tm)[0]
for y in range(tm.shape[0]):
for x in range(tm.shape[1]):
tm[y][x] = im[y+yoffset][x]
# Write the output files
tmimage.write(vargs['of'])
The current program contains two primary componnets (a) a program fucntion that performs computation on image data and (b) a command line interface that parses the command line paramters, reads the input data, and writes the results. It may be useful to make the computation functions available to other python programs as a "library". The standard python module organiztion may be used to achieve this.
First we need to partition the program into the two primary components and then only execute the command line interface if the prorgam is not being loaded as a module. Teh interface should only be executed when the program is being used as a python script or a linux command. The overall program structure to provide script, linux command, and module use options is as follows:
#!/usr/bin/env python
# first line is required for use as a Linux command
def main():
""" The command line interface goes here """
. . .
def corner ():
""" The program processing function goes here """
. . .
# Last two lines are neeeded to prevent command line
# "main()" execution when used as a python module
if __name__ == "__main__":
main()
For our simple demonstration program, the action function may be rewrtiten as a simple function "corner()" that only involves np arrays as shown below:
def corner(im, size):
""" select the left lower corner of an np array
Args:
im (np-array): The input image
size (int): The size of each side of the result array
Returns:
tm (np array): of size s x s and type unit8.
"""
tm=np.zeros((size,size), dtype='uint8')
# Compute offset to get lower corner
yoffset = np.shape(im)[0] - np.shape(tm)[0]
for y in range(tm.shape[0]):
for x in range(tm.shape[1]):
tm[y][x] = im[y+yoffset][x]
return tm
An example of using this modified progarm as a python module is shown below:
import vcorner as vc
im = np.reshape(np.arange(0, 15, dtype='uint8'), (5,3), order='C')
print(im)
[[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11] [12 13 14]]
om = vc.corner(im, 2)
print(om)
[[ 9 10] [12 13]]
#!/usr/bin/env python
""" vcorner.py: copy (crop) the lower left corner of a byte image
This demonstration program reads a single byte image and
returns a cropped part of it selected from the lower left corner.
The size of the returned image may be specified on the command line.
The default size is 10 x 10 pixels
"""
import numpy as np
from v4 import vx
def main ():
# parse command parameters
vargs = vx.vaparse("if= of= s= -")
if '-' in vargs:
print ("vcorner: copy lower left corner")
print ("if= input file")
print ("of= output file")
print ("s= size of result (default 10)")
exit()
# read input image
if 'if' in vargs:
inimage = vx.Vx(vargs['if'])
else:
print ( 'vcorner error: if= must be specified')
exit()
im = inimage.i
if im.dtype != 'uint8' :
print ("vcorner error: image not byte type", file=sys.stderr)
exit(1)
# check for output file name
if 'of' not in vargs:
print ( 'vpcorner error: of= must be specified')
exit()
# set output image size
if 's' in vargs:
s = vargs['s']
else:
s = 10
tmimage = vx.Vx()
#compute the corner function
tmimage.i = corner(im, s)
# Write the output file
tmimage.write(vargs['of'])
def corner(im, size):
""" select the left lower corner of an np array
Args:
im (np-array): The input image
size (int): rTthe size of each side of the result array
Returns:
tm (np array): of size s x s and type unit8.
"""
tm=np.zeros((size,size), dtype='uint8')
# Compute offset to get lower corner
yoffset = np.shape(im)[0] - np.shape(tm)[0]
for y in range(tm.shape[0]):
for x in range(tm.shape[1]):
tm[y][x] = im[y+yoffset][x]
return tm
if __name__ == "__main__":
main()