4
\$\begingroup\$

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

  1. Is there anything here that screams "bad idea" with the flow of this approach?
  2. Do I need to worry about anything security-wise that my _sanitize() function doesn't help with?
  3. 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
\$\endgroup\$
1
\$\begingroup\$
sanitized = "".join(
    [c for c in filename
     if c.isalpha() or c.isdigit() or c in ['.', '_', '-', '/']]).rstrip()

Just a small improvement - as strings are sequences, you may change it to

sanitized = "".join(
    [c for c in filename
         if c.isalpha() or c.isdigit() or c in "._-/"])

( .rstrip() is superfluous as you don't allow whitespaces.)

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Great catch there, pushing straight to development with that one. \$\endgroup\$ – alkasm Aug 27 '17 at 23:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.