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 # 返回缩放后的图像
相关文章:

OpenCV之信用卡识别实战
文章目录 代码视频讲解模板匹配文件主程序(ocr_template_match.py)myutils.py 代码 链接: https://pan.baidu.com/s/1KjdiqkyYGfHk97wwgF-j3g?pwdhhkf 提取码: hhkf 视频讲解 链接: https://pan.baidu.com/s/1PZ6w5NcSOuKusBTNa3Ng2g?pwd79wr 提取码: 79wr 模板匹配文件 …...

Detector定位算法在FPGA中的实现——section1 原理推导
关于算法在FPGA中的实现,本次利用业余的时间推出一个系列章节,专门记录从算法的推导、Matlab的实现、FPGA的移植开发与仿真做一次完整的FPGA算法开发,在此做一下相关的记录和总结,做到温故知新。 这里以Detector在Global Coordinate System(原点为O)中运动为背景,Detect…...
心电信号去噪:方法与应用
目录 1 去噪技术的发展历程 2 滤波器去噪的应用 3 小波去噪的优势 4 深度学习去噪的前景...

睡眠助手/白噪音/助眠夜曲微信小程序源码下载 附教程
睡眠助手/白噪音/助眠夜曲微信小程序源码 附教程 支持分享海报 支持暗黑模式 包含了音频数据 最近很火的助眠小程序,前端vue,可以打包H5,APP,小程序 后台可以设置流量主广告,非常不错的源码 代码完整 完美运营 搭配无…...
Spring Cloud常见问题处理和代码分析
目录 1. 问题:如何在 Spring Cloud 中实现服务注册和发现?2. 问题:如何在 Spring Cloud 中实现分布式配置?3. 问题:如何在 Spring Cloud 中实现服务间的调用?4. 问题:如何在 Spring Cloud 中实现…...
debian怎么修改man help为中文,wsl怎么修改显示语言为中文
在Debian 12系统中,要将系统语言和Man帮助手册设置为中文,需要执行以下步骤: 安装中文语言包: 首先,更新软件包列表并安装中文语言包。打开终端并运行以下命令: sudo apt update sudo apt install locales配…...
k8s概念-亲和力与反亲和力
回到目录 亲和力 Affinity 对部署调度时的优先选择 分为 节点亲和力 pod亲和力 pod反亲和力 节点亲和力 NodeAffinity 进行 pod 调度时,优先调度到符合条件的亲和力节点上 可配置 硬亲和力和软亲和力 RequiredDuringSchedulingIgnoredDuringExecution 硬…...

【数据结构】实现单链表的增删查
目录 1.定义接口2.无头单链表实现接口2.1 头插addFirst2.2 尾插add2.3 删除元素remove2.4 修改元素set2.5 获取元素get 3.带头单链表实现接口3.1 头插addFirst3.2 尾插add3.3 删除元素remove3.4 判断是否包含元素element 1.定义接口 public interface SeqList<E>{//默认…...

Vue2 第二十节 vue-router (四)
1.全局前置路由和后置路由 2.独享路由守卫 3.组件内路由守卫 4.路由器的两种工作模式 路由 作用:对路由进行权限控制 分类:全局守卫,独享守卫,组件内守卫 一.全局前置路由和后置路由 ① 前置路由守卫:每次路由…...

第三章 图论 No.1单源最短路及其综合应用
文章目录 1129. 热浪1128. 信使1127. 香甜的黄油1126. 最小花费920. 最优乘车903. 昂贵的聘礼1135. 新年好340. 通信线路342. 道路与航线341. 最优贸易 做乘法的最短路时,若权值>0,只能用spfa来做,相等于加法中的负权边 1129. 热浪 1129.…...

❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件
❤ npm不是内部或外部命令,也不是可运行的程序 或批处理文件 cmd或者终端用nvm 安装提示: npm不是内部或外部命令,也不是可运行的程序或批处理文件 原因(一) 提示这个问题,有可能是Node没有安装,也有可能是没有配置…...

关于Godot游戏引擎制作流水灯
先上核心代码 游戏节点 流水灯的通途可以是 1. 装饰 2. 音乐类多媒体程序(如FL中TB-303的步进灯) FL Studio Transistor Bass...

C语言 函数指针详解
一、函数指针 1.1、概念 函数指针:首先它是一个指针,一个指向函数的指针,在内存空间中存放的是函数的地址; 示例: int Add(int x,int y) {return xy;} int main() {printf("%p\n",&Add);…...

LNMP及论坛搭建
安装 Nginx 服务 systemctl stop firewalld systemctl disable firewalld setenforce 0 1.安装依赖包 #nginx的配置及运行需要pcre、zlib等软件包的支持,因此需要安装这些软件的开发包,以便提供相应的库和头文件。 yum -y install pcre-devel zlib-devel…...

【使用机器学习和深度学习对城市声音进行分类】基于两种技术(ML和DL)对音频数据(城市声音)进行分类(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

Godot 4 练习 - 制作粒子
演示项目dodge_the_creeps中,有一个Trail,具体运行效果 想要看看咋实现的,看完也不清晰,感觉是要设置某些关键的属性 ChatGPT说:以下是一些重要的属性: texture:用于渲染粒子的纹理。您可以使用…...
Java基础继承详解
Java基础继承详解 在Java中,继承是面向对象编程中的一个重要概念。通过继承,一个类可以从另一个类继承属性和方法,使代码重用和扩展更加方便。下面是关于Java基础继承的一些详解: 关键字: 使用extends关键字可以在一个…...

如何维护你的电脑:打造IT人的重要武器
文章目录 方向一:介绍我的电脑方向二:介绍我的日常维护措施1. 定期清理和优化2. 保持良好的上网习惯和安全防护3. 合理安排软件和硬件的使用4. 数据备份和系统还原 方向三:推荐的维护技巧1. 数据分区和多系统安装2. 内部清洁和散热优化3. 安全…...

【雕爷学编程】MicroPython动手做(31)——物联网之Easy IoT 3
1、物联网的诞生 美国计算机巨头微软(Microsoft)创办人、世界首富比尔盖茨,在1995年出版的《未来之路》一书中,提及“物物互联”。1998年麻省理工学院提出,当时被称作EPC系统的物联网构想。2005年11月,国际电信联盟发布《ITU互联网…...
Elasticsearch 快照和恢复
文章目录 简介快照存储库说明创建或更新存储库接口说明路径参数查询参数请求正文 使用 fs 方式创建存储库验证储存库获取存储库信息删除存储库清理储存库 快照创建快照路径参数查询参数请求正文示例 获取快照查询参数示例 克隆快照查询参数示例 获取快照状态示例 恢复快照查询参…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...