Skip to Content

← All functionalities

Visual inspection (machine vision)Raspberry Pi + USB cameraUSB/V4L2Machine vision

An OpenCV preprocessing pipeline for automated print inspection

Checking the printed marking on every part by eye is slow and unreliable — and exactly the kind of job a Raspberry Pi with a USB camera does well. This OpenCV quality inspection pipeline, taken from a real production line visual inspection deployment, isolates the part from the background and binarizes its print so it can be compared against a golden reference. Every stage — grayscale, blur, thresholding, morphology, CLAHE, adaptive threshold — is a few lines of Python you can tune with six parameters.

From raw frame to clean part crop

The camera sees a bright part on a dark background, so a fixed cv2.threshold after a Gaussian blur separates the two reliably. Morphological open removes specks, close fills holes, and the largest contour gives the bounding box of the part. Cropping to that box throws away everything that is not the part — the single biggest robustness win in the whole pipeline.

CLAHE plus adaptive threshold beats global thresholding

Factory lighting is never uniform: reflections, shadows from the operator, daylight changes through a shift. CLAHE equalizes contrast locally inside the crop, and cv2.adaptiveThreshold then binarizes the print using each pixel's own neighbourhood instead of one global value. A medianBlur in between removes salt noise without eating edges. The result is a stable black-and-white image of the marking, almost independent of ambient light — exactly the input the template matcher needs.

Six parameters, tuned from the GUI

Background threshold, morphology kernel size, CLAHE clip and grid, adaptive block size and constant C: six knobs cover every part variant the line has seen so far. In the deployed system they live in config.json and are adjusted with sliders over the live camera image, so a line operator can re-tune the station for a new product in minutes, without touching a single line of code or redeploying anything.

A snippet from the implementation

Straight from the example as deployed on the Raspberry Pi + USB camera — copy it freely:

def synthetic_image():
    """Generates a light 'part' on a dark background with a silkscreen print."""
    img = np.full((480, 640, 3), 25, np.uint8)            # dark background
    cv2.rectangle(img, (180, 120), (460, 360), (190, 190, 190), -1)  # part
    cv2.putText(img, "IS-42", (230, 260), cv2.FONT_HERSHEY_SIMPLEX,
                1.6, (40, 40, 40), 4)                      # silkscreen
    noise = np.random.randint(0, 18, img.shape, np.uint8)  # camera noise
    return cv2.add(img, noise)

The full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.

Frequently asked questions

Why not just compare the raw camera images directly?

Raw frames change with lighting, part position and camera noise. Comparing binarized, cropped prints makes the verdict depend on the marking itself, which is what you actually want to inspect.

Is a Raspberry Pi fast enough for this pipeline?

Yes. All stages are standard OpenCV operations on a small cropped region; a Raspberry Pi 4 processes a part in well under a second, which fits typical manual-feed station rates.

What camera settings matter most?

Fixed manual focus and constant exposure. Autofocus hunting between parts is the main source of inconsistent binarization, so the deployment locks focus via V4L2 and stores it in the config file.

Related functionalities