大创项目推荐 题目:基于机器视觉的图像矫正 (以车牌识别为例) - 图像畸变校正

news2024/12/23 17:39:08

文章目录

  • 0 简介
  • 1 思路简介
    • 1.1 车牌定位
    • 1.2 畸变校正
  • 2 代码实现
    • 2.1 车牌定位
      • 2.1.1 通过颜色特征选定可疑区域
      • 2.1.2 寻找车牌外围轮廓
      • 2.1.3 车牌区域定位
    • 2.2 畸变校正
      • 2.2.1 畸变后车牌顶点定位
      • 2.2.2 校正
  • 7 最后

0 简介

🔥 优质竞赛项目系列,今天要分享的是

基于机器视觉的图像矫正 (以车牌识别为例)

该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

1 思路简介

目前车牌识别系统在各小区门口随处可见,识别效果貌似都还可以。查阅资料后,发现整个过程又可以细化为车牌定位、畸变校正、车牌分割和内容识别四部分。本篇随笔主要介绍车牌定位及畸变校正两部分,在python环境下通过opencv实现。

1.1 车牌定位

目前主流的车牌定位方法从大的方面来说可以分为两类:一种是基于车牌的背景颜色特征;另一种基于车牌的轮廓形状特征。基于颜色特征的又可分为两类:一种在RGB空间识别,另一种在HSV空间识别。经测试后发现,单独使用任何一种方法,效果均不太理想。目前比较普遍的做法是几种定位方法同时使用,或用一种识别,另一种验证。本文主要通过颜色特征对车牌进行定位,以HSV空间的H分量为主,以RGB空间的R分量和B分量为辅,后续再用车牌的长宽比例排除干扰。

1.2 畸变校正

在车牌的图像采集过程中,相机镜头通常都不是垂直于车牌的,所以待识别图像中车牌或多或少都会有一定程度的畸变,这给后续的车牌内容识别带来了一定的困难。因此需要对车牌进行畸变校正,消除畸变带来的不利影响。

2 代码实现

2.1 车牌定位

2.1.1 通过颜色特征选定可疑区域

取了不同光照环境下车牌的图像,截取其背景颜色,利用opencv进行通道分离和颜色空间转换,经试验后,总结出车牌背景色的以下特征:

  • (1)在HSV空间下,H分量的值通常都在115附近徘徊,S分量和V分量因光照不同而差异较大(opencv中H分量的取值范围是0到179,而不是图像学中的0到360;S分量和V分量的取值范围是到255);

  • (2)在RGB空间下,R分量通常较小,一般在30以下,B分量通常较大,一般在80以上,G分量波动较大;

  • (3)在HSV空间下对图像进行补光和加饱和度处理,即将图像的S分量和V分量均置为255,再进行色彩空间转换,由HSV空间转换为RGB空间,发现R分量全部变为0,B分量全部变为255(此操作会引入较大的干扰,后续没有使用)。

根据以上特征可初步筛选出可疑的车牌区域。随后对灰度图进行操作,将可疑位置的像素值置为255,其他位置的像素值置为0,即根据特征对图像进行了二值化。二值化图像中,可疑区域用白色表示,其他区域均为黑色。随后可通过膨胀腐蚀等操作对图像进一步处理。

for i in range(img_h):
    for j in range(img_w):
        # 普通蓝色车牌,同时排除透明反光物质的干扰
        if ((img_HSV[:, :, 0][i, j]-115)**2 < 15**2) and (img_B[i, j] > 70) and (img_R[i, j] < 40):
            img_gray[i, j] = 255
        else:
            img_gray[i, j] = 0

在这里插入图片描述

2.1.2 寻找车牌外围轮廓

选定可疑区域并将图像二值化后,一般情况下,图像中就只有车牌位置的像素颜色为白,但在一些特殊情况下还会存在一些噪声。如上图所示,由于图像右上角存在蓝色支架,与车牌颜色特征相符,因此也被当做车牌识别了出来,由此引入了噪声。

经过观察可以发现,车牌区域与噪声之间存在较大的差异,且车牌区域特征比较明显:

  • (1)根据我国常规车牌的形状可知,车牌的形状为扁平矩形,长宽比约为3:1;

  • (2)车牌区域面积远大于噪声区域,一般为图像中最大的白色区域。

在这里插入图片描述

可以通过cv2.findContours()函数寻找二值化后图像中白色区域的轮廓。

注意:在opencv2和opencv4中,cv2.findContours()的返回值有两个,而在opencv3中,返回值有3个。视opencv版本不同,代码的写法也会存在一定的差异。

# 检测所有外轮廓,只留矩形的四个顶点
# opencv4.0, opencv2.x
contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# opencv3.x
_, contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

这里,因为二值化图像中共有三块白色区域(车牌及两处噪声),因此返回值contours为长度为3的list。list内装有3个array,每个array内各存放着一块白色区域的轮廓信息。每个array的shape均为(n,
1, 2),即每个array存放着对应白色区域轮廓上n个点的坐标。

目前得到了3个array,即3组轮廓信息,但我们并不清楚其中哪个是车牌区域对应的那一组轮廓信息。此时可以根据车牌的上述特征筛选出车牌区域的轮廓。

#形状及大小筛选校验
det_x_max = 0
det_y_max = 0
num = 0
for i in range(len(contours)):
    x_min = np.min(contours[i][ :, :, 0])
    x_max = np.max(contours[i][ :, :, 0])
    y_min = np.min(contours[i][ :, :, 1])
    y_max = np.max(contours[i][ :, :, 1])
    det_x = x_max - x_min
    det_y = y_max - y_min
    if (det_x / det_y > 1.8) and (det_x > det_x_max ) and (det_y > det_y_max ):
        det_y_max = det_y
        det_x_max = det_x
        num = i
# 获取最可疑区域轮廓点集
points = np.array(contours[num][:, 0])

最终得到的points的shape为(n, 2),即存放了n个点的坐标,这n个点均分布在车牌的边缘上

2.1.3 车牌区域定位

获取车牌轮廓上的点集后,可用cv2.minAreaRect()获取点集的最小外接矩形。返回值rect内包含该矩形的中心点坐标、高度宽度及倾斜角度等信息,使用cv2.boxPoints()可获取该矩形的四个顶点坐标。

# 获取最小外接矩阵,中心点坐标,宽高,旋转角度
rect = cv2.minAreaRect(points)
# 获取矩形四个顶点,浮点型
box = cv2.boxPoints(rect)
# 取整
box = np.int0(box)

但我们并不清楚这四个坐标点各对应着矩形的哪一个顶点,因此无法充分地利用这些坐标信息。

可以从坐标值的大小特征入手,将四个坐标与矩形的四个顶点匹配起来:在opencv的坐标体系下,纵坐标最小的是top_point,纵坐标最大的是bottom_point,
横坐标最小的是left_point,横坐标最大的是right_point。

# 获取四个顶点坐标
left_point_x = np.min(box[:, 0])
right_point_x = np.max(box[:, 0])
top_point_y = np.min(box[:, 1])
bottom_point_y = np.max(box[:, 1])

left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0]
right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0]
top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0]
bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0]
# 上下左右四个点坐标
vertices = np.array([[top_point_x, top_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y], [right_point_x, right_point_y]])

在这里插入图片描述
在这里插入图片描述

2.2 畸变校正

2.2.1 畸变后车牌顶点定位

要想实现车牌的畸变矫正,必须找到畸变前后对应点的位置关系。

可以看出,本是矩形的车牌畸变后变成了平行四边形,因此车牌轮廓和得出来的矩形轮廓并不契合。但有了矩形的四个顶点坐标后,可以通过简单的几何相似关系求出平行四边形车牌的四个顶点坐标。

在本例中,平行四边形四个顶点与矩形四个顶点之间有如下关系:矩形顶点Top_Point、Bottom_Point与平行四边形顶点new_top_point、new_bottom_point重合,矩形顶点Top_Point的横坐标与平行四边形顶点new_right_point的横坐标相同,矩形顶点Bottom_Point的横坐标与平行四边形顶点new_left_point的横坐标相同。

在这里插入图片描述

但事实上,由于拍摄的角度不同,可能出现两种不同的畸变情况。可以根据矩形倾斜角度的不同来判断具体是哪种畸变情况。

在这里插入图片描述

判断出具体的畸变情况后,选用对应的几何相似关系,即可轻易地求出平行四边形四个顶点坐标,即得到了畸变后车牌四个顶点的坐标。

要想实现车牌的校正,还需得到畸变前车牌四个顶点的坐标。因为我国车牌的标准尺寸为440X140,因此可规定畸变前车牌的四个顶点坐标分别为:(0,0),(440,0),(0,140),(440,140)。顺序上需与畸变后的四个顶点坐标相对应。

# 畸变情况1
if rect[2] > -45:
    new_right_point_x = vertices[0, 0]
    new_right_point_y = int(vertices[1, 1] - (vertices[0, 0]- vertices[1, 0]) / (vertices[3, 0] - vertices[1, 0]) * (vertices[1, 1] - vertices[3, 1]))
    new_left_point_x = vertices[1, 0]
    new_left_point_y = int(vertices[0, 1] + (vertices[0, 0] - vertices[1, 0]) / (vertices[0, 0] - vertices[2, 0]) * (vertices[2, 1] - vertices[0, 1]))
    # 校正后的四个顶点坐标
    point_set_1 = np.float32([[440, 0],[0, 0],[0, 140],[440, 140]])
# 畸变情况2
elif rect[2] < -45:
    new_right_point_x = vertices[1, 0]
    new_right_point_y = int(vertices[0, 1] + (vertices[1, 0] - vertices[0, 0]) / (vertices[3, 0] - vertices[0, 0]) * (vertices[3, 1] - vertices[0, 1]))
    new_left_point_x = vertices[0, 0]
    new_left_point_y = int(vertices[1, 1] - (vertices[1, 0] - vertices[0, 0]) / (vertices[1, 0] - vertices[2, 0]) * (vertices[1, 1] - vertices[2, 1]))
    # 校正后的四个顶点坐标
    point_set_1 = np.float32([[0, 0],[0, 140],[440, 140],[440, 0]])

# 校正前平行四边形四个顶点坐标
new_box = np.array([(vertices[0, 0], vertices[0, 1]), (new_left_point_x, new_left_point_y), (vertices[1, 0], vertices[1, 1]), (new_right_point_x, new_right_point_y)])
point_set_0 = np.float32(new_box)

2.2.2 校正

该畸变是由于摄像头与车牌不垂直而引起的投影造成的,因此可用cv2.warpPerspective()来进行校正。

# 变换矩阵
mat = cv2.getPerspectiveTransform(point_set_0, point_set_1)
# 投影变换
lic = cv2.warpPerspective(img, mat, (440, 140))

在这里插入图片描述


    import cv2
    import numpy as np
    
    # 预处理
    def imgProcess(path):
        img = cv2.imread(path)
        # 统一规定大小
        img = cv2.resize(img, (640,480))
        # 高斯模糊
        img_Gas = cv2.GaussianBlur(img,(5,5),0)
        # RGB通道分离
        img_B = cv2.split(img_Gas)[0]
        img_G = cv2.split(img_Gas)[1]
        img_R = cv2.split(img_Gas)[2]
        # 读取灰度图和HSV空间图
        img_gray = cv2.cvtColor(img_Gas, cv2.COLOR_BGR2GRAY)
        img_HSV = cv2.cvtColor(img_Gas, cv2.COLOR_BGR2HSV)
        return img, img_Gas, img_B, img_G, img_R, img_gray, img_HSV
    
    # 初步识别
    def preIdentification(img_gray, img_HSV, img_B, img_R):
        for i in range(480):
            for j in range(640):
                # 普通蓝色车牌,同时排除透明反光物质的干扰
                if ((img_HSV[:, :, 0][i, j]-115)**2 < 15**2) and (img_B[i, j] > 70) and (img_R[i, j] < 40):
                    img_gray[i, j] = 255
                else:
                    img_gray[i, j] = 0
        # 定义核
        kernel_small = np.ones((3, 3))
        kernel_big = np.ones((7, 7))
    
        img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0) # 高斯平滑
        img_di = cv2.dilate(img_gray, kernel_small, iterations=5) # 腐蚀5次
        img_close = cv2.morphologyEx(img_di, cv2.MORPH_CLOSE, kernel_big) # 闭操作
        img_close = cv2.GaussianBlur(img_close, (5, 5), 0) # 高斯平滑
        _, img_bin = cv2.threshold(img_close, 100, 255, cv2.THRESH_BINARY) # 二值化
        return img_bin
    
    # 定位
    def fixPosition(img, img_bin):
        # 检测所有外轮廓,只留矩形的四个顶点
        contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        #形状及大小筛选校验
        det_x_max = 0
        det_y_max = 0
        num = 0
        for i in range(len(contours)):
            x_min = np.min(contours[i][ :, :, 0])
            x_max = np.max(contours[i][ :, :, 0])
            y_min = np.min(contours[i][ :, :, 1])
            y_max = np.max(contours[i][ :, :, 1])
            det_x = x_max - x_min
            det_y = y_max - y_min
            if (det_x / det_y > 1.8) and (det_x > det_x_max ) and (det_y > det_y_max ):
                det_y_max = det_y
                det_x_max = det_x
                num = i
        # 获取最可疑区域轮廓点集
        points = np.array(contours[num][:, 0])
        return points

    #img_lic_canny = cv2.Canny(img_lic_bin, 100, 200)

    def findVertices(points):
        # 获取最小外接矩阵,中心点坐标,宽高,旋转角度
        rect = cv2.minAreaRect(points)
        # 获取矩形四个顶点,浮点型
        box = cv2.boxPoints(rect)
        # 取整
        box = np.int0(box)
        # 获取四个顶点坐标
        left_point_x = np.min(box[:, 0])
        right_point_x = np.max(box[:, 0])
        top_point_y = np.min(box[:, 1])
        bottom_point_y = np.max(box[:, 1])
    
        left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0]
        right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0]
        top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0]
        bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0]
        # 上下左右四个点坐标
        vertices = np.array([[top_point_x, top_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y], [right_point_x, right_point_y]])
        return vertices, rect
    
    def tiltCorrection(vertices, rect):
        # 畸变情况1
        if rect[2] > -45:
            new_right_point_x = vertices[0, 0]
            new_right_point_y = int(vertices[1, 1] - (vertices[0, 0]- vertices[1, 0]) / (vertices[3, 0] - vertices[1, 0]) * (vertices[1, 1] - vertices[3, 1]))
            new_left_point_x = vertices[1, 0]
            new_left_point_y = int(vertices[0, 1] + (vertices[0, 0] - vertices[1, 0]) / (vertices[0, 0] - vertices[2, 0]) * (vertices[2, 1] - vertices[0, 1]))
            # 校正后的四个顶点坐标
            point_set_1 = np.float32([[440, 0],[0, 0],[0, 140],[440, 140]])
        # 畸变情况2
        elif rect[2] < -45:
            new_right_point_x = vertices[1, 0]
            new_right_point_y = int(vertices[0, 1] + (vertices[1, 0] - vertices[0, 0]) / (vertices[3, 0] - vertices[0, 0]) * (vertices[3, 1] - vertices[0, 1]))
            new_left_point_x = vertices[0, 0]
            new_left_point_y = int(vertices[1, 1] - (vertices[1, 0] - vertices[0, 0]) / (vertices[1, 0] - vertices[2, 0]) * (vertices[1, 1] - vertices[2, 1]))
            # 校正后的四个顶点坐标
            point_set_1 = np.float32([[0, 0],[0, 140],[440, 140],[440, 0]])
    
        # 校正前平行四边形四个顶点坐标
        new_box = np.array([(vertices[0, 0], vertices[0, 1]), (new_left_point_x, new_left_point_y), (vertices[1, 0], vertices[1, 1]), (new_right_point_x, new_right_point_y)])
        point_set_0 = np.float32(new_box)
        return point_set_0, point_set_1, new_box
    
    def transform(img, point_set_0, point_set_1):
        # 变换矩阵
        mat = cv2.getPerspectiveTransform(point_set_0, point_set_1)
        # 投影变换
        lic = cv2.warpPerspective(img, mat, (440, 140))
        return lic
    
    def main():
        path = 'F:\\Python\\license_plate\\test\\9.jpg'
        # 图像预处理
        img, img_Gas, img_B, img_G, img_R, img_gray, img_HSV = imgProcess(path)
        # 初步识别
        img_bin  = preIdentification(img_gray, img_HSV, img_B, img_R)
        points = fixPosition(img, img_bin)
        vertices, rect = findVertices(points)
        point_set_0, point_set_1, new_box = tiltCorrection(vertices, rect)
        img_draw = cv2.drawContours(img.copy(), [new_box], -1, (0,0,255), 3)
        lic = transform(img, point_set_0, point_set_1)
        # 原图上框出车牌
        cv2.namedWindow("Image")
        cv2.imshow("Image", img_draw)
        # 二值化图像
        cv2.namedWindow("Image_Bin")
        cv2.imshow("Image_Bin", img_bin)
        # 显示校正后的车牌
        cv2.namedWindow("Lic")
        cv2.imshow("Lic", lic)
        # 暂停、关闭窗口
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    
    if __name__ == '__main__':
        main()



在这里插入图片描述

7 最后

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1418628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pytest单元测试框架

第一章、pytest概述 Pytest is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or l…

Onvif协议5: 预置位的操作

目录 1. 介绍 2. GetPreset 3. SetPreset 4.预置位的索引 5. GotoPreset 1. 介绍 球机的云台预置位控制主要包含的内容有&#xff1a;预置位的设置、预置位的调用、预置位的删除以及预置位的名称修改等 云台预置位的设置&#xff1a;调用Onvif协议中云台预置位设置接口&a…

CH395Q之CH395Q简介(一)

本节主要介绍以下内容&#xff1a; 1、TCP/IP协议栈是什么&#xff08;了解&#xff09; 2、CH395Q是什么&#xff08;了解&#xff09; 3、CH395Q工作命令&#xff08;熟悉&#xff09; 4、CH395Q & W5500 一、TCP/IP协议栈是什么 是一系列网络协议的总和&#xff0…

如何使用宝塔面板搭建MySQL 5.5数据库并实现公网远程连接

文章目录 前言1.Mysql服务安装2.创建数据库3.安装cpolar3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 宝塔面板的简易操作性,使得运维难度降低,简化了Linux命令行进行繁琐的配置,下面简单几步,通过宝塔面板cp…

微信小程序|摇骰子

目录 简介设计与功能需求确定用户界面设计确定摇骰子动画效果确定随机数生成算法编码实现实现摇骰子动画测试与优化进行功能测试进行性能测试说明简介 制作一个摇骰子小程序是一个有趣且具有挑战性的项目。通过这个项目,你可以学习如何运用编程技术来模拟骰子的摇动和结果显示…

【全程录屏GPT3.5升级4.0】2024最新GPT4升级订阅详细指南

前言&#xff1a;为什么要升级GPT4.0&#xff0c;下图是来自GPT4.0的官方回答&#xff0c;可以看出&#xff0c;GPT4无愧于是一个大版本升级的。 一、视频教程 记录了普通用户使用WildCrad从GPT3.5升级到4.0的全部过程&#xff0c;感兴趣可以前往观看&#xff1a;https://www.…

qemu 抓取linux kernel vmcore

一、背景 在qemu调试linux kernel时 有时我们会遇到dump 情况&#xff0c;这时可以通过gdb 方式连接分析dump&#xff0c; 但实际中我们用得更多的是离线dump 分析&#xff0c;分析的文件通常是vmcore&#xff08;linux kernel panic 生成的coredump文件&#xff09;或者ramdu…

合并有序链表---链表OJ---归并思想

https://leetcode.cn/problems/merge-two-sorted-lists/?envTypestudy-plan-v2&envIdtop-100-liked 将两个有序的链表合并为一个新的有序链表&#xff0c;那不就是和归并排序中最后合并的思想一样吗&#xff1f;只不过那里合并的是数组&#xff0c;这里合并的是链表。 首先…

构建知识图谱:从技术到实战的完整指南

目录 一、概述二、知识图谱的基础理论定义与分类核心组成历史与发展 三、知识获取与预处理数据源选择数据清洗实体识别 四、知识表示方法知识表示模型RDFOWL属性图模型 本体构建关系提取与表示 五、知识图谱构建技术图数据库选择Neo4jArangoDB 构建流程数据预处理实体关系识别图…

C++类和对象引入以及类的介绍使用

文章目录 一、面向过程和面向对象的初步认识二、类的引入2.2 类的引入 三、类的访问限定符及封装3.3 访问限定符3.4 【面试题】C中struct和class的区别3.5 类的两种定义方式 四、封装【面试题】面向对象的三大特性 五、类的作用域六、类的实例化七、类对象模型7.1 类对象的存储…

[嵌入式系统-5]:龙芯1B 开发学习套件 -2- LoongIDE 集成开发环境集成开发环境的安装步骤

目录 一、LoongIDE&#xff08;龙芯开发工具集成环境&#xff09;概述 1.1 概述 二、软件开发环境的安装过程 2.0 注意事项 2.1 步骤1&#xff1a;MingW运行环境 2.2 步骤2&#xff1a;安装LoongIDE 2.3 步骤3&#xff1a;安装MIPS工具链 2.4 配置工具链 2.5 重启电脑…

做什么副业比较靠谱,这五个正规项目,记得收藏

人这一生不易&#xff0c;每个阶段都会有压力和烦恼&#xff0c;尤其是成年人&#xff0c;上有老下有小的&#xff0c;生活的重担都在一个人身上&#xff0c;压得人喘不过气。生活的方方面面都需要钱&#xff0c;仅靠工资已经很难维持一家人的开支了。所以很多人打算利用业余时…

基于YOLOv7算法和FLIR数据集的高精度实时红外行人目标检测系统(PyTorch+Pyside6+YOLOv7)

摘要&#xff1a;基于YOLOv7算法和FLIR数据集的高精度实时红外行人目标检测系统可用于日常生活中检测与定位自行车、汽车、狗和行人目标&#xff0c;此系统可完成对输入图片、视频、文件夹以及摄像头方式的目标检测与识别&#xff0c;同时本系统还支持检测结果可视化与导出。本…

Alexa Fluo350 NHS 酯,AF350活性酯,可用于标记胺修饰的寡核苷酸

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;AF350 Succinimidyl Ester&#xff0c;AF350 NHS Ester&#xff0c;Alexa Fluo350 NHS 酯&#xff0c;AF350活性酯 一、基本信息 产品简介&#xff1a;AF 350 is a blue fluorescent dye. AF 350 dye is used for s…

NPDP证书:让你的职业生涯飞升!

&#x1f31f;没错&#xff01;NPDP证书正在成为产品经理们的“新宠”&#xff01;越来越多的同行们纷纷选择考取NPDP证书&#xff0c;为什么这么火爆&#xff1f;一起来探究下吧&#xff01; &#x1f680;NPDP认证&#xff1a;产品经理的国际通行证 &#x1f4cd;NPDP&#x…

雪深传感器的工作原理

TH-XL2雪深传感器的工作原理主要是利用超声波的发射和反射来测量雪的厚度。传感器发射超声波&#xff0c;超声波遇到雪表面后会反射回来&#xff0c;传感器再接收反射回来的超声波&#xff0c;通过计算超声波的传播时间和速度&#xff0c;就可以得出雪的厚度。 另外&#xff0c…

给信息安全专业想做网络安全方面的人一些忠告

别一直打CTF 打CTF是为了打基础&#xff0c;大概知道一些基础就出来吧&#xff0c;千万不要一直打下去出不来了。简历上实习经历&#xff0c;项目经历以及漏洞成果才能构成一个不错的背景&#xff0c;只有ctf比赛会很尴尬。要知道有些人是py打比赛&#xff0c;面试官知道情况&…

根据两个主键查询变更日志 抽屉时间线降序

时间戳例子&#xff1a; <div class"block"><el-timeline><el-timeline-item timestamp"2018/4/12" placement"top"><el-card><h4>更新 Github 模板</h4><p>王小虎 提交于 2018/4/12 20:46</p>…

SimpleDateFormat 格式化 Date 时间戳

前言 Date 是 Java 中经常用来表示时间的类型&#xff0c;但将 Date 类型的数据发送给前端时&#xff0c;通常会呈现出乱码的状态&#xff0c;用户不宜理解&#xff0c;所以要通过 SimpleDateFormat 把 Date 类型的数据格式化为用户容易理解的格式 如下是 Date 的格式&#xff…

GIS应用水平考试一级—2009 年度第二次

全国信息化工程师——GIS应用水平考试 2009 年度第二次全国统一考试一级 试卷说明: 1、本试卷共9页,6个大题,满分150 分,150 分钟完卷。 2、考试方式为闭卷考试。 3、将第一、二、三題的答案用铅笔涂写到(NCIE-GIS)答题卡上。 4、将第四、五、六题的答案填写到主观题答题卡上…