# This file is part of Sympathy for Data.
# Copyright (c) 2018, Combine Control Systems AB
#
# SYMPATHY FOR DATA COMMERCIAL LICENSE
# You should have received a link to the License with Sympathy for Data.
from sympathy.api import node
from sympathy.api.nodeconfig import Port, Ports, Tag, Tags
import numpy as np
import os
import os.path as path
import importlib.metadata
from sylib.imageprocessing.image import ImagePort
from sympathy.utils.pip_util import import_optional
def _cv2():
return import_optional("cv2", group="opencv")
[docs]
class HaarCascade(node.Node):
name = 'Haar Cascade'
author = 'Mathias Broxvall'
nodeid = 'com.sympathyfordata.imageanalysis.haar_cascade'
icon = 'image_haarcascade.svg'
description = (
'Uses one of the included pre-trained Haar-cascade classifiers '
'to recognise pre-defined objects such as faces, eyes, or cats. '
'You can use a custom created Haar cascade described by an XML '
'file by using the optional input datasource port instead. '
'For details see offical OpenCV Haar training tutorials and the '
'opencv_traincascade tool.'
)
tags = Tags(Tag.ImageProcessing.Segmentation)
# TODO: These keys intentionally start with a space. This should be fixed
# in a migration.
CLASSIFIERS = {
" eye": "haarcascade_eye.xml",
" eye tree eyeglasses": "haarcascade_eye_tree_eyeglasses.xml",
" frontalcatface": "haarcascade_frontalcatface.xml",
" frontalcatface extended": "haarcascade_frontalcatface_extended.xml",
" frontalface alt": "haarcascade_frontalface_alt.xml",
" frontalface alt2": "haarcascade_frontalface_alt2.xml",
" frontalface alt tree": "haarcascade_frontalface_alt_tree.xml",
" frontalface default": "haarcascade_frontalface_default.xml",
" fullbody": "haarcascade_fullbody.xml",
" lefteye 2splits": "haarcascade_lefteye_2splits.xml",
" license plate rus 16stages": "haarcascade_license_plate_rus_16stages.xml",
" lowerbody": "haarcascade_lowerbody.xml",
" profileface": "haarcascade_profileface.xml",
" righteye 2splits": "haarcascade_righteye_2splits.xml",
" russian plate number": "haarcascade_russian_plate_number.xml",
" smile": "haarcascade_smile.xml",
" upperbody": "haarcascade_upperbody.xml",
}
parameters = node.parameters()
parameters.set_string(
'classifier', value=list(CLASSIFIERS.keys())[0], label='Classifier',
editor=node.editors.combo_editor(options=list(CLASSIFIERS.keys())),
description=(
'Select a pre-trained Haar-cascade classifier to use'))
__doc__ = description
inputs = Ports([
ImagePort('image for object recognition', name='image'),
Port.Custom(
'datasource',
'Pre-trained xml file',
name='xml',
n=(0, 1, 0)
),
])
outputs = Ports([
Port.Custom('table', 'Result', name='result'),
])
def execute(self, node_context):
params = node_context.parameters
image = node_context.input['image'].get_image()
out_table = node_context.output['result']
selected_classifier = params['classifier'].value
xml_datasource = node_context.input.group('xml')
if np.issubdtype(image.dtype, np.floating):
image = (image * 255)
image = image.astype(np.uint8)
if len(image.shape) == 3 and image.shape[2] == 3:
gray = _cv2().cvtColor(image, _cv2().COLOR_BGR2GRAY)
else:
gray = image
if len(xml_datasource) > 0:
classifier_path = xml_datasource[0].decode_path()
else:
_CV2_ROOT = importlib.metadata.distribution('opencv-python').locate_file('cv2')
_CV2_DATAROOT = path.join(_CV2_ROOT, 'data')
classifier_path = os.path.join(
_CV2_DATAROOT, HaarCascade.CLASSIFIERS[selected_classifier])
print("Using classifier: ", classifier_path)
face_cascade = _cv2().CascadeClassifier(classifier_path)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
if len(faces) == 0:
out_table.set_column_from_array('x', np.array([]))
out_table.set_column_from_array('y', np.array([]))
out_table.set_column_from_array('w', np.array([]))
out_table.set_column_from_array('h', np.array([]))
else:
data = np.array(faces)
out_table.set_column_from_array('x', data[:, 0])
out_table.set_column_from_array('y', data[:, 1])
out_table.set_column_from_array('w', data[:, 2])
out_table.set_column_from_array('h', data[:, 3])