- Created by Vojtěch Kováčik, last modified by Filip Chládek on May 21, 2024
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
« Previous Version 7 Next »
This page shows several examples of how to use the current code module. The examples are structured as a copy-and-paste code that works without any knowledge of programming.
(Some codes might require primitive modification based on the description provided)
To implement any of these codes into your project it is necessary that you:
Create a new Code module and name it according to the feature
Delete all of the predefined example code - “make your canvas clean ”
Copy and paste the code provided and modify only the specified variables.
Exit image evaluation from flow:
It is possible to stop evaluation early to save time based on a specific condition e.g. a Classifier distinguishes product types and chooses different evaluation branches to meet the specific product requirements.
This is useful when using Parallelism to split the flow.
See the example of different evaluation flows based on product type:
To stop the evaluation copy and paste the following code.
Modify Program Settings part according to the classes you wish to filter. (In the above example
list_of_exit_classes = ["Type 2"]
, therefore “Type 1” can pass through.)
def main(context): # List containing names of the classes that will exit the flow sooner. ######################## PROGRAM SETTINGS ################################### list_of_exit_classes = ["Type 1"] ######################## END PROGRAM SETTINGS ############################### for rectangle in context['detectedRectangles']: if rectangle['classNames'][0]['label'] in list_of_exit_classes: context['exit'] = True return
It is necessary to modify the list list_of_exit_classes
based on what classes are contained in your current project that are not supposed to continue evaluating.
Crop image by a detected rectangle
Useful when the image contains unnecessary elements and the goal is to focus only on a certain part.
To do this:
Train a Detector module (or other) to find the region of interest.
Copy and paste the following code into the Code module.
Modify the Program Settings part according to the class you wish to cut (or None if the rectangle does not have a class).
def main(context): ###### PROGRAM SETTINGS ######### label = "My Rectangle" ###### END PROGRAM SETTINGS ##### rectangle = find_rectangle(context, label) x_1, x_2 = int(rectangle['x']), int(rectangle['x'] + rectangle['width']) y_1, y_2 = int(rectangle['y']), int(rectangle['y'] + rectangle['height']) context['image'] = context['image'][y_1:y_2, x_1:x_2] def find_rectangle(context, label): for rectangle in context['detectedRectangles']: if label is None: return rectangle if label == rectangle['classNames'][0]['label']: return rectangle
It is necessary to modify the LABEL
variable to contain the class name (or None) of the rectangle by which you want to crop the image.
Save evaluated images to folders
Useful when your goal is to inspect the evaluated images at a later time.
This code draws the found rectangles to the image and saves only the NG evaluated images.
Create a new folder in Windows File Explorer where the images will be saved
Copy and paste the following code into the Code module
Modify Program Settings by specifying the folder path and drawn rectangle color
import cv2 import skimage import numpy as np from datetime import datetime from pathlib import Path RED = (0, 0, 255) GREEN = (0, 255, 0) BLUE = (255, 0, 0) YELLOW = (0, 255, 255) WHITE = (255, 255, 255) #################################### PROGRAM SETTINGS ####################################### # Specify the path to the save folder, image formats, and the wanted color of rectangles SAVE_FOLDER = r"C:\Users\Vox\Downloads" ORIGINAL_IMAGE_FORMAT = '.png' ANNOTATED_IMAGE_FORMAT = '.jpg' RECTANGLE_COLOR = RED ################################# END PROGRAM SETTINGS #################################### def main(context): # Get result result = context['result'] # When image has result TRUE, program stop. if result is True: return # Get image from context image = context['image'] # Save Original Image save_image_to_disc(image, filename_prefix = 'original_', image_format = ORIGINAL_IMAGE_FORMAT) # Draw rectangles to original image image = draw_rectangles_to_image(image, context, BARVA_RECTANGLU) # Save Anotated Image save_image_to_disc(image, filename_prefix = 'anotated_', image_format = ANNOTATED_IMAGE_FORMAT) def save_image_to_disc(image, filename_prefix:str = '', image_format = '.png'): timestamp = generate_timestamp() full_save_path = Path(SAVE_FOLDER).joinpath(filename_prefix + timestamp + image_format) cv2.imwrite(str(full_save_path), image) def generate_timestamp(): timestamp = datetime.now() formatted_timestamp = timestamp.strftime("%Y-%m-%d_%H-%M-%S_%f") return formatted_timestamp def draw_rectangles_to_image(image, context, color:tuple = (0,0,255)): for rect in context['detectedRectangles']: rect_start = (int(rect['x']), int(rect['y'])) rect_end = (int(rect['x'] + rect['width']), int(rect['y'] + rect['height'])) image = cv2.rectangle(image, rect_start, rect_end, color, 2) return image
It is necessary to modify at least SAVE_FOLDER
with your path to the saving folder. Modifying ?_IMAGE_FORMAT
or RECTANGLE_COLOR
is not necessary.
The folder path to copy and paste is written here:
Send data to S7-1200 PLC
To send your custom data or data from Pekat to a PLC:
Make sure that PLC has its Protection settings set up correctly via TIAportal - Same as in this example: https://snap7.sourceforge.net/snap7_client.html#1200_1500
Copy and paste the code below and set correct values to all variables in Program Settings and Output Settings.
This is for sending booleans. To send other type of data:
Set up buffer
to have the correct size of bits to write and update both:
bool_value
and snap7.util.set_bool(buffer, 0, 0, bool_value)
accordingly.
import snap7 #################################### START PROGRAM SETTINGS ####################################### PLC_IP = "192.100.100.1" DATA_BLOCK_NUMBER = 1 # For example for writing to bit on datablock DB1.2 these value will be set to 1 START_BIT_OFFSET = 2 # For example for writing to bit on datablock DB1.2 these value will be set to 2 ################################# END PROGRAM SETTINGS #################################### def main(context): result = context['result'] if result is None: return #################################### OUTPUT SETTINGS ####################################### data_to_send_to_plc = result ################################# START OUTPUT SETTINGS #################################### send_bool_value_to_plc_datablock(ip=PLC_IP, db_number=DATA_BLOCK_NUMBER, start_bit_offset=START_BIT_OFFSET, bool_output=data_to_send_to_plc) def send_bool_value_to_plc_datablock(ip:str, db_number:int, start_bit_offset:int, bool_output:bool): plc = snap7.client.Client() plc.connect(ip, 0, 0) # Initialize the bytearray with one byte buffer = bytearray(1) # Convert the boolean value to an integer (1 for True, 0 for False) bool_value = int(bool_output) # Set the boolean value to the bytearray snap7.util.set_bool(buffer, 0, 0, bool_value) # Write the bytearray to the PLC plc.db_write(DATA_BLOCK_NUMBER, start, buffer) plc.disconnect()
Use Heatmap as a mask
It is possible to use previously detected heatmap (from anomaly or surface) as a masking tool to hide specific objects or features.
The project contains a trained anomaly/surface module producing a heatmap
Copy and paste the following code
def main(context): # Make a copy of the image context['original_image'] = context['image'].copy() # Mask out the heatmap region heatmap = context['heatmaps'][0][0][:, :, 0] context['image'][:, :, 0][heatmap == 255] = 0 context['image'][:, :, 1][heatmap == 255] = 0 context['image'][:, :, 2][heatmap == 255] = 0 context['heatmaps'] = [] context['detectedRectangles'] = []
To get back the original image (delete the mask), copy and paste the following code into a different code module:
def main(context): context['image'] = context['original_image']
See image:
Surface detector finds the heatmap of an object → Code module masks out the image → Further detections are done → Code module unmasks back to the original.
Measure the distance between rectangles
It is possible to calculate the distance between detected objects for further evaluation.
The project needs to contain a module detecting two distinct rectangles
Copy and paste the following code
Modify Program Settings according to the class names in your project
This code:
writes the calculated distance converted to mm to the top left part of the image
draws a line between the centers of said rectangles
import cv2 import math ################ PROGRAM SETTINGS #################### label_1 = "Class 1" label_2 = "Class 2" pixel_to_mm_conversion = 0.078 # Example conversion factor (adjust as needed) ############### END PROGRAM SETTINGS ################# def main(context): rectangle_1 = find_rectangle(context, label_1) rectangle_2 = find_rectangle(context, label_2) if rectangle_1 is not None and rectangle_2 is not None: x1, y1, w1, h1 = rectangle_1['x'], rectangle_1['y'], rectangle_1['width'], rectangle_1['height'] x2, y2, w2, h2 = rectangle_2['x'], rectangle_2['y'], rectangle_2['width'], rectangle_2['height'] center_x1 = x1 + w1 / 2 center_y1 = y1 + h1 / 2 center_x2 = x2 + w2 / 2 center_y2 = y2 + h2 / 2 distance_pixels = math.sqrt((center_x2 - center_x1)**2 + (center_y2 - center_y1)**2) print("Distance between {} and {} in pixels: {:.2f}".format(label_1, label_2, distance_pixels)) # Convert distance from pixels to millimeters distance_mm = distance_pixels * pixel_to_mm_conversion print("Distance between {} and {} in millimeters: {:.2f}".format(label_1, label_2, distance_mm)) # Draw line between centers of rectangles on the original image in context draw_line_on_image(context, (int(center_x1), int(center_y1)), (int(center_x2), int(center_y2))) # Display distance on the original image in context cv2.putText(context['image'], "Distance: {:.2f} mm".format(distance_mm), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) else: print("One or both rectangles not found.") def find_rectangle(context, label): for rectangle in context['detectedRectangles']: for class_name_item in rectangle['classNames']: if label == class_name_item['label']: return rectangle return None def draw_line_on_image(context, start_point, end_point): image = context['image'] # Convert image to BGR format (if not already in BGR) if len(image.shape) == 2: image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) # Draw line on the image cv2.line(image, start_point, end_point, (0, 0, 255), 2) # Update the image in the context context['image'] = image
Color a grayscale depth map image
The grayscale depth map can be colored based on a specified threshold and color.
Useful when using a 3D scanner (like Photoneo) that returns a depth map.
The extreme pixels (white and black) in the image are ignored, other pixels are linearly interpolated and assigned a color.
Copy and paste the following code
Modify the Program Settings
color
if necessary to change the default color
import cv2 RED = cv2.COLORMAP_HOT BLUE = cv2.COLORMAP_OCEAN GREEN = cv2.COLORMAP_DEEPGREEN ####################### PROGRAM SETTINGS ###################### color = RED ###################### END PROGRAM SETTINGS ################### def main(context): image = context["image"] colormap = colormaps_available[color] colored_image = grayscale_to_color(image, colormap) context["image"] = colored_image def grayscale_to_color(image, colormap=cv2.COLORMAP_HOT): """ Convert a grayscale image to a gradient color scale based on pixel values. Args: image (numpy.ndarray): Grayscale image. Returns: numpy.ndarray: Colorized image. """ # Normalize the pixel values to the range [0, 1] normalized_image = image / 255.0 # Apply a colormap to the normalized image colorized_image = cv2.applyColorMap((normalized_image * 255).astype(np.uint8), colormap) return colorized_image
Rename existing model classes
It is possible to change the names of classes that were used in training. Useful when you made a mistake during naming or only made numeric names like “Type 1” - see images
Copy and paste the following code into the Code module
Modify Program Settings specifying a new name for each existing class
Classes not mentioned in the dictionary will not be modified.
################### PROGRAM SETTINGS ####################### # Modify and or add new class names to be used based on the following pattern # LEFT - current name : RIGHT - new name DICTIONARY = { "Type 1": "Screw", "Type 2": "Spring", "Type 3": "Nut", } ################# END PROGRAM SETTINGS ##################### def main(context): for rectangle in context['detectedRectangles']: for idx, class_name in enumerate(rectangle['classNames']): current_name = class_name['label'] new_name = DICTIONARY.get(current_name, current_name) rectangle['classNames'][idx]['label'] = new_name
- No labels