Psycle
153 rue Robert Schuman
60610 Lacroix-Saint-Ouen

Fermer

The software suite for developing and deploying your machine vision applications

Since working with Psycle I am amazed at the progress we have made on our project. Having failed to even get started with 2 previous AI companies, Psycle have helped us get a pre-production prototype on line in a matter of a few weeks. Their SDK and all their tools are well written, well documented and ready to use. This is then backed up with support that is second to none. I would thoroughly recommend working with them on your next AI vision project.

Dr Russell Sion – Managing Director, Jenton Dimaco

Our SDK allows data scientists to focus on what matters most: data and deep learning algorithms. No more wasting time with user interfaces or communication protocols—you get powerful tools that are ready for production.

Océane Durand – Directrice du département Projets Psycle

The user experience on this platform is truly excellent, and the quality of the documentation, combined with responsive support, enables rapid and smooth deployment and commissioning. The main differentiating factor of this tool is its direct and reliable integration with industrial cameras on production lines, a feature that is still rare in many labelling and model training solutions. This approach effectively reduces the distance between model training and operational inspection, making the platform a relevant and truly usable solution in an industrial environment.

Mahdiyé Seyedian – AI Engineer, Groupe WE

Repsycle

Customisable vision control flow

Adapt the default application flow to your specific requirements.

Easily modify the elements transmitted at each stage of the workflow to meet the unique requirements of your projects.

Enjoy unrivalled flexibility, either by replacing the entire flow with your own, or by connecting to predefined hooks to limit development.

def _before_cycle_starting(self) -> None:
    """ Check that all cameras are connected """
    super()._before_cycle_starting()

    nb_connected_cams = len(self.get_connected_cameras())
    if nb_connected_cams != 6:
        self.message(
            fr=f"{nb_connected_cams} caméras sont connectées, mais 6 sont attendues",
            en=f"{nb_connected_cams} cameras are connected, but 6 were expected",
            level=MessageLevel.IMPORTANT,
            msg_type=MessageType.ERROR,
        )
        raise CycleStartingException

    log.info("All checks _before_cycle_starting OK")

 

def _prepare_stream(self, trig: Trig) -> np.ndarray:
    """ Custom override: add rectangle on stream to indicate product conformity """
    stream: np.ndarray = self._prepare_stream(trig)

    border: int = self.get_config_value("border", 40)
    color_bgr = (0, 0, 255) if trig.metadata.is_conform else (0, 255, 0)
    stream = add_border_on_image(
        image_to_stream,
        border,
        color_bgr,
    )

    return stream


 

def _after_save_trig(self, trig: Trig) -> None:
    """
    Custom override: always save locally data not already uploaded to PAQ
    """
    super()._after_save_trig(trig)

    for datum in trig.data:
        if datum.is_saved():
            continue

        image_destination_path = get_image_path(
            root_storage_path=TMP_STORAGE_PATH, 
            datum_id=datum.id,
        )

        self.queue_data_to_local_save(
            data=datum, 
            image_path=image_destination_path,
        )

 

Repsycle

Interactions with end users

Integrate specific code snippets into your applications to enable users to initiate actions at any time.

Empower your production operators to act autonomously based on predefined rules.

Use the HMI to communicate with operators.

@declare_command(
    "test_rising_edge",
    [
        TypedConfigVarFactory(
            "pin",
            "int",
            default=0,
            nullable=False,
            mini=0,
            maxi=7,
            metadata={
                "label_en": "PIN",
            },
        ),
    ],
    command_metadata=CallableMetadata(
        show_on_pap=True,
        label_en="Try rising an edge during 1 second",
    ),
)
@synchronized
def _test_rising_edge(
    self, pin: int,
) -> None:
    dio = self.get_dio()
    if dio is None or not dio.is_connected():
        self.message(
            en="No DIO configured, impossible to rise an adge",
            level=MessageLevel.NORMAL,
            type=MessageType.ERROR,
        )
        return
    dio.set_output(pin=pin, state=1, rollback_after=1)
    self.message(
        en=f"Edge risen on pin {pin}",
        level=MessageLevel.NORMAL,
        type=MessageType.INFO,
    )


 

@declare_custom_rule(
    "check_if_prediction",
    custom_rule_metadata=CallableMetadata(
        show_on_actions=True,
        label_fr="Une prédiction a été faite",
        label_en="At least one prediction was made",
    ),
)
def _check_if_prediction(self, trig: Trig) -> bool:
    """
    :return: True if at least one prediction has been made, False otherwise
    """
    assert len(trig.data) == 1
    return len(trig.data[0].predictions) > 0

 

self.message(
    fr="Démarrage de la calibration, attendre avant de lancer un cycle...",
    en="Starting calibration, wait before starting a cycle...",
    level=MessageLevel.NORMAL,
    type=MessageType.INFO,
)

 

Repsycle

Adding custom components

Create your own types of equipment and display them on the HMI

Adapt existing AI models or add your own

Integrate custom communication protocols

def _prepare_devices_configurations(self, devices_configurations: list[DeviceConfiguration]) -> None: 
    super()._prepare_devices_configurations(devices_configurations)
    log.info("Override of _prepare_devices_configurations method (create custom datamatrix reader)") 

    if len([conf for conf in devices_configurations if conf.device_type == DeviceType.code_reader]) == 0 :
        # Automatically create a datamatrix reader device for it to be accessible to operators from PAP
        scanner_conf = DeviceConfiguration(
        label="Datamatrix reader (custom)", 
        device_type=DeviceType.code_reader, 
        device_model="custom", 
        config=TypedConfig.from_dict({
            "id_vendor": {
                "type": "str", 
                "value": "1234"
            }, 
            "id_product": {
                "type": "str", 
                "value": "5678"
            }, 
            "timeout": {
                "type": "float", 
                "value": 1
            }, 
        }),
        )
    devices_configurations.append(scanner_conf) 

    log.info("Created scanner automatically")

 

class InstancesCounter(DetectionModel):
    """ Adapted version of a detection model to only count instances, for very quick applications with high number of instances """

    def __init__(
        self,
        weights: pathlib.Path,
        preprocess: Optional[PreprocessingT],
        img_size: int = 256,
        force_cpu: bool = False
    ):
        super().__init__(
            weights=weights,
            preprocess=preprocess,
            img_size=img_size,
            force_cpu=force_cpu,
        )

    @torch.inference_mode()
    def infer_get_count_only(self, images_original: np.ndarray, conf_thresh: float = 0.1, iou_thresh: float = 0.5) -> List[int]:
        # If batch dimension not present
        if len(images_original.shape) == 3:
            images_original = np.expand_dims(images_original, 0)

        images = self.preprocess(images_original)
        preds = self.model(images, augment=False)
        preds = non_max_suppression(preds, conf_thresh, iou_thresh)

        nb_instances: List[int] = [0] * len(preds)  # number of instances for each image
        for i, det in enumerate(preds):  # detections per image
            if det is not None and len(det):
                nb_instances[i] = len(det)

        return nb_instances

 

def _after_application_apply(self) -> None:
    super()._after_application_apply()
    self.stop_stream()

    # Start socket manager
    if (
        self.get_config_value("sockets_enabled", True) is True
        and self.socket_manager is None
    ):
        self.socket_manager = SocketManager(
            trig=self._command_trig,
            expose_params=self._expose_params,
            get_param=self._get_param,
            request_stream=self._request_stream,
            tcp_ip=self.get_config_value("tcp_ip"),
            tcp_port=self.get_config_value("tcp_port"),
            frame_suffix="\r\n",
        )
        log.info("starting sockets")
        self.socket_manager.start()
    else:
        log.info("sockets not enabled")








 

Repsycle

Plans

Repsycle

Autonomy
mode


  • SDK licence for 1 user
  • Technical development
    documentation
  • Installation documentation (containerisation)
  • Unlimited offline deployment on company production PCs
  • PAQ licence (image database & trainings) for the development machine
  • Remote support

Repsycle

Support
mode


Autonomy mode

Offre plus
  • 3 PAQ licences for production machines
  • Supply of an industrial PC for training and inference, provisioned with the Psycle standards vision application
  • 3-day on-site training course & peer coding of an initial specific application

Repsycle

Infrastructure
mode


Autonomy mode

Offre plus
  • Installation of PAQ On-Premises (within the company’s IT infrastructure)
  • SDK licences for multiple users
  • Annual 3-day on-site training (introduction for new users, changelog of new features & peer coding of migrations)
  • Network infrastructure studies for multi-line monitoring
  • Implementation of the application build and deployment pipeline without external connection
  • On-site maintenance operations
équipe de développement SDK Psycle

Accelerate and improve the reliability of your vision developments now!