基于轨迹信息的图像近距离可行驶区域方案验证

news/2024/7/8 2:46:44 标签: 计算机视觉, opencv, python

一 图像可行驶区域方案

1.1 标定场景

标定场地

1.2 标定步骤

  1. 设计一定间距标定场,在标定场固定位置设置摄像头标定标识点。
  2. 主车开到标定场固定位置
  3. 录制主车在该位置各个摄像头数据,通过摄像头捕获图像获取图像上关键点坐标pts-2d
  4. 基于标定场设计,计算图像关键点对应车体坐标系中的3d坐标pts-3d
  5. 通过cv2.findHomography(obj_points, img_points, cv2.RANSAC, 5.0) 获取相机坐标系到地面的单应性变换矩阵H

1.3 实车使用

实时获取车辆行进过程中的固定纵向距离的轨迹点信息,使用单应性变换矩阵H反向计算轨迹信息在图像中的投影位置,从而获取到图像中检测目标的距离区间。

二 初步验证结果

'''
Author: XIEXINYAN “1532642675@qq.com”
Date: 2024-07-01 04:52:07
LastEditors: XIEXINYAN “1532642675@qq.com”
LastEditTime: 2024-07-03 05:40:06
FilePath: /202407/hom_matrix.py
Description: 

Copyright (c) 2024 by 1532642675@qq.com, All Rights Reserved. 
'''
import cv2  
import numpy as np  
import os
import argparse

class Counter:
    cnt = 0
    def __init__(self):
        Counter.cnt +=1
    @classmethod
    def get_counter(cls):
        return cls.cnt 
class Calibrate:
    def __init__(self, pattern_size, real_square_size, offset_x, offset_y):
        self.pattern_size = pattern_size
        self.real_square_size = real_square_size
        self.offset_x = offset_x
        self.offset_y = offset_y
    # 1. 检测棋格板角点  
    def find_chessboard_corners(self, image, color=(0, 255, 0), vis=False,  save=False, calib=False): 
        self.image = image.copy()
        image_painted = image.copy()
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  
        ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)  
        if ret:  
            criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)  
            #按照pattern_size[0]的个数排序,绘制的第一组数据个数=pattern_size[0]的个数
            self.corner = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
            
            if self.corner is not None:
                '''
                0  3  6          2  5  8           6  3  0            8  5  2
              1  4  7          1  4  7            7  4  1            7  4  1    
            2  5  8          0  3  6            8  5  2            6  3  0  
                '''
                if vis:
                    #判断corners排序顺序
                    index_list = []
                    if (self.corner[0][0][1]<self.corner[1][0][1] 
                        and self.corner[0][0][0]<self.corner[np.prod(self.pattern_size)-1][0][0]):
                        print("mode A")
                    elif (self.corner[0][0][1]>self.corner[1][0][1] 
                        and self.corner[0][0][0]<self.corner[np.prod(self.pattern_size)-1][0][0]):
                        print("mode B")
                        for j in range(pattern_size[1]):
                            for i in range(pattern_size[0]):
                                index_list.append(pattern_size[0]-1-i+j*pattern_size[0])
                    elif (self.corner[0][0][1]<self.corner[1][0][1] 
                        and self.corner[0][0][0]>self.corner[np.prod(self.pattern_size)-1][0][0]):
                        print("mode C")
                        index_list = []
                        for j in range(pattern_size[1]):
                            for i in range(pattern_size[0]):
                                index_list.append(i+(pattern_size[1]-1-j)*pattern_size[0])
                        print(index_list)
                    elif (self.corner[0][0][1]>self.corner[1][0][1] 
                        and self.corner[0][0][0]>self.corner[np.prod(self.pattern_size)-1][0][0]):
                        print("mode D")
                        index_list = []
                        for j in range(pattern_size[1]):
                            for i in range(pattern_size[0]):
                                index_list.append(i+(pattern_size[1]-1-j)*pattern_size[0])
                        print(index_list)
                    else:
                        print("horizonal mode")
                    self.corner = self.corner[index_list]
                    cv2.circle(image_painted, (int(self.corner[0][0][0]), int(self.corner[0][0][1])), 25, (0, 255, 255), -1)  
                    cv2.circle(image_painted, (int(self.corner[1][0][0]), int(self.corner[1][0][1])), 25, (0, 0, 255), -1) 
                    for corner in self.corner:
                        cv2.circle(image_painted, (int(corner[0][0]), int(corner[0][1])), 5, (0, 0, 255), -1)  
                    cv2.drawChessboardCorners(image_painted, pattern_size, self.corner, True)  
                    cv2.imshow("chess",image_painted)
                if save:
                    instance=Counter()
                    cv2.imwrite(str(instance.get_counter())+".jpg", image)
                if calib:
                    self.find_hom_matrix(vis=True, save=True)
                return True
        return False
    
    def world_chess_board_loc(self):
        # 初始化obj_points数组,注意使用齐次坐标(即每个点都是[x, y, 1])  
        obj_points = np.zeros((np.prod(self.pattern_size), 3), dtype=np.float32)  
        # x_start是每行开始的x坐标  # y_start是每行开始的y坐标 
        # # 填充obj_points数组  
        '''
        从左上角开始
        0  3  6  9  
        1  4  7  10
        2  5  8  11
        '''
        # obj_points =np.array([
        #     [  0.,   0.,   1.],[  0.,  60.,   1.],[  0., 120.,   1.],
        #     [ 60.,   0.,   1.],[ 60.,  60.,   1.],[ 60., 120.,   1.],
        #     [120.,   0.,   1.],[120.,  60.,   1.],[120., 120.,   1.],
        #     [180.,  0.,   1.],[180.,  60.,   1.],[180., 120.,   1.]], dtype=np.float32)
        index = 0 
        for i in range(pattern_size[1]):  
            for j in range(pattern_size[0]):  
                # 计算x和y坐标  
                x = i * self.real_square_size 
                y = j * self.real_square_size  
                # 将点添加到obj_points数组中,注意使用齐次坐标形式  
                obj_points[index, :] = [x, y, 1.0]  
                index += 1  
        return obj_points
    '''
    将世界坐标系下点转化为wraped图像上点
    '''
    def world_transation(self):
        obj_points = self.world_chess_board_loc()
        obj_points_t = obj_points.copy()
        obj_points_t[:,0] += self.offset_x
        obj_points_t[:,1] += self.offset_y
        obj_points_t[:,:2] *= 1000
        return obj_points_t
    
    def world_to_image(self, img, vis=False, save=False):
        # obj_points 是世界坐标系下的点,需要是齐次坐标形式 

        obj_points_t = self.world_transation()
        # 使用np.dot进行矩阵乘法,并计算归一化的图像坐标  
        img_points_homogeneous = np.dot(self.H, obj_points_t.T).T  
        img_points = img_points_homogeneous[:, :2] / img_points_homogeneous[:, 2:].reshape(-1, 1) 
        for pt_2d in img_points:
            cv2.circle(img, (int(pt_2d[0]), int(pt_2d[1])), 5, (0, 0, 255), -1) 
        if vis:
            cv2.imshow("eval image", img)
        if save:
            counter = Counter()
            cv2.imwrite("eval_"+str(counter.get_counter())+".jpg",img)
        return

    
    def find_hom_matrix(self, vis=False, save=False):
        # 世界坐标值 【横,纵,高】
        obj_points = self.world_chess_board_loc() 
        # 偏移到某个坐标系
        obj_points[:,0] += self.offset_x 
        obj_points[:,1] += self.offset_y
        obj_points[:,:2] *= 1000
        # print(obj_points)
        # 3. 计算单应性矩阵  
        img_points = self.corner.reshape(-1, 1, 2).astype(np.float32)  
        self.H, _ = cv2.findHomography(obj_points, img_points, cv2.RANSAC, 5.0)  
        warped_image = cv2.warpPerspective(self.image, self.H,  (self.image.shape[1], self.image.shape[0]))
        if vis:
            cv2.imshow("wrapped image",warped_image)
            cv2.waitKey()
        if save:
            cv2.imwrite("wrapped_"+str(Counter.cnt)+".jpg", warped_image)
        return 
        

def find_qrcode_corners(image, vis=False):
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  
        # 二值化  
        _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)  
        # # 查找轮廓  
        # contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        # 查找轮廓(OpenCV 4.x) 
        contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  
        #打印轮廓信息  
        print("总轮廓数:", len(contours))
        # 筛选和排序轮廓(这里我们假设二维码是最大的轮廓)  
        contours = sorted(contours, key=cv2.contourArea, reverse=True)  
        qrcode_contour = contours[0]  
        # 多边形近似  
        epsilon = 0.02 * cv2.arcLength(qrcode_contour, True)  
        approx = cv2.approxPolyDP(qrcode_contour, epsilon, True)  
        image_out = image.copy()
        # 提取角点坐标  
        corners = approx.reshape((-1, 2))  
        # 在原图上绘制角点  
        if len(corners) == 4:
            for corner in corners:  
                cv2.circle(image_out, (int(corner[0]), int(corner[1])), 25, (0, 0, 255), -1)  
                # 显示图像  
                cv2.imshow('QRCode Corners', image_out)  
            
        cv2.drawContours(image_out, qrcode_contour, -1, (0, 255, 0), 3)  
        cv2.imshow('Corners', image_out)  
        cv2.waitKey(1)   
    



def argParser():
    parser = argparse.ArgumentParser()
    parser.add_argument('--rows', type=int, default=3,help='chess board raw num')
    parser.add_argument('--cols', type=int, default=4,help='chess board col num')
    parser.add_argument('--online',type=bool, default=False, help='online camera calib')
    opt = parser.parse_args()
    return opt

if __name__ == '__main__':
    opt = argParser()
    pattern_size = (opt.rows, opt.cols)
    calib = Calibrate(pattern_size, 0.06, 0.3, 0.7)

    cap = cv2.VideoCapture(0)
    if not cap.isOpened():
        exit()
    print(" now start calib %d\n",opt.online)
    if opt.online:
        while True:
            ret, frame = cap.read()
            flag = calib.find_chessboard_corners(frame, vis=True, save=True, calib=True) 
            cv2.waitKey(1)  
            if flag:
                break
    else:
        path = "img"
        image_list = os.listdir(path)
        for img in image_list:
            img_path = os.path.join(path,img)
            if os.path.isfile(img_path):
                frame = cv2.imread(img_path)
            else:
                continue
            print("image path is: ",img_path)
            flag = calib.find_chessboard_corners(frame, vis=True, save=False, calib=True)
            cv2.waitKey(1)
            if flag:
                break
    print(calib.H)
    print(" now start eval \n")
    if opt.online:
        while True:
            ret, frame = cap.read()
            # 如果正确读取帧,ret为True
            if not ret:
                print("无法接收帧,请退出")
                break       
            calib.world_to_image(frame,True, False)
            # 显示实时画面
            cv2.imshow('raw', frame)
            # 按 'q' 键退出循环
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
        # 释放摄像头资源并关闭所有窗口
        cap.release()
    else:
        path = "img"
        image_list = os.listdir(path)
        for img in image_list:
            img_path = os.path.join(path,img)
            if os.path.isfile(img_path):
                frame = cv2.imread(img_path)
            else:
                continue
            calib.world_to_image(frame, True, True)
            # 显示实时画面
            cv2.imshow('raw', frame)
            # 按 'q' 键退出循环
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

cv2.destroyAllWindows()

三 存在问题

  1. 本方案在设计标定场过程中需要精确计算每个相机的FOV,与地面的交点,设计地面标志物,使得每个相机可以准确有效的提取地面标志物
  2. 会受到道路坡度和车辆pitch角影响,需要模拟分析pitch角对距离的影响度

http://www.niftyadmin.cn/n/5536191.html

相关文章

服务器工具集合推荐

推荐一个朋友开源的服务器运维整合工具,目前的功能包括: ddns&#xff0c;rdp、ssh终端、ftp、http代理&#xff0c;支持在线文件编辑&#xff0c;文件管理&#xff0c;docker&#xff0c;进程&#xff0c;系统监控、wol唤醒&#xff0c;电脑远程开机&#xff0c;点对点&#…

Winform和WPF中关于是否处于设计模式的判断方式

在界面开发中&#xff0c;若在构造函数中增加了加载数据的代码&#xff0c;在设计界面时会出现界面打不开或者出现报错的情况。此时&#xff0c;可以检查一下是否为该情况。 Winform中的DesignMode判断 在WinForm开发中,一般会在窗体或者UserControl中判断当前是否为设计状态,…

JVM垃圾回收性能调优实战指南

JVM垃圾回收性能调优实战指南 一、引言 在Java应用程序中&#xff0c;垃圾回收&#xff08;Garbage Collection, GC&#xff09;是自动管理内存的重要机制。然而&#xff0c;不恰当的垃圾回收配置可能导致性能瓶颈&#xff0c;如频繁的GC暂停、内存碎片过多等。因此&#xff…

Linux 编译生成静态库以及动态库全流程

在Linux系统中&#xff0c;通常不直接使用.lib作为库文件的扩展名&#xff0c;因为.lib是Windows平台下常用的静态库文件扩展名。然而&#xff0c;Linux下对应的静态库文件扩展名是.a&#xff08;archive&#xff09;&#xff0c;而动态库文件扩展名是.so&#xff08;shared ob…

哈喽GPT-4o,对GPT-4o 论文速写的思考与探索

作为一款强大的语言模型&#xff0c;ChatGPT 在论文写作上具备显著优势。它能够辅助学者或研究人员自动创建论文框架、摘要、文献综述及论文段落&#xff08;如引言、方法、结果、结论等&#xff09;。此外&#xff0c;ChatGPT 还能优化论文结构、润色、降低内容重复率&#xf…

【redis】jedis概述_简单使用(Java中使用redis)

1、定义与背景 Jedis是Redis官方推崇的Java客户端实现之一&#xff0c;允许Java程序通过其提供的Java API与Redis服务器进行交互。Redis是一款高性能的NOSQL系列的非关系型数据库&#xff0c;使用C语言开发&#xff0c;支持多种键值数据类型&#xff0c;包括字符串、哈希、列表…

如何使用 3D 建模库在 C# 中将 3DS 转换为 USDZ?

USDZ/USD是一种 3D 文件格式&#xff0c;被广泛用于跨平台共享 3D 资产。另一方面&#xff0c;3DS是另一种以块形式存储数据的 3D 文件格式。在某些情况下&#xff0c;您需要将3DS 文件转换为 USDZ/USD文件格式。因此&#xff0c;本篇博文介绍了一个功能丰富的3D 建模库&#x…

什么是独立服务器?

独立服务器是指一个单独的物理服务器&#xff0c;整体的硬件设施都是独立存在的&#xff0c;有着强大的性能&#xff0c;只需要运行用户个人的数据信息&#xff0c;并且可以享受到独立服务器的硬件与软件&#xff0c;当网站有着大量的用户进行访问或者是需要运行大型的软件时&a…