OpenCV之信用卡识别实战

文章目录

  • 代码
  • 视频讲解
  • 模板匹配文件
  • 主程序(ocr_template_match.py)
  • myutils.py

代码

链接: https://pan.baidu.com/s/1KjdiqkyYGfHk97wwgF-j3g?pwd=hhkf 提取码: hhkf

视频讲解

链接: https://pan.baidu.com/s/1PZ6w5NcSOuKusBTNa3Ng2g?pwd=79wr 提取码: 79wr

模板匹配文件

请添加图片描述

主程序(ocr_template_match.py)

# 导入工具包
from imutils import contours # 从imutils库导入contours模块,imutils是一个方便的图像处理库,contours模块主要用于处理和操作图像轮廓。
import numpy as np
import argparse # 处理命令行参数
import cv2
import myutils# 设置参数
ap = argparse.ArgumentParser() # 创建一个ArgumentParser对象,这个对象会保存命令行参数和程序文档等信息# 添加一个命令行参数,参数的短名称是"-i",长名称是"--image",这个参数是必须的(required=True),参数的含义是输入图像的路径。
ap.add_argument("-i", "--image", required=True,help="path to input image")# 添加另一个命令行参数,参数的短名称是"-t",长名称是"--template",这个参数也是必须的,参数的含义是模板图像的路径。
# 这个模板图像是OCR字体的图像,其中包含了0-9的数字。
ap.add_argument("-t", "--template", required=True,help="path to template OCR-A image")# 解析命令行参数,然后将结果转为一个字典,字典的键是参数的名称,值是参数的值。
args = vars(ap.parse_args())
print('\n', args) # {'image': 'images/credit_card_03.png', 'template': 'images/ocr_a_reference.png'}# 指定信用卡类型
"""
定义了一个名为FIRST_NUMBER的字典,这个字典中的键是信用卡卡号的首位数字的字符串形式,对应的值是信用卡的类型。
这是因为信用卡的种类可以通过卡号的首位数字来区分。
比如credit_card_03.png里面的信用卡的首位数字是5,那么这张信用卡就是MasterCard
"""
FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"
}
# 绘图展示函数
def cv_show(name,img):cv2.imshow(name, img) # name: 窗口名称; img: 要展示的图像cv2.waitKey(0)cv2.destroyAllWindows()cv2.waitKey(1) # 对于MacOS系统,cv2.waitKey(1)是必要的,用于处理一些特定的系统事件,但在Windows上不一定需要。# 读取一个模板图像。使用OpenCV的imread函数读取模板图像,图像的路径从前面解析的命令行参数args中获取。
img = cv2.imread(args["template"])
cv_show('img',img)# 灰度图。将模板图像转换为灰度图像,使用OpenCV的cvtColor函数,COLOR_BGR2GRAY表示从BGR颜色空间转换到灰度空间。
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref',ref) # 展示灰度图像"""
将灰度图像转换为二值图像,使用OpenCV的threshold函数。
10是阈值,255是当像素值高于(对于THRESH_BINARY_INV)或者小于(对于THRESH_BINARY)阈值时应给予的新的像素值 ,
THRESH_BINARY_INV表示进行反二进制阈值化(低于阈值的像素设置为255,高于阈值的像素设置为0)。
"""
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref) # 展示二值图像"""
计算轮廓
使用OpenCV的findContours函数计算二值图像的轮廓,RETR_EXTERNAL表示只检测外轮廓,CHAIN_APPROX_SIMPLE表示只保留终点坐标
返回的refCnts中每个元素都是图像中的一个轮廓
"""
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
"""
cv2.drawContours函数用来在图像上画出找到的轮廓。第一个参数img是要绘制轮廓的图像,第二个参数refCnts是从前面找到的轮廓列表,
第三个参数-1表示要绘制所有轮廓,(0,0,255)是轮廓的颜色(这里是红色),3是线宽。
"""
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
cv_show('img', img)  # 展示绘制了轮廓的图像"""
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] 这行代码的主要作用是对模板图像中的轮廓进行排序。在这里,myutils.sort_contours(refCnts, method="left-to-right")[0] 是调用 sort_contours 函数,对 refCnts(模板图像中的轮廓)进行排序。method="left-to-right" 参数表示轮廓应该按照从左到右的顺序排序。这对于在图像中识别和分类对象(如数字)时非常有用,因为我们通常会按照从左到右的顺序进行阅读和识别。sort_contours 函数的返回值是一个元组,其中包含两个元素:排序后的轮廓和对应的边界框。由于我们只关心排序后的轮廓,所以使用 [0] 索引来获取第一个元素,即排序后的轮廓。因此,这行代码的结果 refCnts 是一个列表,其中的轮廓已经按照从左到右的顺序进行了排序。这样处理之后,程序可以按照这个顺序逐个处理和识别每个数字。
"""
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] # 排序,从左到右,从上到下
# cv2.drawContours(img, refCnts, 0, (255, 0, 0), 2)
# cv_show('img', img) # 同学们这里可以修改method看看输出会有什么区别digits = {} # 定义一个空字典digits,将用来存储每个数字模板。
# 遍历每一个轮廓
for (i, c) in enumerate(refCnts): # 遍历轮廓列表 refCnts。在每一次循环中,索引 i 和对应的轮廓 c 都被提取出来。# 计算外接矩形并且resize成合适大小(x, y, w, h) = cv2.boundingRect(c) # 对于每一个轮廓 c,计算其外接矩形,外接矩形的坐标被存储在 (x, y) 中,矩形的宽度和高度分别是 w 和 h# 使用刚刚计算出来的外接矩形的坐标和大小,从灰度图像 ref 中提取出对应的区域,这就是我们的 ROI。# 在OpenCV和很多其他图像处理库中,图像的索引方式通常为 [y:y+h, x:x+w],这样的顺序:先行(高度)后列(宽度)roi = ref[y:y + h, x:x + w]roi = cv2.resize(roi, (57, 88)) # 调整 ROI 的大小,使得每一个数字模板的大小都是一致的(这里是 57x88 像素)。# 将提取和调整大小后的 ROI 存储在字典 digits 中,字典的键是数字的索引 i,值是对应的数字模板。digits[i] = roi# 初始化卷积核
# 这两行代码生成了两个矩形的结构元素(也称为卷积核或结构核),分别为9x3和5x5的大小。这些结构元素将在接下来的形态学操作中使用。
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))#读取输入图像,预处理
image = cv2.imread(args["image"])
cv_show('image',image)
image = myutils.resize(image, width=300) # 用自定义的resize函数将图像的宽度缩放到300像素,高度按比例缩放
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 将缩放后的彩色图像转换为灰度图像。在OpenCV中,颜色图像通常是BGR格式,而在很多图像处理任务中,我们只需要灰度图像。
cv_show('gray',gray)# 礼帽操作:原始输入-开运算(先腐蚀,在膨胀)的结果,突出更明亮的区域;黑帽操作常常用于强调比周围暗的区域,或者是强调比较小,且比周围区域暗的细节部分。
# 这行代码对灰度图像执行了一个名为"礼帽"的形态学操作,这是通过使用前面定义的9x3的矩形卷积核对图像进行开运算,然后从原始图像中减去结果来实现的
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
cv_show('tophat',tophat)# 这行代码对进行了"礼帽"操作的图像应用Sobel算子,以计算图像的x方向(水平方向)的梯度。
# 这里ksize=-1表示使用3x3的Sobel滤波器。
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, #ksize=-1相当于用3*3的ksize=-1)
gradX = np.absolute(gradX) # 这行代码将Sobel梯度的值转换为绝对值,因为梯度值可能是正值也可能是负值,转换为绝对值使所有梯度值都为正,方便后续处理。
"""
这三行代码对梯度图像进行了归一化和量化处理。
首先找到梯度图像的最小值和最大值,然后将梯度图像的亮度值线性映射到0-255的范围,以便显示和保存。
最后,将浮点型的梯度图像转换为8位无符号整型。
"""
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")
print (np.array(gradX).shape) # 这行代码打印出处理后的梯度图像的形状(尺寸),包括高度和宽度。
cv_show('gradX',gradX)# 通过闭操作(先膨胀,再腐蚀)将数字连在一起
# 使用闭操作来连结图像中的数字。闭操作是先进行膨胀,然后进行腐蚀。这样可以将断开的部分连在一起,主要用来关闭前景物体内部的小孔或者小黑点。这里使用的是前面定义的rectKernel,一个长方形的结构元素。
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 
cv_show('gradX',gradX)"""
使用阈值化操作来将图像转换为二值图像。其中,cv2.THRESH_BINARY表示大于阈值的像素点设为最大值(这里是255),小于阈值的设为0;
cv2.THRESH_OTSU表示使用Otsu算法来自动确定最优阈值,该方法适合对于双峰图像,需要将阈值参数设置为0。
输出的thresh是一个二值化后的图像。
"""
thresh = cv2.threshold(gradX, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 
cv_show('thresh',thresh)# 再进行一次闭操作。这次使用的结构元素是sqKernel,一个正方形的结构元素,其目的是使得图像中的数字更加紧密,减小数字间的空隙。
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作
cv_show('thresh',thresh)"""
计算图像的轮廓。cv2.findContours函数用来寻找二值图像中的轮廓。
这里使用的是cv2.RETR_EXTERNAL模式,表示只查找最外层的轮廓;cv2.CHAIN_APPROX_SIMPLE表示轮廓的存储方式,这种方式会存储轮廓的转折点,从而节省内存空间。
thresh.copy()是为了保护原图,因为findContours函数会修改输入的图像。
"""
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
# 在原图上画出找到的轮廓。
# cv2.drawContours函数用来在图像上画出轮廓,-1表示画出所有轮廓,(0,0,255)是轮廓的颜色,这里是红色,3是轮廓的宽度。
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) 
cv_show('img',cur_img)# 下面这段代码的主要目的是从所有找到的轮廓中筛选出数字的轮廓,并将这些轮廓按照从左到右的顺序排序。
locs = [] # 初始化一个空列表 locs,用于存储选出来的轮廓。
# 遍历找到的所有轮廓
for (i, c) in enumerate(cnts):# 对于每个轮廓,先计算其外接矩形,得到矩形的坐标 (x, y) 和宽高 (w, h)(x, y, w, h) = cv2.boundingRect(c)"""计算矩形的长宽比(宽度除以高度)。如果一个轮廓的外接矩形的长宽比在一定范围内(比如这里的2.5到4.0),并且宽度和高度也在一定范围内(比如这里的宽度40到55,高度10到20),就认为这个轮廓可能是一个数字的轮廓,将其保存到 locs 中。"""ar = w / float(h)# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组if ar > 2.5 and ar < 4.0:if (w > 40 and w < 55) and (h > 10 and h < 20):# 符合的留下来locs.append((x, y, w, h))# 展示此时的结果clone = image.copy()cv2.rectangle(clone, (x, y), (x + w, y + h), (0, 255, 0), 2)cv_show("Selection", clone)# 对 locs 中的轮廓按照 x 坐标(即从左到右)进行排序。
locs = sorted(locs, key=lambda x:x[0]) # locs是一个包含4个元素的列表
# 展示排序后的结果
for (i, (gX, gY, gW, gH)) in enumerate(locs):clone = image.copy()cv2.rectangle(clone, (gX, gY), (gX + gW, gY + gH), (0, 255, 0), 2)cv_show("Sorted", clone)# 这段代码的目的是遍历存储的符合条件的轮廓(即信用卡上的数字组),对每一组数字进行处理以识别出其中的数字,并将识别的数字显示在图像上。
output = []
# 遍历 locs 列表,它包含了一系列的轮廓(或者说是数字组)。每个轮廓都有自己的坐标 (gX, gY) 和尺寸 (gW, gH)
for (i, (gX, gY, gW, gH)) in enumerate(locs):# 初始化一个空列表 groupOutput,用于保存当前轮廓(数字组)内识别出的数字groupOutput = []# 这一行是从灰度图像中切出当前轮廓(数字组)的部分。稍微比轮廓大一些,是为了尽量避免切割到数字。group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]cv_show('group',group)# 对切出的图像进行二值化处理。二值化可以使得后续的轮廓检测操作更准确。group = cv2.threshold(group, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]cv_show('group',group)# 在二值化后的图像中寻找轮廓,这个轮廓就对应于信用卡上的每一个数字。digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 将找到的轮廓(即数字)按照从左到右的顺序进行排序,以确保我们识别出的信用卡号是按照正确的顺序。digitCnts = contours.sort_contours(digitCnts,method="left-to-right")[0]# 遍历每一个数字的轮廓for c in digitCnts:# 找到当前数值的轮廓,resize成合适的的大小(x, y, w, h) = cv2.boundingRect(c)# 从数字组图像中切出当前数字的部分roi = group[y:y + h, x:x + w]# 将切出的数字图像的尺寸调整为57x88,这是为了和模板的尺寸一致,从而可以进行模板匹配。roi = cv2.resize(roi, (57, 88))cv_show('roi',roi)# 计算匹配得分scores = []# 遍历每一个数字模板for (digit, digitROI) in digits.items():# 将切出的数字图像和模板进行匹配,得到的结果是一个矩阵,每一个元素是对应位置上的匹配得分。result = cv2.matchTemplate(roi, digitROI,cv2.TM_CCOEFF)# 找到匹配得分矩阵中的最大值和最小值,以及它们的位置。score 是最大匹配得分(_, score, _, _) = cv2.minMaxLoc(result)scores.append(score) # 将当前模板的匹配得分添加到得分列表中# 在所有模板的匹配得分中找出最大的那个,最大得分对应的数字就是识别出来的数字。将识别出来的数字添加到当前数字组的输出列表中。groupOutput.append(str(np.argmax(scores)))"""这两行代码的主要目的是在原始图像上标记出识别的数字,并且显示识别结果。1. cv2.rectangle(image, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1):这行代码用于在原始图像上画出一个矩形,表示每一组的数字区域。cv2.rectangle()函数的参数是:图像、左上角坐标、右下角坐标、颜色和线条宽度。(gX - 5, gY - 5) 和 (gX + gW + 5, gY + gH + 5) 分别是矩形的左上角和右下角坐标,(0, 0, 255) 是矩形的颜色(这里是红色,因为在OpenCV中,颜色是以BGR而非RGB表示的),1 是线条的宽度。2. cv2.putText(image, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2): 这行代码是将识别的数字添加到图像上。cv2.putText()函数的参数是:图像、文本、位置、字体、字体大小、颜色和线条宽度。"".join(groupOutput) 是要添加的文本,它是将每一组识别的数字拼接成一个字符串;(gX, gY - 15) 是文本的位置,这里选择的是每一组数字的左上角稍微向上一点的位置;cv2.FONT_HERSHEY_SIMPLEX 是字体;0.65 是字体的大小;(0, 0, 255) 是字体的颜色(这里选择的也是红色);2 是线条的宽度。"""cv2.rectangle(image, (gX - 5, gY - 5),(gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)cv2.putText(image, "".join(groupOutput), (gX, gY - 15),cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)# 将识别出来的数字添加到最终的输出列表中output.extend(groupOutput)# 打印出识别出来的信用卡类型和信用卡号。这里 FIRST_NUMBER 是一个字典,它将信用卡号的第一位数字映射到对应的信用卡类型。
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv_show("Image", image) # 显示处理后的图像,可以看到识别出的信用卡号已经被画在了原图上。

myutils.py

import cv2"""
sort_contours函数的作用是对轮廓和其外接矩形进行排序。让我们一步一步地解读:- zip(cnts, boundingBoxes)将轮廓列表cnts和外接矩形列表boundingBoxes合并成一个列表,其中每个元素都是一个由轮廓和其外接矩形组成的元组。- sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse)使用Python的sorted函数对上述列表进行排序。- 排序的关键是key=lambda b: b[1][i],其中b是列表中的一个元素,也就是一个由轮廓和其外接矩形组成的元组,b[1]是外接矩形,b[1][i]是外接矩形的x或y坐标,取决于i的值。- 如果reverse=True,则对结果进行反向排序,即从大到小排序;如果reverse=False,则直接按照从小到大的顺序排序。- zip(*sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse))使用zip函数和*操作符将排序后的列表转换回原来的形式,即分别是轮廓列表和外接矩形列表。- (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse))将返回的轮廓列表和外接矩形列表分别赋值给cnts和boundingBoxes。所以,这行代码的总体作用是按照x或y坐标对轮廓和其外接矩形进行排序,然后返回排序后的轮廓列表和外接矩形列表。怕同学们忘记,补充一个点:
cv2.boundingRect()函数是一个计算轮廓外接矩形的函数,它的参数是一个轮廓。该函数返回一个表示外接矩形的四元组,这四个值分别为(x, y, w, h)。- x 和 y 表示外接矩形左上角点的坐标。
- w 和 h 分别表示外接矩形的宽度和高度。"""
def sort_contours(cnts, method="left-to-right"):reverse = Falsei = 0if method == "right-to-left" or method == "bottom-to-top":reverse = Trueif method == "top-to-bottom" or method == "bottom-to-top":i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts] #用一个最小的矩形,把找到的形状包起来x,y,h,w(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))return cnts, boundingBoxes"""
resize函数用于调整图像的大小下面这段代码定义了一个叫做resize的函数,这个函数接收一个图像和一个宽度或高度的值(或两者都有),并将这个图像缩放到指定的尺寸。如果没有给出宽度和高度值,那么函数将返回原图。插值方法默认为cv2.INTER_AREA。
"""
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None # 这里初始化了一个变量dim,它将在后续的代码中被用作储存图像新的尺寸(h, w) = image.shape[:2] # 这里从输入图像的形状(即尺寸)中提取出图像的高度(h)和宽度(w)if width is None and height is None: # 这一行代码检查了是否给出了新的宽度或高度值。如果没有给出,那么函数将直接返回原图return image# 如果没有指定新的宽度,那么我们就需要按照新的高度值来缩放图像。计算新的宽度值的方法是通过新的高度值与原高度的比例来计算新的宽度。if width is None:r = height / float(h)dim = (int(w * r), height)else: #  如果给出了新的宽度值,那么我们就需要按照新的宽度值来缩放图像。计算新的高度值的方法是通过新的宽度值与原宽度的比例来计算新的高度。r = width / float(w)dim = (width, int(h * r))"""这段if-else代码的作用是在保持图像原有长宽比(即纵横比)的条件下,根据给出的新的宽度或高度来计算出新的图像尺寸。例子1:如果只给出了新的高度值 height(即 width 是 None),那么我们需要计算出新的宽度值,方法是:首先,我们计算出新的高度值与原高度 h 的比例 r = height / float(h)。然后,我们根据这个比例来计算新的宽度值 w * r,并将其转化为整数(因为像素值必须是整数)。最后,我们得到新的图像尺寸 dim = (int(w * r), height)。例子2:如果给出了新的宽度值 width,那么我们需要计算出新的高度值,方法是:首先,我们计算出新的宽度值与原宽度 w 的比例 r = width / float(w)。然后,我们根据这个比例来计算新的高度值 h * r,并将其转化为整数。最后,我们得到新的图像尺寸 dim = (width, int(h * r))。这样做的好处是可以保证图像在缩放过程中不会发生扭曲,也就是保持了图像的原有长宽比。"""# 调用cv2.resize函数,将图像缩放到新的尺寸resized = cv2.resize(image, dim, interpolation=inter)"""cv2.resize()是OpenCV库中的一个函数,用于调整图像的大小。该函数接收三个参数:- 第一个参数 image:需要调整大小的原始图像。- 第二个参数 dim:一个包含两个元素的元组,表示新的图像大小。这个大小是你要调整到的目标大小。- 第三个参数 interpolation:插值方法,也就是当我们调整图像大小时,如何计算新的像素值。 cv2.INTER_AREA 是一种插值方法,适合于图像缩小。OpenCV还提供了其他插值方法,如 cv2.INTER_LINEAR(线性插值,也是默认方法)、cv2.INTER_CUBIC(立方插值)等,这些方法在图像放大时可能会有较好的效果。函数的返回值 resized 就是调整大小后的新图像。这个新图像的大小就是 dim 所指定的大小。举个例子,如果你有一个大小为1000x1000的图像,你希望将它缩小到500x500,那么你可以这样调用这个函数:resized_image = cv2.resize(original_image, (500, 500), interpolation=cv2.INTER_AREA)在这个例子中,resized_image 就是大小为500x500的新图像。"""return resized # 返回缩放后的图像

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

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

相关文章

Ubuntu开机自启服务systemd.service配置教程(Ubuntu服务)(Linux服务)upstart

文章目录 为什么要将程序配置成服务&#xff1f;1. 自动启动2. 后台运行3. 定时重启4. 简化管理5. 整合系统 版本支持1. Ubuntu 14.04及更早版本&#xff1a;使用upstart作为默认的init系统/etc/rc.local旧版本新版本 2. Ubuntu 15.04到16.04版本&#xff1a;默认使用systemd作…

【敏捷开发】测试驱动开发(TDD)

测试驱动开发&#xff08;Test-Driven Development&#xff0c;简称TDD&#xff09;是敏捷开发模式中的一项核心实践和技术&#xff0c;也是一种设计方法论。TDD有别于以往的“先编码&#xff0c;后测试”的开发模式&#xff0c;要求在设计与编码之前&#xff0c;先编写测试脚本…

1310. 数三角形

题目链接&#xff1a;https://www.acwing.com/problem/content/1312/ 首先不考虑三点共线的情况一共有 种&#xff0c;现在来计算三点共线的情况 1.三点在一条直线上 2.三点在一条竖线上 3.三点在一条斜线上&#xff0c;正反斜线对称&#xff0c;仅需考虑一边的情况 如果…

14-5_Qt 5.9 C++开发指南_基于HTTP 协议的网络应用程序

文章目录 1. 实现高层网络操作的类2. 基于HTTP协议的网络文件下载3.源码3.1 可是化UI设计3.2 mainwindow.h3.3 mainwindow.cpp 1. 实现高层网络操作的类 Qt 网络模块提供一些类实现 OSI 7 层网络模型中高层的网络协议&#xff0c;如 HTTP、FTP、SNMP等&#xff0c;这些类主要是…

Netty使用和常用组件辨析

Netty 使用和常用组件 简述 <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId <version>4.1.42.Final </version> <scope>compile</scope> </dependency> Netty 的优势 1 、 AP…

【小吉带你学Git】idea操作(2)_版本和分支的相关操作

&#x1f38a;专栏【Git】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【Counting Stars 】 欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f354;版本⭐首先创建一个项目⭐添加暂存区⭐提交本地库&#x1f33…

压力测试与测试工具jmeter的介绍

目录 一、性能指标 二、jmeter &#xff08;一&#xff09;JMeter 安装 &#xff08;二&#xff09;JMeter 压测示例 1、添加线程组 2、添加 HTTP 请求 3、添加监听器 4、启动压测&查看分析结果 &#xff08;三&#xff09;JMeter Address Already in use 错误解决 压力测…

yum出现Could not retrieve mirrorlist解决方法

Loaded plugins: fastestmirror, security Loading mirror speeds from cached hostfile Could not retrieve mirrorlist http://mirrorlist.centos.org/?release6&archi386&repoos error was 14: PYCURL ERROR 6 - “Couldn’t resolve host ‘mirrorlist.centos.org…

【web逆向】全报文加密及其登录流程的分析案例

aHR0cHM6Ly9oZWFsdGguZWxkZXIuY2NiLmNvbS9zaWduX2luLw 涉及加密库jsencrypt 定位加密点 先看加密的请求和响应&#xff1a; 全局搜索加密字段jsondata&#xff0c;这种非特定参数的一般一搜一个准&#xff0c;搜到就是断点。起初下的断点没停住&#xff0c;转而从调用栈单步…

K8S系列文章之 kubeasz部署K8S环境

自动化安装方式&#xff08;kubeasz&#xff09;* 生产环境推荐&#xff08;首次安装下载相关配置和安装包&#xff09;是基于Ansible实现的部署工具 简单介绍 每一具体k8s集群的详细配置参数文件 Ansible 任务配置文件 镜像安装包 安装部署步骤 前提 &#xff1a; 保证Ansib…

Python web实战之 Django 的模板语言详解

关键词&#xff1a; Python、web开发、Django、模板语言 概要 作为 Python Web 开发的框架之一&#xff0c;Django 提供了一套完整的 MVC 模式&#xff0c;其中的模板语言为开发者提供了强大的渲染和控制前端的能力。本文介绍 Django 的模板语言。 1. Django 模板语言入门 Dj…

npm发布包

1.npm 登录 在控制台输入命令 npm login 按提示输入用户名&#xff0c;密码&#xff0c;邮箱后登录 如果出现如下提示 需要将淘宝镜像源切换为npm源&#xff0c;删除或注释以下内容就行 2.发布 进入准备发布的代码的根目录下&#xff0c;输入命令 npm publish 3.删除已发…

怎么学习CSS相关技术知识? - 易智编译EaseEditing

学习CSS技术是前端开发中的重要一环&#xff0c;它用于控制网页的样式和布局&#xff0c;使网页更加美观和易于使用。以下是学习CSS技术的几个方面&#xff1a; 基本语法和选择器&#xff1a; 了解CSS的基本语法&#xff0c;学习如何使用选择器来选择HTML元素并应用样式。 样…

一条sql语句在mysql中如何执行(查询+更新)

文章目录 一 MySQL 基础架构1.1 MySQL 基本架构1.2 Server 层基本组件介绍1) 连接器2) 查询缓存(MySQL 8.0 版本后移除)3) 分析器4) 优化器5) 执行器 二 语句分析2.1 查询语句2.2 更新语句为什么要用两个日志模块&#xff0c;用一个日志模块不行吗?为什么必须有“两阶段提交”…

MySQL的索引使用的数据结构,事务知识

一、索引的数据结构&#x1f338; 索引的数据结构&#xff08;非常重要&#xff09; mysql的索引的数据结构&#xff0c;并非定式&#xff01;&#xff01;&#xff01;取决于MySQL使用哪个存储引擎 数据库这块组织数据使用的数据结构是在硬盘上的。我们平时写的代码是存在内存…

MyCat核心概念、需求案例讲解、环境准备及分片配置

1.MyCat概念介绍 2.MyCat入门需求 2.1 需求分析 2.2 环境准备 输入以下命令检查服务器防火墙状态 dead代表关闭状态&#xff0c;如果不关闭也可以需要开放特定的端口号&#xff01;&#xff01; systemctl status firewalld接着需要在三台服务器上的MySQL上创建三个数据库db0…

企业架构NOSQL数据库之MongoDB

目录 一、背景描述及其方案设计 (一)业务背景描述 &#xff08;二&#xff09;模拟运维设计方案 二、Mongodb介绍 &#xff08;一&#xff09;nosql介绍 &#xff08;二&#xff09;产品特点 1、存储性 2、 效率性 3、结构 三、安装和配置 &#xff08;一&#xff09…

Leetcode-每日一题【剑指 Offer 10- I. 斐波那契数列】

题目 写一个函数&#xff0c;输入 n &#xff0c;求斐波那契&#xff08;Fibonacci&#xff09;数列的第 n 项&#xff08;即 F(N)&#xff09;。斐波那契数列的定义如下&#xff1a; F(0) 0, F(1) 1 F(N) F(N - 1) F(N - 2), 其中 N > 1. 斐波那契数列由 0 和 1 开…

[openCV]基于赛道追踪的智能车巡线方案V1

import cv2 as cv import os import numpy as npimport time# 遍历文件夹函数 def getFileList(dir, Filelist, extNone):"""获取文件夹及其子文件夹中文件列表输入 dir&#xff1a;文件夹根目录输入 ext: 扩展名返回&#xff1a; 文件路径列表""&quo…

K3s vs K8s:轻量级对决 - 探索替代方案

在当今云原生应用的领域中&#xff0c;Kubernetes&#xff08;简称K8s&#xff09;已经成为了无可争议的领导者。然而&#xff0c;随着应用规模的不断增长&#xff0c;一些开发者和运维人员开始感受到了K8s的重量级特性所带来的挑战。为了解决这一问题&#xff0c;一个名为K3s的…