51c视觉~CV~合集10

我自己的原文哦~    https://blog.51cto.com/whaosoft/13241694

一、CV创建自定义图像滤镜

  热图滤镜

    这组滤镜提供了各种不同的艺术和风格化光学图像捕捉方法。例如,热滤镜会将图像转换为“热图”,而卡通滤镜则提供生动的图像,这些图像看起来就像是漫画书制作的。最接近自然色彩以及海滩和自然场景的是 VSCO 滤镜。如果要减少工业感,可以对 Instagram 应用滤镜进行大量投资。将这个简单的灰度图转换为彩色图像。这将是灰度滤镜之一。最后,让我们考虑油画滤镜,OpenCV 通过一种风格化技术实现了该滤镜,该技术可创建看起来像油画的纹理效果。用户只需几行代码即可通过 OpenCV 和 Python 轻松使用它们来增强图像。

    热成像非常适合在夜间或存在轻微雾、雨或烟等遮挡物的情况下生成图像。例如,前视红外或 FLIR 摄像机可用于为军用和民用飞机提供夜视功能,或用于安全和监视。

import cv2
img =  cv2.imread('image.jpg')
#applying filter
color_image = cv2.applyColorMap(img, cv2.COLORMAP_JET) 
cv2.imshow('Image',color_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

    卡通滤镜

    使用我们举世闻名的 Cartoonizer 效果将任何照片变成卡通!只需单击一下即可了解为什么它是我们最喜爱的艺术类别。

    这是读取图像后的片段代码,我们必须应用灰色,然后模糊图像。

import cv2
image =  cv2.imread('image.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurImage = cv2.medianBlur(image, 1)
edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)
color = cv2.bilateralFilter(image, 9, 200, 200)
cartoon = cv2.bitwise_and(color, color, mask = edges)
cv2.imshow('Image',cartoon)
cv2.waitKey(0)
cv2.destroyAllWindows()

    VSCO 滤镜

    要创建 VSCO 风格的滤镜效果,您需要使用鲜艳的预设。类似 VSCO 的滤镜非常适合各种图像。让您的图像呈现出色彩鲜艳、充满活力的外观,非常适合自然和海滩场景等主题。

import cv2
import numpy as np
def colorful_vibrant_filter(image):
    """
    Apply a colorful and vibrant filter to the input image.
    Args:
        image (numpy.ndarray): The input image.
    Returns:
        numpy.ndarray: The filtered image.
    """
    # Convert the image to HSV color space
    hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    # Increase the saturation by 50%
    hsv_image[..., 1] = np.clip(hsv_image[..., 1] * 1.5, 0, 255)
    # Increase the value (brightness) by 20%
    hsv_image[..., 2] = np.clip(hsv_image[..., 2] * 1.2, 0, 255)
    # Convert the image back to BGR color space
    filtered_image = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)
    return filtered_image
# Load an example image
image = cv2.imread('image.jpg')
# Apply the colorful vibrant filter
filtered_image = colorful_vibrant_filter(image)
# Display the original and filtered images
cv2.imshow('Original Image', image)
cv2.imshow('Filtered Image', filtered_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

    灰度滤镜

    使用 Fotors 的“灰度”、“镀铬”和“黑白”选项,在几秒钟内将您的照片变成黑白色!“褪色白色”滤镜还添加了微妙的仿旧效果。

import cv2
def grayscale_filter(image):
    """
    Apply a grayscale filter to the input image.
    Args:
        image (numpy.ndarray): The input image.
    Returns:
        numpy.ndarray: The grayscale image.
    """
    # Convert the image to grayscale using cv2.cvtColor
    grayscale_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return grayscale_image
# Load an example image
image = cv2.imread('image.jpg')
# Apply the grayscale filter
grayscale_image = grayscale_filter(image)
# Display the original and grayscale images
cv2.imshow('Original Image', image)
cv2.imshow('Grayscale Image', grayscale_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

    油画滤镜

    厌倦了必须打开 Photoshop 才能为照片添加油画滤镜?只需在“油画”下单击几下即可添加!“光泽”可让所有东西都呈现出绿色,非常适合绿叶照片。

import cv2
# Load the image
img = cv2.imread('image.jpg')
# Apply oil painting filter
output = cv2.stylization(img, sigma_s=60, sigma_r=0.6)
# Display the output
cv2.imshow('Oil Painting', output)
cv2.waitKey(0)
cv2.destroyAllWindows()

    这些图像滤镜提供了一种富有创意和艺术感的方式来增强和转换您的图像。使用 OpenCV 和 Python,用户可以轻松应用这些滤镜来创建各种时尚且具有视觉吸引力的转换效果,从热和卡通转换到充满活力的 VSCO 风格外观和经典的灰度转换。

二、MoveNet Lightning 和 CV 实现实时姿势检测

  在本文中,我们将探讨如何使用 TensorFlow Lite 的 MoveNet Lightning 模型和 OpenCV 构建实时姿势检测系统。这个项目使我们能够使用网络摄像头检测身体关节并动态地可视化运动。 

    MoveNet Lightning 概述

    MoveNet 是由 TensorFlow 开发的最先进的姿态估计模型,专为实时应用程序而设计。MoveNet 的 Lightning 变体针对速度和准确性进行了优化,使其适用于健身跟踪、运动分析等任务。

    第 1 步:安装所需的库

    在开始之前,请确保您已安装以下 Python 库:

pip install tensorflow numpy opencv-python matplotlib

    这些库对于加载 MoveNet 模型、处理视频帧和可视化结果至关重要。

    第 2 步:加载 MoveNet 模型

    首先,我们将加载 TensorFlow Lite MoveNet Lightning 模型并分配张量进行推理。

import tensorflow as tf
import numpy as np
import cv2


# Load the TensorFlow Lite model
interpreter = tf.lite.Interpreter(model_path='3.tflite')
interpreter.allocate_tensors()

    第 3 步:定义辅助函数

    为了可视化检测到的姿势,我们需要在每一帧上绘制关键点 (关节) 和连接 (骨骼)。

    绘制关键点

def draw_keypoints(frame, keypoints, confidence_threshold):
    """
    Draws keypoints on the frame if their confidence exceeds the threshold.
    """
    y, x, c = frame.shape
    shaped = np.squeeze(np.multiply(keypoints, [y, x, 1]))


    for kp in shaped:
        ky, kx, kp_conf = kp
        if kp_conf > confidence_threshold:
            cv2.circle(frame, (int(kx), int(ky)), 4, (0, 255, 0), -1)

    绘制连接

EDGES = {
    (0, 1): 'm', (0, 2): 'c', (1, 3): 'm', (2, 4): 'c',
    (0, 5): 'm', (0, 6): 'c', (5, 7): 'm', (7, 9): 'm',
    (6, 8): 'c', (8, 10): 'c', (5, 6): 'y', (5, 11): 'm',
    (6, 12): 'c', (11, 12): 'y', (11, 13): 'm', (13, 15): 'm',
    (12, 14): 'c', (14, 16): 'c'
}


def draw_connections(frame, keypoints, edges, confidence_threshold):
    """
    Draws connections (edges) between keypoints if both exceed the threshold.
    """
    y, x, c = frame.shape
    shaped = np.squeeze(np.multiply(keypoints, [y, x, 1]))


    for edge, color in edges.items():
        p1, p2 = edge
        y1, x1, c1 = shaped[p1]
        y2, x2, c2 = shaped[p2]


        if (c1 > confidence_threshold) & (c2 > confidence_threshold):
            cv2.line(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2)

    第 4 步:实时姿势检测

    使用 OpenCV,我们将从网络摄像头捕获帧,并通过 MoveNet 处理它们以进行姿势检测。

# Initialize webcam capture
cap = cv2.VideoCapture(1)  # Use '0' for the default camera


while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break


    # Preprocess the frame for MoveNet
    img = frame.copy()
    img = tf.image.resize_with_pad(np.expand_dims(img, axis=0), 192, 192)
    input_image = tf.cast(img, dtype=tf.float32)


    # Get input and output tensor details
    input_details = interpreter.get_input_details()
    output_details = interpreter.get_output_details()


    # Run inference
    interpreter.set_tensor(input_details[0]['index'], np.array(input_image))
    interpreter.invoke()
    keypoints_with_scores = interpreter.get_tensor(output_details[0]['index'])


    # Draw connections and keypoints on the frame
    draw_connections(frame, keypoints_with_scores, EDGES, 0.4)
    draw_keypoints(frame, keypoints_with_scores, 0.4)


    # Display the frame
    cv2.imshow('MoveNet Lightning', frame)


    if cv2.waitKey(10) & 0xFF == ord('q'):
        break


cap.release()
cv2.destroyAllWindows()

    如何运行

  • 模型加载:TensorFlow Lite MoveNet 模型已加载并准备好进行推理。
  • 帧预处理:每个网络摄像头帧的大小都会调整并填充,以匹配模型的预期输入尺寸。
  • 姿势检测:该模型预测每帧的关键点及其置信度分数。
  • 可视化:关键点和连接叠加在框架上,实时动态更新。

    应用

    该项目具有多种应用:

  • 健身追踪和体型校正。
  • 交互式系统的手势识别。
  • 运动中的实时运动分析。

    通过利用 TensorFlow Lite 的 MoveNet 和 OpenCV,我们创建了一个功能强大且高效的姿势检测系统。这种设置是轻量级的,非常适合边缘设备上的实时应用程序。通过将该系统集成到健身或游戏应用程序中来进一步探索!

   源码下载:

https://github.com/iamramzan/Real-Time-Pose-Detection-Using-MoveNet-Lightning-and-OpenCV

三、CV修改一行代码,将图像匹配效果提升14%

OpenCV发布了4.5.1,包含了BEBLID算子,一个新的局部特征描述符,超越ORB。​

OpenCV 4.5.1中最令人兴奋的特性之一是BEBLID (Boosted Efficient Binary Local Image Descriptor),一个新的描述符能够提高图像匹配精度,同时减少执行时间!这篇文章将向你展示这个魔法是如何实现的。所有的源代码都在这个GitHub库中:https://github.com/iago-suarez/beblid-opencv-demo/blob/main/demo.ipynb

在这个例子中,我们将匹配这两个视角不一样的图像:

图片

首先,确保安装了正确的OpenCV版本是很重要的。在你喜欢的环境中,你可以通过以下方式安装并检查OpenCV Contrib版本:

pip install "opencv-contrib-python>=4.5.1"
python
>>> import cv2 as cv
>>> print(f"OpenCV Version: {cv.__version__}")
OpenCV Version: 4.5.1

在Python中加载这两个图像所需的代码是:

import cv2 as cv

# Load grayscale images
img1 = cv.imread("graf1.png", cv.IMREAD_GRAYSCALE)
img2 = cv.imread("graf3.png", cv.IMREAD_GRAYSCALE)

if img1 is None or img2 is None:
    print('Could not open or find the images!')
    exit(0)

为了评估我们的图像匹配程序,我们需要在两幅图像之间进行正确的(即ground truth)几何变换。它是一个称为单应性的3x3矩阵,当我们从第一个图像中乘以一个点(在齐次坐标中)时,它返回第二个图像中这个点的坐标。加载这个矩阵:

# Load homography (geometric transformation between image)
fs = cv.FileStorage("H1to3p.xml", cv.FILE_STORAGE_READ)
homography = fs.getFirstTopLevelNode().mat()
print(f"Homography from img1 to img2:\n{homography}")

下一步是检测图像中容易在其他图像中找到的部分:Local image features。在本例中,我们将使用ORB,一个快速可靠的检测器来检测角点。ORB检测到强角,在不同的尺度上比较它们,并使用FAST或Harris响应来挑选最好的。它还使用局部patch的一阶矩来寻找每个角点的方向。我们检测每个图像中最多10000个角点:

detector = cv.ORB_create(10000)
kpts1 = detector.detect(img1, None)
kpts2 = detector.detect(img2, None)

在下面的图片中,你可以看到500个用绿点标记的检测响应最强的角点特征:

图片

很好,现在是时候以一种我们可以在另一张图中找到它们的方式来表示这些关键点了。这个步骤被称为description,因为每个角点的局部patch中的纹理表示 为图像上不同操作得到的数字的向量。有很多的描述符可以用,但如果我们想要一些精确的东西,即使在移动电话或低功耗设备上也能实时运行,OpenCV有两个重要的方法:

  • ORB(导向快速和旋转简短):一个经典的方法,有10年的历史,工作相当好。
  • BEBLID (Boosted Efficient Binary Local Image Descriptor):2020年引入的一个新的描述符,已被证明在几个任务中改善了ORB。由于BEBLID适用于多种检测方法,所以必须将ORB关键点的比例设置为0.75~1。
# Comment or uncomment to use ORB or BEBLID
descriptor = cv.xfeatures2d.BEBLID_create(0.75)
# descriptor = cv.ORB_create()
kpts1, desc1 = descriptor.compute(img1, kpts1)
kpts2, desc2 = descriptor.compute(img2, kpts2)

现在可以匹配这两个图像的描述符来建立对应关系了。让我们使用暴力求解算法,它基本上比较了第一张图像中的每个描述符和第二张图像中的所有描述符。当我们处理二进制描述符时,使用汉明距离进行比较,即计算每对描述符之间不同的比特数。

这里还使用了一个叫做比率检验的小技巧。它不仅确保描述符1和2彼此相似,而且确保没有其他像2一样接近1的描述符。

matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_BRUTEFORCE_HAMMING)
nn_matches = matcher.knnMatch(desc1, desc2, 2)
matched1 = []
matched2 = []
nn_match_ratio = 0.8  # Nearest neighbor matching ratio
for m, n in nn_matches:
    if m.distance < nn_match_ratio * n.distance:
        matched1.append(kpts1[m.queryIdx])
        matched2.append(kpts2[m.trainIdx])

因为我们知道正确的几何变换,让我们检查有多少匹配是正确的(inliners)。如果图像2中的点和从图像1投射到图像2的点距离小于2.5像素,我们认为匹配是有效的。

inliers1 = []
inliers2 = []
good_matches = []
inlier_threshold = 2.5  # Distance threshold to identify inliers with homography check
for i, m in enumerate(matched1):
    # Create the homogeneous point
    col = np.ones((3, 1), dtype=np.float64)
    col[0:2, 0] = m.pt
    # Project from image 1 to image 2
    col = np.dot(homography, col)
    col /= col[2, 0]
    # Calculate euclidean distance
    dist = sqrt(pow(col[0, 0] - matched2[i].pt[0], 2) + pow(col[1, 0] - matched2[i].pt[1], 2))
    if dist < inlier_threshold:
        good_matches.append(cv.DMatch(len(inliers1), len(inliers2), 0))
        inliers1.append(matched1[i])
        inliers2.append(matched2[i])

现在我们在inliers1和inliers2变量中有了正确的匹配,我们可以使用cv.drawMatches定性地评估结果。每一个对应点可以在更高级别的任务上对我们有帮助,比如homography estimation,Perspective-n-Point, plane tracking, real-time pose estimation 以及 images stitching。

图片

由于很难定性地比较这种结果,让我们绘制一些定量的评价指标。最能反映描述符可靠程度的指标是inlier的百分比:

图片

Matching Results (BEBLID)
*******************************
# Keypoints 1:                          9105
# Keypoints 2:                          9927
# Matches:                              660
# Inliers:                              512
# Percentage of Inliers:                77.57%

使用BEBLID描述符获得77.57%的inliers。如果我们在描述符部分注释掉BEBLID并取消注释ORB描述符,结果下降到63.20%

# Comment or uncomment to use ORB or BEBLID
# descriptor = cv.xfeatures2d.BEBLID_create(0.75)
descriptor = cv.ORB_create()
kpts1, desc1 = descriptor.compute(img1, kpts1)
kpts2, desc2 = descriptor.compute(img2, kpts2)
Matching Results (ORB)
*******************************
# Keypoints 1:                          9105
# Keypoints 2:                          9927
# Matches:                              780
# Inliers:                              493
# Percentage of Inliers:                63.20%

总之,只需更改一行代码,将ORB描述符替换为BEBLID ,就可以将这两个图像的匹配结果提高14%。这在需要局部特征匹配的高级任务中会产生很大影响,所以不要犹豫,试试BEBLID

英文原文:​​https://towardsdatascience.com/improving-your-image-matching-results-by-14-with-one-line-of-code-b72ae9ca2b73​

四、眨眼检测

先决条件

    首先,请确保您已安装以下软件:

  • Python 3.x
  • OpenCV(pip install opencv-python和pip install opencv-python-headless)

    此外,下载用于面部和眼睛检测的 Haar Cascade XML 文件:

   haarcascade_frontalface_default.xml

   haarcascade_eye_tree_eyeglasses.xml

https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml
https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml

    将这些文件放在你的项目目录中。

具体实现步骤

    1. 导入库

    首先,导入所需的库:

import numpy as np
import cv2

    2. 加载 Haar Cascades

    初始化 Haar Cascade 分类器以进行人脸和眼睛检测:

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')eye_cascade = cv2.CascadeClassifier('haarcascade_eye_tree_eyeglasses.xml')

    3. 初始化变量

    我们使用一个变量first_read来控制检测过程:

first_read = True

    4. 获取视频帧

    设置网络摄像头进行实时处理:

cap = cv2.VideoCapture(1)  # Use 0 if there's only one camera
ret, img = cap.read()

    5. 处理每一帧

    处理视频的每一帧以检测面部和眼睛:

while ret:
    ret, img = cap.read()
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # Convert to grayscale
    gray = cv2.bilateralFilter(gray, 5, 1, 1)  # Apply a bilateral filter


    faces = face_cascade.detectMultiScale(gray, 1.3, 5, minSize=(200, 200))
    if len(faces) > 0:
        for (x, y, w, h) in faces:
            img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
            roi_face = gray[y:y + h, x:x + w]
            roi_face_clr = img[y:y + h, x:x + w]


            eyes = eye_cascade.detectMultiScale(roi_face, 1.3, 5, minSize=(50, 50))


            if len(eyes) >= 2:
                if first_read:
                    cv2.putText(img, "Eye detected. Press 's' to begin", (70, 70),
                                cv2.FONT_HERSHEY_PLAIN, 3, (0, 255, 0), 2)
                else:
                    cv2.putText(img, "Eyes open!", (70, 70),
                                cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 255), 2)
            else:
                if first_read:
                    cv2.putText(img, "No eyes detected", (70, 70),
                                cv2.FONT_HERSHEY_PLAIN, 3, (0, 0, 255), 2)
                else:
                    print("Blink detected.")
                    cv2.waitKey(3000)
                    first_read = True
    else:
        cv2.putText(img, "No face detected", (100, 100),
                    cv2.FONT_HERSHEY_PLAIN, 3, (0, 255, 0), 2)


    cv2.imshow('img', img)
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
    elif key == ord('s') and first_read:
        first_read = False


cap.release()
cv2.destroyAllWindows()

    面部和眼睛检测

    该程序使用 Haar Cascade 分类器实时检测面部和眼睛。该detectMultiScale函数可识别可能包含所需对象的区域。

    眨眼检测逻辑

    检测逻辑很简单:

  • 如果检测到眼睛,系统确认眼睛是睁开的。
  • 如果没有检测到眼睛,系统就会认为发生了眨眼。

    用户交互

  • 按下s即可开始检测过程。
  • 按下q退出程序。

    应用

  • 疲劳监测:监测眨眼情况以检测驾驶员是否困倦。
  • 手势控制:使用眨眼作为信号来控制设备。
  • 医疗保健:跟踪眼球运动以用于医疗目的。

源码下载:

https://github.com/iamramzan/Eye-Blink-Detection-Using-OpenCV

五、单目摄像机测距

论文方向目前是使用单目摄像头实现机器人对人的跟随,首先单目摄像头与kinect等深度摄像头最大的区别是无法有效获取深度信息,那就首先从这方面入手,尝试通过图像获取摄像头与人的距离。        在网上看了几天关于摄像头标定和摄像头焦距等原理的文章,然后通过这篇文章真正启发了我:用python和opencv来测量目标到相机的距离  主要的测距的原理是利用相似三角形计算物体到相机的距离。在这里我的环境为: Ubuntu14.04 + Opencv2.4.9

一 用相似三角形计算物体或者目标到相机的距离

我们将使用相似三角形来计算相机到一个已知的物体或者目标的距离。

相似三角形就是这么一回事:假设我们有一个宽度为 W 的目标或者物体。然后我们将这个目标放在距离我们的相机为 D 的位置。我们用相机对物体进行拍照并且测量物体的像素宽度 P 。这样我们就得出了相机焦距的公式:

F = (P x D) / W

举个例子,假设我在离相机距离 D = 24 英寸的地方放一张标准的 8.5 x 11 英寸的 A4 纸(横着放;W = 11)并且拍下一张照片。我测量出照片中 A4 纸的像素宽度为 P = 249 像素。

因此我的焦距 F 是:

F = (248px x 24in) / 11in = 543.45

当我继续将我的相机移动靠近或者离远物体或者目标时,我可以用相似三角形来计算出物体离相机的距离:

D’ = (W x F) / P

为了更具体,我们再举个例子,假设我将相机移到距离目标 3 英尺(或者说 36 英寸)的地方并且拍下上述的 A4 纸。通过自动的图形处理我可以获得图片中 A4 纸的像素距离为 170 像素。将这个代入公式得:

D’ = (11in x 543.45) / 170 = 35 英寸

或者约 36 英寸,合 3 英尺。

从以上的解释中,我们可以看到,要想得到距离,我们就要知道摄像头的焦距和目标物体的尺寸大小,这两个已知条件根据公式:  

D’ = (W x F) / P 

得出目标到摄像机的距离D,其中P是指像素距离,W是A4纸的宽度,F是摄像机焦距。

  在原文中,是通过预先拍照,根据第一张照片算出摄像头的焦距,在根据已知的焦距算出接下来的照片中白纸到摄像机的距离,这样不太直观,而且需要预先拍照,我将源程序改为实时测距,简单来说就是将原来的读入照片变为读摄像头,这样的效果看起来比较直观.源程序如下:

图片

程序效果图如下:

图片

在这张图里我摄像头距离桌面大概100cm,可以看到图中距离为96cm,可以看到精度还可以。

需要注意的是, 如果使用的是opencv3的版本, 

1. 需要将find_marker函数中

(cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

改为:

(_, cnts, _) = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

因为 In Opencv 3 API version the  cv2.findCoutours()  returns 3  object

  • image
  • contours
  • hierarchy

2. 需要将:

box = np.int0(cv2.cv.BoxPoints(marker))

改为:

box = cv2.boxPoints(marker)
 box = np.int0(box)

以上两个地方是安装不同版本opencv需要修改的地方。

存在的问题:

1. 程序在运行时在未检测到A4纸时有时候会报错:

Traceback (most recent call last):
  File "video_paper_distance.py", line 86, in <module>
    marker = find_marker(frame)
  File "video_paper_distance.py", line 18, in find_marker
    c = max(cnts, key = cv2.contourArea)

ValueError: max() arg is an empty sequence

目前关于这个错误,我还没有解决,猜测主要是由于没有检测到目标造成max()函数为空的原因,不过没有深究。

2. 程序是通过第一张图已知目标到相机的距离来计算摄像头焦距,然后再通过焦距计算接下来目标到摄像头的距离,在这里焦距是一个关键的参数,所以我准备尝试通过对摄像头的标定直接获取相机的像素焦距,我是通过ros的一个包实现了对相机的标定,不过通过相机标定得出的像素焦距计算出来的距离并没有通过第一张图片计算出的焦距计算出来的距离准确,这个具体原因也没有搞明白,可能是我标定的结果不够准确?

3. 在通过摄像头测距时, 得出的距离也是准确且随着摄像头距离桌面远近而线性变化的,但距离偶尔会出现突变,目前也没找到是什么原因造成的.

ros相机标定主要参考的是这篇博客,博主是白巧克力亦唯心,ROS大神:

ROS 教程之 vision: 摄像头标定camera calibration

这里主要记录的是,通过摄像机标定,得到的3*3的内参数矩阵,其中M[1][1]和M[2][2]分别为我们要求的相机的x,y轴的像素焦距。

二 使用相机计算人到相机的距离

  在第一部分中我们已经计算出了A4纸距离相机的距离,在具体应用中,我需要计算的是人距离相机的距离,来实现机器人对目标人距离的判断,应用与对目标人的跟随。在这里主要的思路是先通过opencv中的HOG方法检测到人,再根据人的预估身高和摄像头焦距计算人到摄像机的距离。在这里选择身高的原因在于人的身高在不同方向上变化较小,而且我们的摄像头高度是固定的,所以选择身高。

1.首先要使用opencv进行行人检测:

图片

2.将行人检测与测距代码结合:

图片

3.存在的问题:

  目前使用HOG检测行人的效果不是很好,会把类似人体形状的物体都框出来,比如实验室的三脚架等物体,受背景干扰较大。程序中存在一个bug就是在没有检测到人时,pix_person_height会为0,这样分母为0时无法计算,在接下来我也要通过3个方面改进,首先要想办法进一步改进人体检测,使用YOLO的方法目前是比较好的,但在CPU下速度较慢。然后要改进的是精度,这里需要主要的是选择摄像头要选择固定焦距的摄像头,自动变焦摄像头焦距会变化,测量的距离也会变。最后就是尽可能完善程序,减少bug。

4 . 将要进行的工作

      通过程序可以看到使用单目摄像头检测人到摄像头的距离,其中一个影响较大的因素是对人体的准确检测,如果想要使测量的距离准确(完全准确是不可能的,但要达到可以用于机器人跟随人的功能的程度),那就要尽可能的准确的检测出人,通过我的测试,在准确知道目标人的身高前提下,在离摄像头固定距离上对人拍照,然后手动对人进行画框,标定出目标人的在画面中的高度,通过计算,得到的距离比较准确,其精度完全是可以接受的,所以接下来的工作主要是如何通过程序来准确的框出目标人来获取其在图像中的高度。

六、CV ~ 6 种自动对焦算法介绍、实现与效果对比

自动对焦在成像系统中起着至关重要的作用,可确保捕获的图像和视频帧清晰且清晰。在各种应用中,例如医学成像、监控和摄影,从序列中选择最清晰的帧可以显著提高分析和演示的质量。在本文中,我们探讨了使用 OpenCV 的各种焦点测量运算符,OpenCV 是一个广泛用于图像处理任务的强大计算机视觉库。通过利用 OpenCV 的内置功能,我们计算和比较不同的焦点测量,以评估图像清晰度并确定视频序列中的最佳焦点帧。通过这种分析,我们深入了解了不同作员的有效性、他们的优缺点以及它们在自动成像系统中的实际应用。

准备工作

    1. 安装需要的库

pip install opencv-python numpy scikit-image

    2. 测试数据

图片

自动对焦算法介绍

    1. 局部方差方法

    Local Variance 方法测量局部强度变化,假设与模糊图像相比,清晰图像表现出更高的对比度和方差。它计算邻域内像素强度的方差,使其可用于检测图像中的焦点。

    优点:

    简单快速 – 计算效率高且易于实施。

    对高对比度图像有效 – 当图像具有较强的边缘和强度变化时,效果很好。

    噪声稳健性(在一定程度上) – 与基于梯度的方法相比,受像素强度微小变化的影响较小。

    缺点:

    在低对比度图像上失败 – 如果图像具有均匀的强度(例如,模糊但亮度高),则可能会错误地分配高分。

    对照明变化敏感 – 不均匀的照明会影响变化,从而导致清晰度评估不准确。

    不是自动对焦的最佳选择 – 与基于渐变的方法相比,性能不一致。

    2. 基于熵的焦点测量

    基于熵的焦点测量通过分析强度值的分布来量化图像清晰度。清晰的图像包含更详细的信息并具有较高的熵值,而模糊的图像往往具有更均匀的强度分布和较低的熵。

    优点:

    适用于纹理丰富的图像 – 最适合具有大量详细纹理和图案的图像。

    耐少量噪点 – 仍然可以在轻微噪点的图像中提供可靠的结果。

    适用于低对比度情况 – 当对比度较低时,性能优于基于方差的方法。

    缺点:

    计算成本高 – 需要直方图计算和对数计算,因此比方差或梯度方法慢。

    对高频区域中的噪声敏感 – 如果图像的噪声过多,则由于随机性增加,熵可能会错误地将其识别为“清晰”。

    不适用于基于边缘的清晰度检测 – 在清晰度主要由边缘而不是纹理定义的图像中效果不佳。

    3. Tenengrad(基于 Sobel 梯度)焦点测量

    Tenengrad 方法根据边缘强度评估图像清晰度。它依赖于 Sobel 算子,该算子可以检测水平和垂直方向的强度变化。假设是更清晰的图像包含更强的边缘,从而导致更高的渐变值。

    优点:

    强边缘检测 – 适用于具有强边缘的图像中的锐度检测。

    耐照明变化 – 与基于方差的方法相比,更稳健。

    适用于自动对焦应用 – 常用于自动对焦系统,因为它可以有效地检测急剧过渡。

    缺点:

    对噪声敏感 – 微小的噪声变化会产生强烈的梯度,从而导致错误的锐化检测。

    在纹理丰富的低边缘图像中失败 – 如果图像非常详细但缺乏清晰的边缘,该方法可能会低估清晰度。

    方向灵敏度 – 如果图像中的结构与渐变方向对齐,则可能会产生误导性结果。

    4. Brenner 梯度焦点测量

    Brenner 渐变方法根据相邻像素之间的像素强度差异来评估图像清晰度。假设更清晰的图像具有更大的强度变化,从而导致更高的焦点分数。

    优点:

    非常简单快捷 – 比 Sobel 或 Laplacian 方法更快,因为它只使用像素级差异。

    对基于边缘的锐度有效 – 适用于检测像素强度的急剧变化。

    适用于散焦图像 – 在模糊图像中,性能优于基于变化的方法。

    缺点:

    不是很可靠 – 在低对比度或纹理丰富的图像中可能不可靠。

    对噪声敏感 – 可能会错误地将噪声模式归类为尖锐特征。

    在一般应用程序中的有限使用 – 适用于特定的自动对焦场景,但不适用于所有类型的图像。

    5. Sobel + 方差组合

    此方法通过结合两种方法增强焦点测量:

    Sobel Gradient (索贝尔梯度) – 通过计算水平和垂直方向的强度变化来测量图像中的边缘强度。

    Local Intensity Variance (局部强度方差) – 通过分析像素强度的变化来确定清晰度,假设更清晰的图像具有更高的对比度和方差。

    通过结合这两种测量方法,这种方法提供了更稳健的焦点评估,同时捕获了边缘清晰度和局部强度变化。

    优点:

    平衡边缘和强度信息 – 比单独使用 Sobel 或方差更强大。

    处理基于边缘和基于纹理的清晰度 – 对各种类型的图像有效。

    在自动对焦应用中更可靠 – 减少由于噪声引起的误分类。

    缺点:

    Computationally More Expensive (计算成本更高) – 由于多次计算,速度略慢。

    仍然对噪声有些敏感 – 虽然它比单独的方法更强大,但它在处理极度嘈杂的图像时可能仍然难以理解。

    需要适当的参数调整 – 性能取决于选择合适的 Sobel 内核大小和方差阈值。

    6. 基于 Laplacian 的方法

    此方法使用 Laplacian 算子(二阶导数滤波器)来检测图像中的边缘。它测量拉普拉斯响应的方差,假设更清晰的图像具有更强的边缘响应和更高的方差,而模糊的图像具有更平滑的过渡和较低的方差。

    优点:

    捕捉高频清晰度 – 更强地检测精细细节和细微边缘。

    适用于一般自动对焦应用 – 用于许多图像处理和显微镜自动对焦系统。

    受全局光照变化的影响较小 – 与基于变化的方法相比,在不同的光照条件下更稳定。

    缺点:

    对噪声非常敏感 – 与基于 Sobel 的方法相比,更容易放大噪声。

    计算成本高 – 比更简单的运算符(如方差或 Brenner 梯度)略慢。

    边缘过度加重会导致误报 – 如果边缘对比度过高(例如,由于噪声或伪影),可能会错误地将图像归类为“清晰”。

自动对焦算法代码实现

  • 逐个读取视频帧。
  • 使用选定的焦点度量计算焦点分数。
  • 存储帧及其分数以供以后比较。
  • 选择对焦得分最高的帧作为最清晰的帧。
  • 打印出最佳帧编号及其焦点分数。
  • 显示最佳帧以直观地验证每种方法的有效性。
import cv2
import numpy as np
videoPath = "C:/Users/ssabb/Desktop/opencv_courses/articles/data/autofocus1.mp4"
cap = cv2.VideoCapture(videoPath)
focus_scores = []
frames = []
# Include the function for focus measures here
# def compute_focus_measure(image):
#     Implement the focus measure function here (e.g., Tenengrad, Brenner, etc.)
if cap.isOpened():
    print("Video file successfully retrieved")
else:
    print("Video file wasn't retrieved properly")
while True:
    ret, frame = cap.read()
    if ret:
        focus_scores.append(compute_focus_measure(frame))  # Replace with actual function
        frames.append(frame)
    else:
        print("Frame not read")
        break
cap.release()
# Find the best-focused frame
best_score_index = np.argmax(focus_scores)
best_frame = frames[best_score_index]
print(f"Best frame index: {best_score_index} with score {focus_scores[best_score_index]}")
# Get the original dimensions
original_height, original_width = best_frame.shape[:2]
new_width = 600
aspect_ratio = new_width / original_width
new_height = int(original_height * aspect_ratio)  # Compute height based on aspect ratio
# Resize the image
resized_image = cv2.resize(best_frame, (new_width, new_height))
# Display the best frame
cv2.imshow("Best Frame", resized_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

    1. 局部方差方法

# Function to compute Local Variance focus measure
def compute_local_variance(image, ksize=5):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    mean = cv2.blur(gray, (ksize, ksize))
    squared_mean = cv2.blur(gray**2, (ksize, ksize))
    variance = squared_mean - (mean**2)
    return np.mean(variance)

    2. 基于熵的焦点测量

from skimage.measure import shannon_entropy
import cv2


def compute_entropy(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return shannon_entropy(gray)

    3. Tenengrad(基于 Sobel 梯度)焦点测量

def compute_tenengrad(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # Convert to grayscale
    sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)  # Sobel filter in X direction
    sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)  # Sobel filter in Y direction
    tenengrad = np.sqrt(sobel_x**2 + sobel_y**2)  # Compute gradient magnitude
    return np.mean(tenengrad)  # Return mean gradient magnitude as focus score

    4. Brenner 梯度焦点测量

def compute_brenner_gradient(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # Convert to grayscale
    shifted = np.roll(gray, -2, axis=1)  # Shift by 2 pixels horizontally
    diff = (gray - shifted) ** 2  # Compute squared difference
    return np.sum(diff)  # Sum all differences as the focus measure

    5. Sobel + 方差组合

def compute_sobel_variance(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # Convert to grayscale
    sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)  # Sobel X gradient
    sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)  # Sobel Y gradient
    sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2)  # Compute gradient magnitude
    variance = np.var(gray)  # Compute variance of pixel intensities
    return np.mean(sobel_magnitude) + variance  # Combine Sobel and variance

    6. 基于 Laplacian 的方法

def compute_laplacian(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # Convert to grayscale
    laplacian = cv2.Laplacian(gray, cv2.CV_64F)  # Apply Laplacian filter
    return np.var(laplacian)  # Compute variance of Laplacian

效果对比

    性能比较

    我们根据各种焦点算子识别最佳聚焦帧的能力来分析和比较它们的性能。

  • Local Variance 和 Entropy 失败了,因为它们依赖于与焦点没有很强相关性的强度统计数据。
  • Brenner 梯度表现适中,但效果略低于基于梯度的方法。
  • Sobel + Variance、Tenengrad 和 Laplacian 是最好的方法,因为它们有效地捕获了边缘清晰度。

    因此,要选择最清晰的帧,Tenengrad、Laplacian 和 Sobel + Variance 应该是首选。

七、一维码和QRCode扫描仪

介绍使用Python和OpenCV搭建自己的一维码和QRCode扫描仪(

项目简介

    本文我们将创建一个程序来扫描图像中的二维码和条形码。对于这个程序,我们需要三个包,分别是OpenCV、NumPy和pyzbar。大多数 Python 程序员都熟悉 OpenCV 和 Numpy 库。OpenCV 是一个开源计算机视觉和机器学习库。它是一个有用的图像处理库。我们在项目中使用这个库来处理设备捕获的视频中的每一帧。我们在这里使用 Numpy,因为 pyzbar 可与 OpenCV / numpy ndarrays 配合使用。而 pyzbar 库用于从给定图像中读取条形码和 QR 码。它支持 EAN-13/UPC-A、UPC-E、EAN-8、Code 128、Code 39、Interleaved 2 of 5 和 QR 码。这是对我们的项目和库的简短介绍,下面将详细介绍实现步骤。

实现步骤

    【1】安装需要的库。主要有OpenCV, Numpy, PyZbar。

#安装OpenCV
pip install opencv-python
#安装pyzbar
pip install pyzbar
#安装numpy
pip install numpy

    【2】导入所有需要的库。代码如下:

import cv2
import numpy as np
from pyzbar.pyzbar import decode

    【3】从摄像头设备捕获视频。代码如下:

cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    cv2.imshow('image', frame)
    code = cv2.waitKey(10)
    if code == ord('q'):
        break

    【4】创建解码器函数,用于解码给定图像中的条形码和QRCode。代码如下:

def decoder(image):
    gray_img = cv2.cvtColor(image,0)
    barcode = decode(gray_img)


    for obj in barcode:
        points = obj.polygon
        (x,y,w,h) = obj.rect
        pts = np.array(points, np.int32)
        pts = pts.reshape((-1, 1, 2))
        cv2.polylines(image, [pts], True, (0, 255, 0), 3)


        barcodeData = obj.data.decode("utf-8")
        barcodeType = obj.type
        string = "Data: " + str(barcodeData) + " | Type: " + str(barcodeType)
        
        cv2.putText(frame, string, (x,y), cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,255), 2)
        print("Barcode: "+barcodeData +" | Type: "+barcodeType)

    该函数获取图像,然后从图像中识别 QR 码和条形码,并对其值进行解码。这里的条形码是由解码函数识别的条形码和QRcode对象的列表。每个对象包含矩形、多边形、数据、类型等属性。矩形和多边形属性给出了条形码和QR码的位置。

    完整代码和检测效果:

import cv2
import numpy as np
from pyzbar.pyzbar import decode


def decoder(image):
    gray_img = cv2.cvtColor(image,0)
    barcode = decode(gray_img)


    for obj in barcode:
        points = obj.polygon
        (x,y,w,h) = obj.rect
        pts = np.array(points, np.int32)
        pts = pts.reshape((-1, 1, 2))
        cv2.polylines(image, [pts], True, (0, 255, 0), 3)


        barcodeData = obj.data.decode("utf-8")
        barcodeType = obj.type
        string = "Data " + str(barcodeData) + " | Type " + str(barcodeType)
        
        cv2.putText(frame, string, (x,y), cv2.FONT_HERSHEY_SIMPLEX,0.8,(255,0,0), 2)
        print("Barcode: "+barcodeData +" | Type: "+barcodeType)


cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    decoder(frame)
    cv2.imshow('Image', frame)
    code = cv2.waitKey(10)
    if code == ord('q'):
        break

图片

八、文本检测和擦除

先决条件

    1. 安装必要的库

pip install opencv-python numpy

    2. 下载预先训练的模型

    为了执行文本检测,我们将使用三个预训练模型。从以下链接下载所需的模型

    EAST(高效准确的场景文本检测器):

https://www.dropbox.com/s/r2ingd0l3zt8hxs/frozen_east_text_detection.tar.gz?dl=1

    DB50(以 ResNet-50 为骨干的可区分二值化):

https://drive.google.com/uc?export=dowload&id=19YWhArrNccaoSza0CfkXlA8im4-lAGsR

    DB18(以 ResNet-18 为骨干的可区分二值化):

https://drive.google.com/uc?export=dowload&id=1sZszH3pEt8hliyBlTmB-iulxHP1dCQWV

    3. 数据集

    在本教程中,我们将使用三张包含不同风格和背景的文本的示例图像。您可以使用自己的图像或下载示例图像。使用这些图像,我们将测试EAST、DB50 和 DB18模型检测文本的效果以及 OpenCV修复技术删除文本的效果。

OpenCV 中的文本检测模型

    在本节中,我们将探讨 OpenCV 中提供的三种文本检测模型:EAST、DB50 和 DB18。下面是简要概述以及每个模型的语法及其工作原理。

    每个模型都有独特的优势,适合不同类型的文本检测任务。

    EAST(高效准确的场景文本检测器)

    EAST (高效准确场景文本检测器)模型是一种基于深度学习的文本检测方法,专为实时应用而设计。它使用全卷积神经网络 (FCN)和特征金字塔网络 (FPN)以高精度检测自然场景中的文本。EAST 可生成轴对齐和旋转的边界框,因此可有效检测任意方向的文本。

优点:

  • 对于实时应用来说快速且高效。
  • 可以检测旋转的文本。
  • 提供四边形边界框。

 缺点:

  • 可能难以处理小文本或低对比度的文本。
  • 在杂乱的背景中可能会产生误报。

    DB50(基于 ResNet-50 主干的可区分二值化)

    DB50模型基于可微分二值化 (DB) 方法,该方法通过细化特征图来生成准确的文本边界。此模型使用ResNet-50主干,使其在高精度文本检测方面更加强大。DB50 模型非常适合检测密集且形状不规则的文本区域。

 优点:

  • 检测精度高。
  • 适用于复杂的文本布局。
  • 适用于检测小型和弯曲的文本。

   缺点:

  • 由于架构更深,所以比 EAST 慢。
  • 计算量更大。

    DB18(基于 ResNet-18 主干的可区分二值化)

    DB18模型是 DB50 的轻量版,使用ResNet-18主干。虽然它采用相同的可微分二值化方法,但它针对速度进行了优化,使其成为资源有限环境的更好选择。它在检测准确率和计算效率之间实现了平衡。

 优点:

  • 比 DB50 更快。
  • 适用于实时应用。
  • 适用于中等复杂的文本布局。

    缺点:

  • 与 DB50 相比准确度略低。
  • 可能会难以处理高度复杂的文本区域。

每个模型的使用方法

    以下是初始化和使用 OpenCV 中的EAST、DB50和DB18模型进行文本检测的语法和说明。

    EAST 模型

textDetectorEAST= cv2.dnn_TextDetectionModel_EAST(modelPath)
textDetectorEAST.setConfidenceThreshold(conf_thresh)
textDetectorEAST.setNMSThreshold(nms_thresh)
textDetectorEAST.setInputParams(scale, inputSize, mean, swapRB)
boxes, confidences = textDetectorEAST.detect(image)

参数说明

  • modelPath – 预先训练的EAST模型 (.pb 文件) 的路径。
  • setConfidenceThreshold(conf_thresh) – 认为检测有效的最低置信度分数。
  • setNMSThreshold(nms_thresh) – 用于过滤重叠文本框的非最大抑制 (NMS) 阈值。
  • setInputParams(scale, inputSize, mean, swapRB) – 预处理参数:
  • scale – 图像标准化的缩放因子(例如 1.0)。
  • inputSize – 模型预期的输入大小(例如,(320, 320))。
  • 平均值 – 标准化的平均值(例如,(123.68、116.78、103.94))。
  • swapRB – 用于交换红色和蓝色通道的布尔标志(对于 OpenCV 通常为 True)。
  • 检测(图像)– 对输入图像执行文本检测,返回检测到的文本框和置信度分数。

    DB50 模型(使用 ResNet-50 进行可区分二值化)

textDetectorDB50= cv2.dnn_TextDetectionModel_DB(modelPath)
textDetectorDB50.setBinaryThreshold(bin_thresh)
textDetectorDB50.setPolygonThreshold(poly_thresh)
textDetectorDB50.setInputParams(scale, inputSize, mean, swapRB)
boxes, confidences = textDetectorDB50.detect(image)

  参数说明

  • modelPath – DB50模型 (.onnx 文件)的路径。
  • setBinaryThreshold(bin_thresh) – 文本分割的二值化阈值。
  • setPolygonThreshold(poly_thresh) – 用于确定检测到的文本的多边形形状的阈值。
  • setInputParams(scale, inputSize, mean, swapRB) – 与 EAST 模型预处理参数相同。
  • 检测(图像)——运行文本检测并返回文本边界框。

    DB18 模型(使用 ResNet-18 进行可区分二值化)

textDetectorDB18= cv2.dnn_TextDetectionModel_DB(modelPath)
textDetectorDB18.setBinaryThreshold(bin_thresh)
textDetectorDB18.setPolygonThreshold(poly_thresh)
textDetectorDB18.setInputParams(scale, inputSize, mean, swapRB)
boxes, confidences = textDetectorDB18.detect(image)

    参数说明

  • modelPath – DB18模型 (.onnx 文件)的路径。
  • setBinaryThreshold(bin_thresh) – 定义文本检测期间二值化的阈值。
  • setPolygonThreshold(poly_thresh) – 定义如何确定多边形文本区域。
  • setInputParams(scale, inputSize, mean, swapRB) – 与 EAST 和 DB50 模型相同。
  • 检测(图像)——执行文本检测并返回检测到的文本框。

使用 OpenCV 删除文本

  • Navier-Stokes 修复 (cv2.INPAINT_NS) – 该方法利用流体动力学原理从周围区域传播图像信息,使其对小、光滑或均匀的区域有效,但不太适合复杂的纹理。
  • Telea 的修复 (cv2.INPAINT_TELEA) – 使用快速行进算法通过考虑像素梯度来迭代填充缺失区域,从而产生更自然的结果,特别是在具有复杂纹理和边缘的区域。

    在本文中,我们将演示 Tealea 的修复算法。

用法:

inpainted_image = cv2.inpaint(src, mask, inpaintRadius, flags)

 参数说明:

  • src – 要从中删除文本的输入图像。
  • mask – 与 src 大小相同的二进制掩码,其中白色(255)像素表示要删除的文本区域。
  • inpaintRadius – 定义用于填充缺失区域的邻域大小(以像素为单位)。通常,值为 3-5 效果较好。
  • flags – 指定修复算法:
  • cv2.INPAINT_NS – 使用Navier-Stokes 方法。
  • cv2.INPAINT_TELEA – 使用Telea 的方法(适用于文本删除)。

实现文本检测和删除

    我们将使用三个模型实现文本检测:EAST、DB50 和 DB18,然后使用OpenCV 的修复功能删除检测到的文本。

mport cv2
import numpy as np
# Load the input image
image_path = "C:/Users/ssabb/Downloads/test2.png"
image1 = cv2.imread(image_path)
image = cv2.resize(image1, (320, 320))
# Create copies for different models
annotated_image = image.copy()  # For all models
annotated_db50_image = image.copy()  # For DB50
annotated_db18_image = image.copy()  # For DB18
annotated_east_image = image.copy()  # For EAST
orig_image = image.copy()  # For inpainting (all models)
orig_db50_image = image.copy()  # For DB50
orig_db18_image = image.copy()  # For DB18
orig_east_image = image.copy()  # For EAST
# Set input image size
inputSize = (320, 320)
# Load pre-trained models
textDetectorEAST = cv2.dnn_TextDetectionModel_EAST("C:/Users/ssabb/Desktop/opencv_courses/c0/Text_Det/resources/frozen_east_text_detection.pb")
textDetectorDB50 = cv2.dnn_TextDetectionModel_DB("C:/Users/ssabb/Desktop/opencv_courses/c0/Text_Det/resources/DB_TD500_resnet50.onnx")
textDetectorDB18 = cv2.dnn_TextDetectionModel_DB("C:/Users/ssabb/Desktop/opencv_courses/c0/Text_Det/resources/DB_TD500_resnet18.onnx")
# Set parameters for the models
conf_thresh = 0.8
nms_thresh = 0.4
bin_thresh = 0.3
poly_thresh = 0.5
mean = (122.67891434, 116.66876762, 104.00698793)
textDetectorEAST.setConfidenceThreshold(conf_thresh).setNMSThreshold(nms_thresh)
textDetectorEAST.setInputParams(1.0, inputSize, (123.68, 116.78, 103.94), True)
textDetectorDB18.setBinaryThreshold(bin_thresh).setPolygonThreshold(poly_thresh)
textDetectorDB18.setInputParams(1.0/255, inputSize, mean, True)
textDetectorDB50.setBinaryThreshold(bin_thresh).setPolygonThreshold(poly_thresh)
textDetectorDB50.setInputParams(1.0/255, inputSize, mean, True)
# Create inpainting masks
inpaint_mask = np.zeros(image.shape[:2], dtype=np.uint8)  # Mask for all models
inpaint_mask_db50 = np.zeros(image.shape[:2], dtype=np.uint8)  # Mask for DB50 only
inpaint_mask_db18 = np.zeros(image.shape[:2], dtype=np.uint8)  # Mask for DB18 only
inpaint_mask_east = np.zeros(image.shape[:2], dtype=np.uint8)  # Mask for EAST only
# Detect text using the models
boxesEAST, _ = textDetectorEAST.detect(image)
boxesDB18, _ = textDetectorDB18.detect(image)
boxesDB50, _ = textDetectorDB50.detect(image)
# Process all detected boxes
for box in boxesEAST + boxesDB18 + boxesDB50:
    cv2.fillPoly(inpaint_mask, [np.array(box, np.int32)], 255)  # Full mask
    cv2.polylines(annotated_image, [np.array(box, np.int32)], isClosed=True, color=(0, 255, 0), thickness=1)  # Annotate all models (Green)
# Process DB50 detected boxes
for box in boxesDB50:
    cv2.fillPoly(inpaint_mask_db50, [np.array(box, np.int32)], 255)  # DB50 mask
    cv2.polylines(annotated_db50_image, [np.array(box, np.int32)], isClosed=True, color=(0, 0, 255), thickness=1)  # Annotate DB50 (Red)
# Process DB18 detected boxes
for box in boxesDB18:
    cv2.fillPoly(inpaint_mask_db18, [np.array(box, np.int32)], 255)  # DB18 mask
    cv2.polylines(annotated_db18_image, [np.array(box, np.int32)], isClosed=True, color=(255, 0, 0), thickness=1)  # Annotate DB18 (Blue)
# Process EAST detected boxes
for box in boxesEAST:
    cv2.fillPoly(inpaint_mask_east, [np.array(box, np.int32)], 255)  # EAST mask
    cv2.polylines(annotated_east_image, [np.array(box, np.int32)], isClosed=True, color=(0, 255, 255), thickness=1)  # Annotate EAST (Cyan)
# Perform inpainting
inpainted_image = cv2.inpaint(orig_image, inpaint_mask, inpaintRadius=5, flags=cv2.INPAINT_NS)  # All models
inpainted_db50_image = cv2.inpaint(orig_db50_image, inpaint_mask_db50, inpaintRadius=5, flags=cv2.INPAINT_NS)  # DB50 only
inpainted_db18_image = cv2.inpaint(orig_db18_image, inpaint_mask_db18, inpaintRadius=5, flags=cv2.INPAINT_NS)  # DB18 only
inpainted_east_image = cv2.inpaint(orig_east_image, inpaint_mask_east, inpaintRadius=5, flags=cv2.INPAINT_NS)  # EAST only
# Show results
cv2.imshow('Original', image)
cv2.imshow('Annotated (All Models)', annotated_image)
cv2.imshow('Inpainted (All Models)', inpainted_image)
cv2.imshow('Annotated (DB50 Only)', annotated_db50_image)
cv2.imshow('Inpainted (DB50 Only)', inpainted_db50_image)
cv2.imshow('Annotated (DB18 Only)', annotated_db18_image)
cv2.imshow('Inpainted (DB18 Only)', inpainted_db18_image)
cv2.imshow('Annotated (EAST Only)', annotated_east_image)
cv2.imshow('Inpainted (EAST Only)', inpainted_east_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

    该实现使用 OpenCV 的多个深度学习模型有效地检测和删除文本。检测到的文本区域使用修复技术进行填充。

九、CV 和 MediaPipe ~实时手指计数系统

 项目的作用

    该项目可以检测手部并实时计算举起手指的数量。它适用于:

    ✋ 单手或双手检测

    📊 显示单个手指数和总手指数

    🎥 支持外部摄像头(如您的手机)以获得更好的视频质量

    💾 使用 H.264 编解码器以格式保存带注释的视频.mp4

⚙️ 技术堆栈细分

    为了实现这一点,我使用了:

  • Python — 主要编程语言
  • MediaPipe — 用于检测手部特征点(每手 21 个点)
  • OpenCV — 用于视频流和绘图注释
  • H.264 编解码器 — 用于保存高质量录音

    MediaPipe + OpenCV 的组合提供了一种轻量级且速度惊人的解决方案——即使在常规硬件上也是如此。

🧠 它是如何工作的 

    🔍 手部特征点检测 (MediaPipe)

    MediaPipe 每只手检测 21 个地标。这些标志代表指尖、关节和手腕,是识别手指位置的关键。

    ✋ Finger Counting 逻辑

    拇指检测:

  • 对于左手,如果满足以下条件,拇指会张开:

        tip_x > base_x

  • 对于右手,如果满足以下条件,拇指会打开:

        tip_x < base_x

    其他手指:

  • 如果手指的尖端高于其第二个关节,则将其计为“张开”。

    此逻辑可以轻松确定每只手上举起的手指数,然后计算总数。

    🎥 视频输出

    系统使用 H.264 编解码器保存处理后的视频源,从而确保:

    ✅ 流畅稳定的帧速率

    ✅ 高压缩率,质量损失

    ✅最小 兼容几乎所有设备

    最终输出文件保存为:

finger_counter_output.mp4

    📸 实时演示

图片

    它会立即响应,跟踪双手牌,并将计数直接叠加到视频上。它快速、准确,而且实时测试令人满意。

完整代码:

1. 单手检测:

import cv2
import mediapipe as mp
import numpy as np
# Initialize the MediaPipe Hand module
mp_hands = mp.solutions.hands
mp_draw = mp.solutions.drawing_utils
# OpenCV video capture
cap = cv2.VideoCapture(0)
# Hand tracking configuration
with mp_hands.Hands(min_detection_cnotallow=0.7, min_tracking_cnotallow=0.7) as hands:
    
    prev_count = 0  # Store the previous finger count to reduce flickering
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        # Flip the frame horizontally
        frame = cv2.flip(frame, 1)
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        # Detect hands
        result = hands.process(rgb_frame)
        if result.multi_hand_landmarks:
            for hand_landmarks in result.multi_hand_landmarks:
                # Draw hand landmarks
                mp_draw.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
                # Extract landmarks
                landmarks = []
                for lm in hand_landmarks.landmark:
                    h, w, _ = frame.shape
                    cx, cy = int(lm.x * w), int(lm.y * h)
                    landmarks.append((cx, cy))
                # Finger tips and base points
                tip_ids = [4, 8, 12, 16, 20]
                fingers = []
                # Thumb detection based on X and Y position
                if landmarks[tip_ids[0]][0] < landmarks[tip_ids[0] - 1][0]:  # Thumb is open
                    fingers.append(1)
                else:
                    fingers.append(0)
                # Detecting 4 fingers
                for id in range(1, 5):
                    if landmarks[tip_ids[id]][1] < landmarks[tip_ids[id] - 2][1]:
                        fingers.append(1)  # Open
                    else:
                        fingers.append(0)  # Closed
                # Count fingers
                total_fingers = fingers.count(1)
                # Smooth the count to reduce flickering
                if total_fingers != prev_count:
                    prev_count = total_fingers
                # Display the finger count
                cv2.putText(frame, f'Fingers: {prev_count}', (10, 50),
                            cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        # Show the frame
        cv2.imshow("Finger Counter", frame)
        # Press 'q' to exit
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
# Release and close windows
cap.release()
cv2.destroyAllWindows()

2. 双手检测:

import cv2
import mediapipe as mp
# Initialize the MediaPipe Hand module
mp_hands = mp.solutions.hands
mp_draw = mp.solutions.drawing_utils
# OpenCV video capture
cap = cv2.VideoCapture(0)
# Hand tracking configuration
with mp_hands.Hands(min_detection_cnotallow=0.7, min_tracking_cnotallow=0.7, max_num_hands=2) as hands:
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        # Flip the frame horizontally
        frame = cv2.flip(frame, 1)
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        # Detect hands
        result = hands.process(rgb_frame)
        # Initialize finger counts
        left_count = 0
        right_count = 0
        if result.multi_hand_landmarks and result.multi_handedness:
            
            for idx, (hand_landmarks, handedness) in enumerate(zip(result.multi_hand_landmarks, result.multi_handedness)):
                # Draw landmarks
                mp_draw.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
                # Extract landmarks
                landmarks = []
                for lm in hand_landmarks.landmark:
                    h, w, _ = frame.shape
                    cx, cy = int(lm.x * w), int(lm.y * h)
                    landmarks.append((cx, cy))
                # Finger tips and base points
                tip_ids = [4, 8, 12, 16, 20]
                fingers = []
                # Improved thumb detection for mirrored frame
                label = handedness.classification[0].label
                if label == "Left":
                    # Left hand - reverse thumb logic
                    if landmarks[tip_ids[0]][0] > landmarks[tip_ids[0] - 1][0]:
                        fingers.append(1)  # Open
                    else:
                        fingers.append(0)  # Closed
                else:
                    # Right hand - normal thumb logic
                    if landmarks[tip_ids[0]][0] < landmarks[tip_ids[0] - 1][0]:
                        fingers.append(1)  # Open
                    else:
                        fingers.append(0)  # Closed
                # Detecting 4 fingers
                for id in range(1, 5):
                    if landmarks[tip_ids[id]][1] < landmarks[tip_ids[id] - 2][1]:
                        fingers.append(1)  # Open
                    else:
                        fingers.append(0)  # Closed
                # Count fingers
                finger_count = fingers.count(1)
                # Assign count based on handedness
                if label == "Left":
                    left_count = finger_count
                else:
                    right_count = finger_count
        # Total fingers
        total_fingers = left_count + right_count
        # Display the finger count for both hands
        cv2.putText(frame, f'Left Hand: {left_count}', (10, 50),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)  # Blue
        cv2.putText(frame, f'Right Hand: {right_count}', (10, 100),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)  # Blue
        # Display the total finger count in red
        cv2.putText(frame, f'Total Fingers: {total_fingers}', (10, 150),
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)  # Red
        # Show the frame
        cv2.imshow("Both Hands Finger Counter", frame)
        # Press 'q' to exit
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
# Release and close windows
cap.release()
cv2.destroyAllWindows()

十、实时弯道检测

本文主要介绍如何使用 Python 和 OpenCV实现一个实时曲线道路检测系统。

背景介绍

    在任何驾驶场景中,车道线都是指示交通流量和车辆应行驶位置的重要组成部分。这也是开发自动驾驶汽车的一个很好的起点!在我之前的车道检测项目的基础上,我实现了一个曲线车道检测系统,该系统工作得更好,并且对具有挑战性的环境更加稳健。车道检测系统是使用 OpenCV 库用 Python 编写的。    

下面是实现步骤:

  • 畸变校正
  • 透视变换
  • Sobel滤波
  • 直方图峰值检测
  • 滑动窗口搜索
  • 曲线拟合
  • 覆盖检测车道
  • 应用于视频

畸变矫正

    相机镜头扭曲入射光以将其聚焦在相机传感器上。尽管这对于我们捕捉环境图像非常有用,但它们最终往往会稍微不准确地扭曲光线。这可能导致计算机视觉应用中的测量不准确。然而,我们可以很容易地纠正这种失真。

    我们可以使用棋盘格来标定相机然后做畸变校正:

    测试视频中使用的相机用于拍摄棋盘格的 20 张照片,用于生成畸变模型。我们首先将图像转换为灰度,然后应用cv2.findChessboardCorners()函数。我们已经知道这个棋盘是一个只有直线的二维对象,所以我们可以对检测到的角应用一些变换来正确对齐它们。用 cv2.CalibrateCamera() 来获取畸变系数和相机矩阵。相机已校准!

    然后,您可以使用它cv2.undistort()来矫正其余的输入数据。您可以在下面看到棋盘的原始图像和校正后的图像之间的差异:

实现代码:

def undistort_img():
    # Prepare object points 0,0,0 ... 8,5,0
    obj_pts = np.zeros((6*9,3), np.float32)
    obj_pts[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1,2)
    # Stores all object points & img points from all images
    objpoints = []
    imgpoints = []
    # Get directory for all calibration images
    images = glob.glob('camera_cal/*.jpg')
    for indx, fname in enumerate(images):
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        ret, corners = cv2.findChessboardCorners(gray, (9,6), None)
        if ret == True:
            objpoints.append(obj_pts)
            imgpoints.append(corners)
    # Test undistortion on img
    img_size = (img.shape[1], img.shape[0])
    # Calibrate camera
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None,None)
    dst = cv2.undistort(img, mtx, dist, None, mtx)
    # Save camera calibration for later use
    dist_pickle = {}
    dist_pickle['mtx'] = mtx
    dist_pickle['dist'] = dist
    pickle.dump( dist_pickle, open('camera_cal/cal_pickle.p', 'wb') )
def undistort(img, cal_dir='camera_cal/cal_pickle.p'):
    #cv2.imwrite('camera_cal/test_cal.jpg', dst)
    with open(cal_dir, mode='rb') as f:
        file = pickle.load(f)    mtx = file['mtx']
    dist = file['dist']
    dst = cv2.undistort(img, mtx, dist, None, mtx)
    return dst
undistort_img()
img = cv2.imread('camera_cal/calibration1.jpg')
dst = undistort(img) # Undistorted image

    这是应用于道路图像的失真校正。您可能无法注意到细微的差异,但它会对图像处理产生巨大影响。

透视变换

    在相机空间中检测弯曲车道并不是很容易。如果我们想鸟瞰车道怎么办?这可以通过对图像应用透视变换来完成。这是它的样子:

    注意到什么了吗?通过假设车道位于平坦的 2D 表面上,我们可以拟合一个多项式,该多项式可以准确地表示车道空间中的车道!这不是很酷吗?

    您可以使用cv2.getPerspectiveTransform()函数将这些变换应用于任何图像,以获取变换矩阵,并将cv2.warpPerspective()其应用于图像。下面是代码:

def perspective_warp(img,
                     dst_size=(1280,720),
                     src=np.float32([(0.43,0.65),(0.58,0.65),(0.1,1),(1,1)]),
                     dst=np.float32([(0,0), (1, 0), (0,1), (1,1)])):
    img_size = np.float32([(img.shape[1],img.shape[0])])
    src = src* img_size
    # For destination points, I'm arbitrarily choosing some points to be
    # a nice fit for displaying our warped result
    # again, not exact, but close enough for our purposes
    dst = dst * np.float32(dst_size)
    # Given src and dst points, calculate the perspective transform matrix
    M = cv2.getPerspectiveTransform(src, dst)
    # Warp the image using OpenCV warpPerspective()
    warped = cv2.warpPerspective(img, M, dst_size)
    return warped

Sobel滤波

   在之前的版本中,我使用颜色过滤掉了车道线。然而,这并不总是最好的选择。如果道路使用浅色混凝土代替沥青,道路很容易通过彩色滤光片,管道会将其感知为白色车道线,此方法不够稳健。

    相反,我们可以使用类似于边缘检测器的方法,这次过滤掉道路。车道线通常与道路具有高对比度,因此我们可以利用这一点。之前版本 1 中使用的Canny边缘检测器利用Sobel 算子来获取图像函数的梯度。OpenCV 文档对它的工作原理有很好的解释。我们将使用它来检测高对比度区域以过滤车道标记并忽略道路。

    我们仍将再次使用 HLS 色彩空间,这一次是为了检测饱和度和亮度的变化。sobel 算子应用于这两个通道,我们提取相对于 x 轴的梯度,并将通过梯度阈值的像素添加到表示图像中像素的二进制矩阵中。这是它在相机空间和车道空间中的样子:

    请注意,远离相机的图像部分不能很好地保持其质量。由于相机的分辨率限制,来自更远物体的数据非常模糊和嘈杂。我们不需要专注于整个图像,所以我们可以只使用它的一部分。这是我们将使用的图像的样子(ROI):

直方图峰值检测

   我们:应用一种称为滑动窗口算法的特殊算法来检测我们的车道线。但是,在我们应用它之前,我们需要为算法确定一个好的起点。如果它从存在车道像素的位置开始,它会很好地工作,但是我们如何首先检测这些车道像素的位置呢?其实很简单!

    我们将获得图像相对于 X 轴的直方图。下面直方图的每个部分都显示了图像每列中有多少个白色像素。然后我们取图像每一侧的最高峰,每条车道线一个。这是直方图的样子,在二值图像旁边:

滑动窗口搜索

   滑动窗口算法将用于区分左右车道边界,以便我们可以拟合代表车道边界的两条不同曲线。

    算法本身非常简单。从初始位置开始,第一个窗口测量有多少像素位于窗口内。如果像素数量达到某个阈值,它将下一个窗口移动到检测到的像素的平均横向位置。如果没有检测到足够的像素,则下一个窗口从相同的横向位置开始。这一直持续到窗口到达图像的另一边缘。

    落在窗口内的像素被赋予一个标记。在下图中,蓝色标记的像素代表右侧车道,红色标记的像素代表左侧:

曲线拟合

   项目的其余部分非常简单。我们分别使用 对红色和蓝色像素应用多项式回归np.polyfit(),然后检测器就完成了!

    这是曲线的样子:

绘制检测车道

   这是检测系统的最后一部分,用户界面!我们只需创建一个覆盖层来填充检测到的车道部分,然后我们最终可以将其应用于视频。一旦应用于视频检测,您应该会看到以下输出:

图片

结论

   就是这样,一个基本的弯曲车道检测器!它比以前的版本好得多,它甚至可以处理弯曲的车道!但是,它仍然会在一定程度上受到阴影和道路纹理剧烈变化的影响。在我的下一个车道检测项目中,我们将使用一些机器学习技术来开发一个非常强大的车道和车辆检测系统,谢谢!

完整代码:​https://github.com/kemfic/Curved-Lane-Lines/blob/master/P4.ipynb​

参考链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值