fix(types): removing implicit generics. <!-- Thank you for contributing to Pyodide! All improvements are welcome,
     so don't be afraid to make a PR. -->

### Description

<!-- Please explain what your PR is about:
     - reasoning for the change
     - some details of updated code
     - any noteworthy choices to be aware of
	Please refer to any related issues by #<issue_id> -->


import base64
import pathlib

from pyodide_test_runner import run_in_pyodide

REFERENCE_IMAGES_PATH = pathlib.Path(__file__).parent / "reference-images"


def compare_with_reference_image(selenium, reference_image, var="img", grayscale=True):
    reference_image_encoded = base64.b64encode(reference_image.read_bytes())
    grayscale = "cv.IMREAD_GRAYSCALE" if grayscale else "cv.IMREAD_COLOR"
    match_ratio = selenium.run(
        f"""
        import base64
        import numpy as np
        import cv2 as cv
        DIFF_THRESHOLD = 2
        arr = np.frombuffer(base64.b64decode({reference_image_encoded!r}), np.uint8)
        ref_data = cv.imdecode(arr, {grayscale})

        pixels_match = np.count_nonzero(np.abs({var}.astype(np.int16) - ref_data.astype(np.int16)) <= DIFF_THRESHOLD)
        pixels_total = ref_data.size
        float(pixels_match / pixels_total)
        """
    )

    # Due to some randomness in the result, we allow a small difference
    return match_ratio > 0.95


def test_import(selenium):
    selenium.set_script_timeout(60)
    selenium.load_package("opencv-python")
    selenium.run(
        """
        import cv2
        cv2.__version__
        """
    )


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_image_extensions():
    import cv2 as cv
    import numpy as np

    shape = (16, 16, 3)
    img = np.zeros(shape, np.uint8)

    extensions = {
        "bmp": b"BM6\x03",
        "jpg": b"\xff\xd8\xff\xe0",
        "jpeg": b"\xff\xd8\xff\xe0",
        "png": b"\x89PNG",
        "webp": b"RIFF",
    }

    for ext, signature in extensions.items():
        result, buf = cv.imencode(f".{ext}", img)
        assert result
        assert bytes(buf[:4]) == signature


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_io():
    import cv2 as cv
    import numpy as np

    shape = (16, 16, 3)
    img = np.zeros(shape, np.uint8)

    filename = "test.bmp"
    cv.imwrite(filename, img)
    img_ = cv.imread(filename)
    assert img_.shape == img.shape


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_drawing():
    import cv2 as cv
    import numpy as np

    width = 100
    height = 100
    shape = (width, height, 3)
    img = np.zeros(shape, np.uint8)

    cv.line(img, (0, 0), (width - 1, 0), (255, 0, 0), 5)
    cv.line(img, (0, 0), (0, height - 1), (0, 0, 255), 5)
    cv.rectangle(img, (0, 0), (width // 2, height // 2), (0, 255, 0), 2)
    cv.circle(img, (0, 0), radius=width // 2, color=(255, 0, 0))
    cv.putText(img, "Hello Pyodide", (0, 0), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_pixel_access():
    import cv2 as cv
    import numpy as np

    shape = (16, 16, 3)
    img = np.zeros(shape, np.uint8)

    img[5, 5] = [1, 2, 3]
    assert list(img[5, 5]) == [1, 2, 3]

    b, g, r = cv.split(img)
    img_ = cv.merge([b, g, r])
    assert (img == img_).all()


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_image_processing():
    import cv2 as cv
    import numpy as np

    # Masking
    img = np.random.randint(0, 255, size=500)
    lower = np.array([0])
    upper = np.array([200])
    mask = cv.inRange(img, lower, upper)
    res = cv.bitwise_and(img, img, mask=mask)
    assert not (res > 200).any()


def test_edge_detection(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        sobel = cv.Sobel(gray, cv.CV_8U, 1, 0, 3)
        laplacian = cv.Laplacian(gray, cv.CV_8U, ksize=3)
        canny = cv.Canny(src, 100, 255)
        None
        """
    )

    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_sobel.png", "sobel"
    )
    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_laplacian.png", "laplacian"
    )
    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_canny.png", "canny"
    )


def test_photo_decolor(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        grayscale, color_boost = cv.decolor(src)
        None
        """
    )

    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_decolor_grayscale.png", "grayscale"
    )
    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "baboon_decolor_color_boost.png",
        "color_boost",
        grayscale=False,
    )


def test_stitch(selenium):
    original_img_left = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "mountain1.png").read_bytes()
    )
    original_img_right = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "mountain2.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        left = np.frombuffer(base64.b64decode({original_img_left!r}), np.uint8)
        left = cv.imdecode(left, cv.IMREAD_COLOR)
        right = np.frombuffer(base64.b64decode({original_img_right!r}), np.uint8)
        right = cv.imdecode(right, cv.IMREAD_COLOR)
        stitcher = cv.Stitcher.create(cv.Stitcher_PANORAMA)
        status, panorama = stitcher.stitch([left, right])

        # It seems that the result is not always the same due to the randomness, so check the status and size instead
        assert status == cv.Stitcher_OK
        assert panorama.shape[0] >= max(left.shape[0], right.shape[0])
        assert panorama.shape[1] >= max(left.shape[1], right.shape[1])
        """
    )


def test_video_optical_flow(selenium):
    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "traffic.mp4").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np

        src = base64.b64decode({original_img!r})

        video_path = "video.mp4"
        with open(video_path, "wb") as f:
            f.write(src)

        cap = cv.VideoCapture(video_path)
        assert cap.isOpened()

        # params for ShiTomasi corner detection
        feature_params = dict( maxCorners = 100,
                            qualityLevel = 0.3,
                            minDistance = 7,
                            blockSize = 7 )
        # Parameters for lucas kanade optical flow
        lk_params = dict( winSize  = (15, 15),
                        maxLevel = 2,
                        criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))

        # Take first frame and find corners in it
        ret, old_frame = cap.read()
        assert ret

        old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
        p0 = cv.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
        # Create a mask image for drawing purposes
        mask = np.zeros_like(old_frame)
        while(1):
            ret, frame = cap.read()
            if not ret:
                break
            frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
            # calculate optical flow
            p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
            # Select good points
            if p1 is not None:
                good_new = p1[st==1]
                good_old = p0[st==1]
            # draw the tracks
            for i, (new, old) in enumerate(zip(good_new, good_old)):
                a, b = new.ravel()
                c, d = old.ravel()
                mask = cv.line(mask, (int(a), int(b)), (int(c), int(d)), [0, 0, 255], 2)
                frame = cv.circle(frame, (int(a), int(b)), 5, [255, 0, 0], -1)
            img = cv.add(frame, mask)
            # Now update the previous frame and previous points
            old_gray = frame_gray.copy()
            p0 = good_new.reshape(-1, 1, 2)

        optical_flow = img
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "traffic_optical_flow.png",
        "optical_flow",
        grayscale=False,
    )


def test_flann_sift(selenium):
    original_img_src1 = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "box.png").read_bytes()
    )
    original_img_src2 = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "box_in_scene.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src1 = np.frombuffer(base64.b64decode({original_img_src1!r}), np.uint8)
        src1 = cv.imdecode(src1, cv.IMREAD_GRAYSCALE)
        src2 = np.frombuffer(base64.b64decode({original_img_src2!r}), np.uint8)
        src2 = cv.imdecode(src2, cv.IMREAD_GRAYSCALE)

        #-- Step 1: Detect the keypoints using SIFT Detector, compute the descriptors
        detector = cv.SIFT_create()
        keypoints1, descriptors1 = detector.detectAndCompute(src1, None)
        keypoints2, descriptors2 = detector.detectAndCompute(src2, None)

        #-- Step 2: Matching descriptor vectors with a FLANN based matcher
        matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED)
        knn_matches = matcher.knnMatch(descriptors1, descriptors2, 2)

        #-- Filter matches using the Lowe's ratio test
        ratio_thresh = 0.3
        good_matches = []
        for m,n in knn_matches:
            if m.distance < ratio_thresh * n.distance:
                good_matches.append(m)

        #-- Draw matches
        matches = np.empty((max(src1.shape[0], src2.shape[0]), src1.shape[1]+src2.shape[1], 3), dtype=np.uint8)
        cv.drawMatches(src1, keypoints1, src2, keypoints2, good_matches, matches, matchColor=[255, 0, 0], flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

        sift_result = cv.cvtColor(matches, cv.COLOR_BGR2GRAY)
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "box_sift.png",
        "sift_result",
        grayscale=True,
    )


def test_dnn_mnist(selenium):
    """
    Run tiny MNIST classification ONNX model
    Training script: https://github.com/ryanking13/torch-opencv-mnist
    """

    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "mnist_2.png").read_bytes()
    )
    tf_model = base64.b64encode((REFERENCE_IMAGES_PATH / "mnist.onnx").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np

        model_weights = base64.b64decode({tf_model!r})
        model_weights_path = './mnist.onnx'
        with open(model_weights_path, 'wb') as f:
            f.write(model_weights)

        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_GRAYSCALE)

        net = cv.dnn.readNet(model_weights_path)
        blob = cv.dnn.blobFromImage(src, 1.0, (28, 28), (0, 0, 0), False, False)

        net.setInput(blob)
        prob = net.forward()
        assert "output_0" in net.getLayerNames()
        assert np.argmax(prob) == 2
        """
    )


def test_ml_pca(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "pca.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        from math import atan2, cos, sin, sqrt, pi

        def drawAxis(img, p_, q_, colour, scale):
            p = list(p_)
            q = list(q_)

            angle = atan2(p[1] - q[1], p[0] - q[0]) # angle in radians
            hypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))
            # Here we lengthen the arrow by a factor of scale
            q[0] = p[0] - scale * hypotenuse * cos(angle)
            q[1] = p[1] - scale * hypotenuse * sin(angle)
            cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
            # create the arrow hooks
            p[0] = q[0] + 9 * cos(angle + pi / 4)
            p[1] = q[1] + 9 * sin(angle + pi / 4)
            cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
            p[0] = q[0] + 9 * cos(angle - pi / 4)
            p[1] = q[1] + 9 * sin(angle - pi / 4)
            cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)

        def getOrientation(pts, img):

            sz = len(pts)
            data_pts = np.empty((sz, 2), dtype=np.float64)
            for i in range(data_pts.shape[0]):
                data_pts[i,0] = pts[i,0,0]
                data_pts[i,1] = pts[i,0,1]
            # Perform PCA analysis
            mean = np.empty((0))
            mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)
            # Store the center of the object
            cntr = (int(mean[0,0]), int(mean[0,1]))


            cv.circle(img, cntr, 3, (255, 0, 255), 2)
            p1 = (cntr[0] + 0.02 * eigenvectors[0,0] * eigenvalues[0,0], cntr[1] + 0.02 * eigenvectors[0,1] * eigenvalues[0,0])
            p2 = (cntr[0] - 0.02 * eigenvectors[1,0] * eigenvalues[1,0], cntr[1] - 0.02 * eigenvectors[1,1] * eigenvalues[1,0])
            drawAxis(img, cntr, p1, (0, 255, 0), 1)
            drawAxis(img, cntr, p2, (255, 255, 0), 5)
            angle = atan2(eigenvectors[0,1], eigenvectors[0,0]) # orientation in radians

            return angle

        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

        # Convert image to binary
        _, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        contours, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
        for i, c in enumerate(contours):
            # Calculate the area of each contour
            area = cv.contourArea(c)
            # Ignore contours that are too small or too large
            if area < 1e2 or 1e5 < area:
                continue
            # Draw each contour only for visualisation purposes
            cv.drawContours(src, contours, i, (0, 0, 255), 2)
            # Find the orientation of each shape
            getOrientation(c, src)

        pca_result = src
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "pca_result.png",
        "pca_result",
        grayscale=False,
    )


def test_objdetect_face(selenium):
    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "monalisa.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        from pathlib import Path

        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        gray = cv.equalizeHist(gray)

        face_cascade = cv.CascadeClassifier()
        eyes_cascade = cv.CascadeClassifier()
        data_path = Path(cv.data.haarcascades)
        face_cascade.load(str(data_path / "haarcascade_frontalface_alt.xml"))
        eyes_cascade.load(str(data_path / "haarcascade_eye_tree_eyeglasses.xml"))

        faces = face_cascade.detectMultiScale(gray)
        face_detected = src.copy()
        for (x,y,w,h) in faces:
            center = (x + w//2, y + h//2)
            face_detected = cv.ellipse(face_detected, center, (w//2, h//2), 0, 0, 360, (255, 0, 255), 4)
            faceROI = gray[y:y+h,x:x+w]
            eyes = eyes_cascade.detectMultiScale(faceROI)
            for (x2,y2,w2,h2) in eyes:
                eye_center = (x + x2 + w2//2, y + y2 + h2//2)
                radius = int(round((w2 + h2)*0.25))
                face_detected = cv.circle(face_detected, eye_center, radius, (255, 0, 0 ), 4)

        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "monalisa_facedetect.png",
        "face_detected",
        grayscale=False,
    )


def test_feature2d_kaze(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)

        detector = cv.KAZE_create()
        keypoints = detector.detect(src)

        kaze = cv.drawKeypoints(src, keypoints, None, color=(0, 0, 255), flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "baboon_kaze.png",
        "kaze",
        grayscale=False,
    )


def test_calib3d_chessboard(selenium):
    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "chessboard.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)

        criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        ret, corners = cv.findChessboardCorners(gray, (9, 6), None)
        cv.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        cv.drawChessboardCorners(gray, (9, 6), corners, ret)
        chessboard_corners = gray
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "chessboard_corners.png",
        "chessboard_corners",
    )

import base64
import pathlib

from pyodide_test_runner import run_in_pyodide

REFERENCE_IMAGES_PATH = pathlib.Path(__file__).parent / "reference-images"


def compare_with_reference_image(selenium, reference_image, var="img", grayscale=True):
    reference_image_encoded = base64.b64encode(reference_image.read_bytes())
    grayscale = "cv.IMREAD_GRAYSCALE" if grayscale else "cv.IMREAD_COLOR"
    match_ratio = selenium.run(
        f"""
        import base64
        import numpy as np
        import cv2 as cv
        DIFF_THRESHOLD = 2
        arr = np.frombuffer(base64.b64decode({reference_image_encoded!r}), np.uint8)
        ref_data = cv.imdecode(arr, {grayscale})

        pixels_match = np.count_nonzero(np.abs({var}.astype(np.int16) - ref_data.astype(np.int16)) <= DIFF_THRESHOLD)
        pixels_total = ref_data.size
        float(pixels_match / pixels_total)
        """
    )

    # Due to some randomness in the result, we allow a small difference
    return match_ratio > 0.95


def test_import(selenium):
    selenium.set_script_timeout(60)
    selenium.load_package("opencv-python")
    selenium.run(
        """
        import cv2
        cv2.__version__
        """
    )


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_image_extensions():
    import cv2 as cv
    import numpy as np

    shape = (16, 16, 3)
    img = np.zeros(shape, np.uint8)

    extensions = {
        "bmp": b"BM6\x03",
        "jpg": b"\xff\xd8\xff\xe0",
        "jpeg": b"\xff\xd8\xff\xe0",
        "png": b"\x89PNG",
        "webp": b"RIFF",
    }

    for ext, signature in extensions.items():
        result, buf = cv.imencode(f".{ext}", img)
        assert result
        assert bytes(buf[:4]) == signature


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_io():
    import cv2 as cv
    import numpy as np

    shape = (16, 16, 3)
    img = np.zeros(shape, np.uint8)

    filename = "test.bmp"
    cv.imwrite(filename, img)
    img_ = cv.imread(filename)
    assert img_.shape == img.shape


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_drawing():
    import cv2 as cv
    import numpy as np

    width = 100
    height = 100
    shape = (width, height, 3)
    img = np.zeros(shape, np.uint8)

    cv.line(img, (0, 0), (width - 1, 0), (255, 0, 0), 5)
    cv.line(img, (0, 0), (0, height - 1), (0, 0, 255), 5)
    cv.rectangle(img, (0, 0), (width // 2, height // 2), (0, 255, 0), 2)
    cv.circle(img, (0, 0), radius=width // 2, color=(255, 0, 0))
    cv.putText(img, "Hello Pyodide", (0, 0), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_pixel_access():
    import cv2 as cv
    import numpy as np

    shape = (16, 16, 3)
    img = np.zeros(shape, np.uint8)

    img[5, 5] = [1, 2, 3]
    assert list(img[5, 5]) == [1, 2, 3]

    b, g, r = cv.split(img)
    img_ = cv.merge([b, g, r])
    assert (img == img_).all()


@run_in_pyodide(packages=["opencv-python", "numpy"])
def test_image_processing():
    import cv2 as cv
    import numpy as np

    # Masking
    img = np.random.randint(0, 255, size=500)
    lower = np.array([0])
    upper = np.array([200])
    mask = cv.inRange(img, lower, upper)
    res = cv.bitwise_and(img, img, mask=mask)
    assert not (res > 200).any()


def test_edge_detection(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        sobel = cv.Sobel(gray, cv.CV_8U, 1, 0, 3)
        laplacian = cv.Laplacian(gray, cv.CV_8U, ksize=3)
        canny = cv.Canny(src, 100, 255)
        None
        """
    )

    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_sobel.png", "sobel"
    )
    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_laplacian.png", "laplacian"
    )
    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_canny.png", "canny"
    )


def test_photo_decolor(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        grayscale, color_boost = cv.decolor(src)
        None
        """
    )

    assert compare_with_reference_image(
        selenium, REFERENCE_IMAGES_PATH / "baboon_decolor_grayscale.png", "grayscale"
    )
    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "baboon_decolor_color_boost.png",
        "color_boost",
        grayscale=False,
    )


def test_stitch(selenium):
    original_img_left = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "mountain1.png").read_bytes()
    )
    original_img_right = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "mountain2.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        left = np.frombuffer(base64.b64decode({original_img_left!r}), np.uint8)
        left = cv.imdecode(left, cv.IMREAD_COLOR)
        right = np.frombuffer(base64.b64decode({original_img_right!r}), np.uint8)
        right = cv.imdecode(right, cv.IMREAD_COLOR)
        stitcher = cv.Stitcher.create(cv.Stitcher_PANORAMA)
        status, panorama = stitcher.stitch([left, right])

        # It seems that the result is not always the same due to the randomness, so check the status and size instead
        assert status == cv.Stitcher_OK
        assert panorama.shape[0] >= max(left.shape[0], right.shape[0])
        assert panorama.shape[1] >= max(left.shape[1], right.shape[1])
        """
    )


def test_video_optical_flow(selenium):
    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "traffic.mp4").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np

        src = base64.b64decode({original_img!r})

        video_path = "video.mp4"
        with open(video_path, "wb") as f:
            f.write(src)

        cap = cv.VideoCapture(video_path)
        assert cap.isOpened()

        # params for ShiTomasi corner detection
        feature_params = dict( maxCorners = 100,
                            qualityLevel = 0.3,
                            minDistance = 7,
                            blockSize = 7 )
        # Parameters for lucas kanade optical flow
        lk_params = dict( winSize  = (15, 15),
                        maxLevel = 2,
                        criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))

        # Take first frame and find corners in it
        ret, old_frame = cap.read()
        assert ret

        old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
        p0 = cv.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
        # Create a mask image for drawing purposes
        mask = np.zeros_like(old_frame)
        while(1):
            ret, frame = cap.read()
            if not ret:
                break
            frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
            # calculate optical flow
            p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
            # Select good points
            if p1 is not None:
                good_new = p1[st==1]
                good_old = p0[st==1]
            # draw the tracks
            for i, (new, old) in enumerate(zip(good_new, good_old)):
                a, b = new.ravel()
                c, d = old.ravel()
                mask = cv.line(mask, (int(a), int(b)), (int(c), int(d)), [0, 0, 255], 2)
                frame = cv.circle(frame, (int(a), int(b)), 5, [255, 0, 0], -1)
            img = cv.add(frame, mask)
            # Now update the previous frame and previous points
            old_gray = frame_gray.copy()
            p0 = good_new.reshape(-1, 1, 2)

        optical_flow = img
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "traffic_optical_flow.png",
        "optical_flow",
        grayscale=False,
    )


def test_flann_sift(selenium):
    original_img_src1 = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "box.png").read_bytes()
    )
    original_img_src2 = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "box_in_scene.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src1 = np.frombuffer(base64.b64decode({original_img_src1!r}), np.uint8)
        src1 = cv.imdecode(src1, cv.IMREAD_GRAYSCALE)
        src2 = np.frombuffer(base64.b64decode({original_img_src2!r}), np.uint8)
        src2 = cv.imdecode(src2, cv.IMREAD_GRAYSCALE)

        #-- Step 1: Detect the keypoints using SIFT Detector, compute the descriptors
        detector = cv.SIFT_create()
        keypoints1, descriptors1 = detector.detectAndCompute(src1, None)
        keypoints2, descriptors2 = detector.detectAndCompute(src2, None)

        #-- Step 2: Matching descriptor vectors with a FLANN based matcher
        matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED)
        knn_matches = matcher.knnMatch(descriptors1, descriptors2, 2)

        #-- Filter matches using the Lowe's ratio test
        ratio_thresh = 0.3
        good_matches = []
        for m,n in knn_matches:
            if m.distance < ratio_thresh * n.distance:
                good_matches.append(m)

        #-- Draw matches
        matches = np.empty((max(src1.shape[0], src2.shape[0]), src1.shape[1]+src2.shape[1], 3), dtype=np.uint8)
        cv.drawMatches(src1, keypoints1, src2, keypoints2, good_matches, matches, matchColor=[255, 0, 0], flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

        sift_result = cv.cvtColor(matches, cv.COLOR_BGR2GRAY)
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "box_sift.png",
        "sift_result",
        grayscale=True,
    )


def test_dnn_mnist(selenium):
    """
    Run tiny MNIST classification ONNX model
    Training script: https://github.com/ryanking13/torch-opencv-mnist
    """

    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "mnist_2.png").read_bytes()
    )
    tf_model = base64.b64encode((REFERENCE_IMAGES_PATH / "mnist.onnx").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np

        model_weights = base64.b64decode({tf_model!r})
        model_weights_path = './mnist.onnx'
        with open(model_weights_path, 'wb') as f:
            f.write(model_weights)

        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_GRAYSCALE)

        net = cv.dnn.readNet(model_weights_path)
        blob = cv.dnn.blobFromImage(src, 1.0, (28, 28), (0, 0, 0), False, False)

        net.setInput(blob)
        prob = net.forward()
        assert "output_0" in net.getLayerNames()
        assert np.argmax(prob) == 2
        """
    )


def test_ml_pca(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "pca.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        from math import atan2, cos, sin, sqrt, pi

        def drawAxis(img, p_, q_, colour, scale):
            p = list(p_)
            q = list(q_)

            angle = atan2(p[1] - q[1], p[0] - q[0]) # angle in radians
            hypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))
            # Here we lengthen the arrow by a factor of scale
            q[0] = p[0] - scale * hypotenuse * cos(angle)
            q[1] = p[1] - scale * hypotenuse * sin(angle)
            cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
            # create the arrow hooks
            p[0] = q[0] + 9 * cos(angle + pi / 4)
            p[1] = q[1] + 9 * sin(angle + pi / 4)
            cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)
            p[0] = q[0] + 9 * cos(angle - pi / 4)
            p[1] = q[1] + 9 * sin(angle - pi / 4)
            cv.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv.LINE_AA)

        def getOrientation(pts, img):

            sz = len(pts)
            data_pts = np.empty((sz, 2), dtype=np.float64)
            for i in range(data_pts.shape[0]):
                data_pts[i,0] = pts[i,0,0]
                data_pts[i,1] = pts[i,0,1]
            # Perform PCA analysis
            mean = np.empty((0))
            mean, eigenvectors, eigenvalues = cv.PCACompute2(data_pts, mean)
            # Store the center of the object
            cntr = (int(mean[0,0]), int(mean[0,1]))


            cv.circle(img, cntr, 3, (255, 0, 255), 2)
            p1 = (cntr[0] + 0.02 * eigenvectors[0,0] * eigenvalues[0,0], cntr[1] + 0.02 * eigenvectors[0,1] * eigenvalues[0,0])
            p2 = (cntr[0] - 0.02 * eigenvectors[1,0] * eigenvalues[1,0], cntr[1] - 0.02 * eigenvectors[1,1] * eigenvalues[1,0])
            drawAxis(img, cntr, p1, (0, 255, 0), 1)
            drawAxis(img, cntr, p2, (255, 255, 0), 5)
            angle = atan2(eigenvectors[0,1], eigenvectors[0,0]) # orientation in radians

            return angle

        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

        # Convert image to binary
        _, bw = cv.threshold(gray, 50, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
        contours, _ = cv.findContours(bw, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
        for i, c in enumerate(contours):
            # Calculate the area of each contour
            area = cv.contourArea(c)
            # Ignore contours that are too small or too large
            if area < 1e2 or 1e5 < area:
                continue
            # Draw each contour only for visualisation purposes
            cv.drawContours(src, contours, i, (0, 0, 255), 2)
            # Find the orientation of each shape
            getOrientation(c, src)

        pca_result = src
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "pca_result.png",
        "pca_result",
        grayscale=False,
    )


def test_objdetect_face(selenium):
    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "monalisa.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        from pathlib import Path

        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        gray = cv.equalizeHist(gray)

        face_cascade = cv.CascadeClassifier()
        eyes_cascade = cv.CascadeClassifier()
        data_path = Path(cv.data.haarcascades)
        face_cascade.load(str(data_path / "haarcascade_frontalface_alt.xml"))
        eyes_cascade.load(str(data_path / "haarcascade_eye_tree_eyeglasses.xml"))

        faces = face_cascade.detectMultiScale(gray)
        face_detected = src.copy()
        for (x,y,w,h) in faces:
            center = (x + w//2, y + h//2)
            face_detected = cv.ellipse(face_detected, center, (w//2, h//2), 0, 0, 360, (255, 0, 255), 4)
            faceROI = gray[y:y+h,x:x+w]
            eyes = eyes_cascade.detectMultiScale(faceROI)
            for (x2,y2,w2,h2) in eyes:
                eye_center = (x + x2 + w2//2, y + y2 + h2//2)
                radius = int(round((w2 + h2)*0.25))
                face_detected = cv.circle(face_detected, eye_center, radius, (255, 0, 0 ), 4)

        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "monalisa_facedetect.png",
        "face_detected",
        grayscale=False,
    )


def test_feature2d_kaze(selenium):
    original_img = base64.b64encode((REFERENCE_IMAGES_PATH / "baboon.png").read_bytes())
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)

        detector = cv.KAZE_create()
        keypoints = detector.detect(src)

        kaze = cv.drawKeypoints(src, keypoints, None, color=(0, 0, 255), flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "baboon_kaze.png",
        "kaze",
        grayscale=False,
    )


def test_calib3d_chessboard(selenium):
    original_img = base64.b64encode(
        (REFERENCE_IMAGES_PATH / "chessboard.png").read_bytes()
    )
    selenium.load_package("opencv-python")
    selenium.run(
        f"""
        import base64
        import cv2 as cv
        import numpy as np
        src = np.frombuffer(base64.b64decode({original_img!r}), np.uint8)
        src = cv.imdecode(src, cv.IMREAD_COLOR)

        criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
        ret, corners = cv.findChessboardCorners(gray, (9, 6), None)
        cv.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        cv.drawChessboardCorners(gray, (9, 6), corners, ret)
        chessboard_corners = gray
        None
        """
    )

    assert compare_with_reference_image(
        selenium,
        REFERENCE_IMAGES_PATH / "chessboard_corners.png",
        "chessboard_corners",
    )
