Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

Overview

Say there’s an existing service that does almost everything you want it to, but there’s some slight changes you wish you could make. Luckily, SIC’s object-oriented architecture enables you to inherit from and extend the component you wish to modify. Let’s take the ‘detect’ function of the FaceDetectionComponent, for example:

    def detect(self, image):
        img = array(image).astype(np.uint8)

        gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

        faces = self.faceCascade.detectMultiScale(
            gray,
            scaleFactor=1.2,
            minNeighbors=5,
            minSize=(int(self.params.minW), int(self.params.minH)),
        )

        faces = [BoundingBox(x, y, w, h) for (x, y, w, h) in faces]

        return BoundingBoxesMessage(faces)

In this function the ‘scaleFactor’ and ‘minNeighbors’ variables are hidden within the function, meaning the only way to change them is to rewrite the function. However, rather than rewriting the whole entire FaceDetectionComponent, we can simply create a new component where we inherit from the FaceDetectionComponent, and change just the detect function.

Outline

To extend an existing component we will go through the following steps:

  1. Identify the component we wish to change.

  2. Create a new script called ‘custom_{COMPONENT_NAME}.py'.

It is recommended to create a ‘custom_components’ folder within your repo to keep track of these.

  1. Import the original component and relevant modules in the new custom component.

  2. Extend the original component, rewrite the functions you wish to change.

  3. Change the SICConnector and SICComponentManager to match the new component name.

  4. Import the new connector for the custom component in your scripts that use it.

  5. Run the new custom component in place of the old one.

Setup (steps 1 and 2)

First, we have already decided we are going to change the ‘detect’ function of the FaceDetectionComponent. So secondly, we create a ‘custom_components’ folder in our repo if we do not have one already, and within that add a ‘custom_{COMPONENT}’ script:

image-20241114-155512.png

Writing the new component (steps 3, 4, and 5)

Inside the new custom component script, we import the old component, as well as the SICComponentManager, SICConnector, and anything the new custom component may need:

from sic_framework.services.face_detection.face_detection import FaceDetectionComponent  # Import the original component
from sic_framework.core.component_manager_python2 import SICComponentManager
from sic_framework.core.connector import SICConnector

# additional modules used by custom function
import cv2
import numpy as np
from numpy import array
from sic_framework.core.message_python2 import (
    BoundingBox,
    BoundingBoxesMessage,
)

We then extend the original component by instantiating a new class and rewriting just the functionality we wish to change:

class CustomFaceDetectionComponent(FaceDetectionComponent):
    def __init__(self, *args, **kwargs):
        super(CustomFaceDetectionComponent, self).__init__(*args, **kwargs)
        self.scaleFactor = 1.2
        self.minNeighbors = 3

    def detect(self, image):
        # Override the detect function with custom behavior
        img = array(image).astype(np.uint8)

        gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

        faces = self.faceCascade.detectMultiScale(
            gray,
            scaleFactor=self.scaleFactor,  # Example of different scale factor
            minNeighbors=self.minNeighbors,    # Example of different minNeighbors
            minSize=(int(self.params.minW), int(self.params.minH)),
        )

        faces = [BoundingBox(x, y, w, h) for (x, y, w, h) in faces]

        return BoundingBoxesMessage(faces)

Next, each SICComponent needs a SICConnector and a main() function that calls the SICComponentManager, so we add this after we have instantiated the class:

class CustomFaceDetection(SICConnector):
    component_class = CustomFaceDetectionComponent # make sure to change the component name here

def main():
    # register the custom component in the component manager
    SICComponentManager([CustomFaceDetectionComponent])

if __name__ == "__main__":
    main()

Using the new component (step 6)

The work on the new custom component is done. Now, you must change the scripts that you want to use the new component. Here the demo_desktop_camera_facedetection.py is used as an example.

First, make sure your script is able to find your new component. Here is one way of doing so:

import sys
from pathlib import Path
# Get the absolute path to the custom_services folder
custom_services_path = Path(__file__).resolve().parent.parent.parent / "custom_components"
sys.path.append(str(custom_services_path))

Alternatively, you can append the ‘custom_components' folder path to the PYTHONPATH environment variable within the ‘activate’ script of your virtual environment. For example:

export PYTHONPATH="/path/to/custom_components:$PYTHONPATH"

If you go this route, you will have to deactivate and reactivate your virtual environment

Next, import the new connector rather than the old one

### from sic_framework.services.face_detection.face_detection import FaceDetection
from custom_face_detection import CustomFaceDetection

And then change anywhere you use the old one to the new one

### face_rec = FaceDetection()
face_rec = CustomFaceDetection()

Run the component (step 7)

Run the new component in a separate shell as if it were the old service (assuming you’re inside ‘custom_components') folder:

python custom_face_detection.py

You should see an output like this

[SICComponentManager 100.84.25.4]: INFO: Manager on device 100.84.25.4 starting
[SICComponentManager 100.84.25.4]: INFO: Started component manager on ip "100.84.25.4" with components:
[SICComponentManager 100.84.25.4]: INFO:  - CustomFaceDetectionComponent

And now you should be able to run your scripts with the new custom component!

  • No labels