Background
I've got OpenCV & Python installed on a Docker container which is on a http server to process images and serve them. The user interface is through the web; user inputs an image and some values; the values are collected through a form and the values and image paths (hashed input and output paths) are sent through JSON to a bash script which runs the Python code. There are two Python scripts; one which does the JSON input processing (cspaceIO.py
), and the main processing module (cspaceFilter.py
) which is only called by the first script.
Questions
- Is there anything here that screams "bad idea" with the flow of this approach?
- Do I need to worry about anything security-wise that my
_sanitize()
function doesn't help with? - How do the python scripts look on their own?
I/O script cspaceIO.py
import argparse # command line inputs
import cspaceFilter # running the algo
import cv2 # for checking the image
import json # for i/o
"""Validator functions"""
def _sanitize(filename):
sanitized = "".join(
[c for c in filename
if c.isalpha() or c.isdigit() or c in ['.', '_', '-', '/']]).rstrip()
return sanitized
def _checkimg(imgPath):
img = cv2.imread(imgPath)
if img is None:
invalidmsg = ("%s is an invalid image filename, "
"did not load image." % imgPath)
raise argparse.ArgumentTypeError(invalidmsg)
return img
def _checkcspace(cspaceLabel):
validLabels = ['BGR', 'HSV', 'HLS', 'Lab',
'Luv', 'YCrCb', 'XYZ', 'Grayscale']
if cspaceLabel not in validLabels:
invalidmsg = ("{0} is an invalid colorspace, must be one of: "
"{1}, {2}, {3}, {4}, {5}, {6}, {7}, or {8}."
).format(cspaceLabel, *validLabels)
raise argparse.ArgumentTypeError(invalidmsg)
"""Command line parsing"""
if __name__ == "__main__":
"""To be ran from command line
Usage example:
python3 cspaceIO.py '{
"paths":
{
"srcPath":"input/test.png",
"maskPath":"output/test.png",
"maskedPath":"output/test2.png"
},
"cspaceLabel":"BGR",
"sliderPos":[127,255,127,255,127,255]
}'
"""
parser = argparse.ArgumentParser(
description='Color threshold an image in any colorspace \
and save it to a file.')
parser.add_argument('jsonIn', help='JSON containing paths \
(dict {imgPath (str), maskPath (str), maskedPath (str)}), \
cspaceLabel (str), and sliderPos (6-long int list[])')
args = parser.parse_args()
# grab inputs from json
jsonIn = json.loads(args.jsonIn)
paths = jsonIn['paths']
srcPath = paths['srcPath']
maskPath = paths['maskPath']
maskedPath = paths['maskedPath']
cspaceLabel = jsonIn['cspaceLabel']
sliderPos = jsonIn['sliderPos']
# check inputs
_checkcspace(cspaceLabel)
srcPath = _sanitize(srcPath)
maskPath = _sanitize(maskPath)
maskedPath = _sanitize(maskedPath)
img = _checkimg(srcPath)
# run the colorspace filter script
mask, masked_img = cspaceFilter.main(img, cspaceLabel, sliderPos)
# write the output image
cv2.imwrite(maskPath, mask)
cv2.imwrite(maskedPath, masked_img)
Image processing module cspaceFilter.py
import cv2
import numpy as np
"""Helper functions"""
def _cspaceSwitch(img, cspaceLabel):
"""Coverts the colorspace of img from BGR to cspaceLabel
Keyword arguments:
img -- the image to convert
cspaceLabel -- the colorspace to convert to
Returns:
img -- img with the converted colorspace
"""
if cspaceLabel == 'BGR':
return img
convert_code = {
'HSV': cv2.COLOR_BGR2HSV,
'HLS': cv2.COLOR_BGR2HLS,
'Lab': cv2.COLOR_BGR2Lab,
'Luv': cv2.COLOR_BGR2Luv,
'YCrCb': cv2.COLOR_BGR2YCrCb,
'XYZ': cv2.COLOR_BGR2XYZ,
'Grayscale': cv2.COLOR_BGR2GRAY}
img = cv2.cvtColor(img, convert_code[cspaceLabel])
return img
def _cspaceBounds(cspaceLabel, slider_pos):
"""Calculates the lower and upper bounds for thresholding a
colorspace based on the thresholding slider positions.
Keyword arguments:
cspaceLabel -- the colorspace to find bounds of; see keys in main()
slider_pos -- positions of the thresholding trackbars; length 6 list
Returns:
lowerb -- list containing the lower bounds for each channel threshold
upperb -- list containing the upper bounds for each channel threshold
"""
if cspaceLabel is 'Grayscale':
lowerb, upperb = slider_pos[0], slider_pos[1]
else:
lowerb = np.array([slider_pos[0], slider_pos[2], slider_pos[4]])
upperb = np.array([slider_pos[1], slider_pos[3], slider_pos[5]])
return lowerb, upperb
def _cspaceRange(img, cspaceLabel, lowerb, upperb):
"""Thresholds img in cspaceLabel with lowerb and upperb
Keyword arguments:
img -- the image to be thresholded
cspaceLabel -- the colorspace to threshold in
Returns:
mask -- a binary image that has been thresholded
"""
img = _cspaceSwitch(img, cspaceLabel)
mask = cv2.inRange(img, lowerb, upperb)
return mask
def _applyMask(img, mask):
"""Applies a mask to an image
Keyword arguments:
img -- the image to be masked
mask -- the mask (non-zero values are included, zero values excluded)
Returns:
masked_img -- the input img with mask applied
"""
masked_img = cv2.bitwise_and(img, img, mask=mask)
return masked_img
"""Main public function"""
def main(img, cspaceLabel, slider_pos):
"""Computes the colorspace thresholded image based on
slider positions and selected colorspace.
Inputs:
img -- input image
cspaceLabel -- colorspace to filter the image in
slider_pos -- positions of the six sliders (6-long int list)
returns
mask -- mask created from thresholding the image
masked_img -- masked image
"""
lowerb, upperb = _cspaceBounds(cspaceLabel, slider_pos)
mask = _cspaceRange(img, cspaceLabel, lowerb, upperb)
masked_img = _applyMask(img, mask)
return mask, masked_img