《深度学习》【项目】OpenCV 答题卡识别 项目流程详解

目录

一、项目上半部分

1、定义展示图像函数

2、预处理

运行结果:

3、轮廓检测并绘制

运行结果:

4、排序轮廓

5、定义排序点函数

6、透视变换

1)定义透视变换处理函数

2)执行透视变换

运行结果:

7、二值化处理

运行结果:

8、绘制圆圈的轮廓

运行结果:

二、项目下半部分

1、筛选选项的圆圈并排序

2、标记正误

运行结果:

3、打印正确率并批注

运行结果:

三、完整代码


一、项目上半部分

1、定义展示图像函数

def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)

2、预处理

# 预处理
image = cv2.imread(r'./images/test_01.png')   # 读取待识别答题卡图片
contours_img = image.copy()   # 生成图片副本
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度图
blurred = cv2.GaussianBlur(gray,(5,5),0)  # 使用高斯滤波对灰度图进行处理,减小图像噪声和细节,高斯核大小为5*5,0表示自动计算标准差
cv_show('blurred',blurred)   # 展示
edged = cv2.Canny(blurred,75,200)  # 对处理完的图像进行边缘检测,灰度低于75的将其更改为0,高于200的设置为255
cv_show('edged',edged)
        运行结果:

3、轮廓检测并绘制

cnts = cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[1]   # 对副本图像进行轮廓检测,cv2.RETR_EXTERNAL只检测最外层,cv2.CHAIN_APPROX_SIMPLE只保留轮廓端点
# cv2.findContours返回3个值,原图、轮廓信息、层次结构,其受OpenCV版本影响
cv2.drawContours(contours_img,cnts,-1,(0,0,255),4)  # 在原图的副本上绘制轮廓cnts,-1表示绘制所有轮廓
cv_show('contours',contours_img)  # 展示
        运行结果:

4、排序轮廓

docCnt = None
cnts = sorted(cnts,key = cv2.contourArea,reverse=True)  # 对检测到的轮廓进行排序,排序方式为轮廓面积,降序形式
for c in cnts:  # 遍历排序后的每一个轮廓peri = cv2.arcLength(c,True)   # 计算每一个轮廓的周长,True表示闭合approx = cv2.approxPolyDP(c,0.002*peri,True)  # 多边形逼近,逼近的精度为周长的0.2%,返回逼近后的多边形轮廓if len(approx)==4:  # 判断多边形是否是4边的docCnt = approx  # 如果是将其传入docCnt,然后中断循环,因为精度很高,所以认定这个多边形为检测到的轮廓外接多边形break

5、定义排序点函数

        输入轮廓外接四边形的四个顶点,对其进行排序,返回排序后的点坐标

def order_points(pts):   # 对输入的四个点按照左上、右上、右下、左下进行排序rect = np.zeros((4,2),dtype='float32')   # 创建一个4*2的数组,用来存储排序之后的坐标位置# 按顺序找到对应坐标0123分别是左上、右上、右下、左下s = pts.sum(axis=1)   # 对pts矩阵的每个点的x y相加rect[0] = pts[np.argmin(s)]    # np.argmin(s)表示数组s中最小值的索引,表示左上的点的坐标rect[2] = pts[np.argmax(s)]    # 返回最大值索引,即右下角的点坐标diff = np.diff(pts,axis=1)   # 对pts矩阵的每一行的点求差值rect[1] = pts[np.argmin(diff)]   # 差值最小的点为右上角点rect[3] = pts[np.argmax(diff)]   # 差值最大表示左下角点return rect   # 返回排序好的四个点的坐标

6、透视变换

        1)定义透视变换处理函数
def four_point_transform(image,pts):  # 对图像进行透视变换,pts为轮廓外界四边形端点# 获取输入坐标点rect = order_points(pts)  # 为上述排序的四个点(tl,tr,br,bl) = rect   # 分别返回给四个值,分别表示为左上、右上、右下、左下# 计算四边形的宽高widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))   # 计算四边形底边的宽度widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))   # 计算顶边的宽度maxWidth = max(int(widthA), int(widthB))   # 返回最大宽度heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))   # 计算左上角到右下角的对角线长度heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))   # 计算右上角到左下角的高的长度maxHeight = max(int(heightA),int(heightB))   # 返回最长的高度# 变换后对应坐标位置dst = np.array([[0,0],   # 定义四个点,表示变换后的矩阵的角点[maxWidth-1,0],[maxWidth-1,maxHeight-1],[0,maxHeight-1]],dtype='float32')# 图像透视变换 cv2.getPerspectiveTransform(src,dst[,solveMethod])→ M获得转换之间的关系# cv2.warpPerspective(src, Mp, dsizel, dstl, flagsl, borderModel, borderValue]]1])- dst# #参数说明:# src:变换前图像四边形顶点坐标/第2个是原图# MP:透视变换矩阵,3行3列# dsize:输出图像的大小,二元元组(width,heiqht)M = cv2.getPerspectiveTransform(rect,dst)  # 根据原始点和变换后的点计算透视变换矩阵 Mwarped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))  # 对原始图像,针推变换矩阵和输出图像大小进行透视变换,返回变换后的图片# 返回变换后的结果return warped
        2)执行透视变换
warped_t = four_point_transform(image,docCnt.reshape(4,2))  # 传入原图,docCnt.reshape(4,2)表示将轮廓的4个点排成一列,进行透视变换,返回透视变换后的图片
warped_new = warped_t.copy()
cv_show('wraped',warped_t)   # 展示透视变换后的图片
                运行结果:

7、二值化处理

warped = cv2.cvtColor(warped_t,cv2.COLOR_BGR2GRAY)  # 将经过透视变换的图像转换为灰度图
# 阈值处理
thresh = cv2.threshold(warped,0,255,cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1]  # 对灰度图做二值化,THRESH_BINARY_INV反二值化处理,较亮的区域变为黑,较暗的区域变为白,THRESH_OTSU自动计算阈值,返回两个值,一个是阈值,一个是处理后的图像,
cv_show('thresh',thresh)  # 展示二值化处理后的图像
        运行结果:

8、绘制圆圈的轮廓

thresh_Contours = thresh.copy()
# 找到每一个圆圈轮廓
cnts = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[1]   # 对二值化处理后的图像进行轮廓检测,检测最外层轮廓,只返回轮廓端点信息,返回值有三个见上文
warped_Contours = cv2.drawContours(warped_t,cnts,-1,(0,255,0),3)  # 绘制轮廓
cv_show('wraped_Contours',warped_Contours)
        运行结果:

二、项目下半部分

1、筛选选项的圆圈并排序

questionCnts=[]
for c in cnts:  # 遍历绘制的每一个轮廓(x,y,w,h) = cv2.boundingRect(c)   # 计算轮廓矩形,返回边界矩形的坐标x,y,宽高ar = w / float(h)   # 计算宽高比# 根据实际情况指定标准if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:  # 筛选矩形轮廓的宽、高、宽高比questionCnts.append(c)   # 将满足条件的轮廓信息增加到列表
# 将圆圈轮廓按照从上到下进行排序
questionCnts = sort_contours(questionCnts,method='top-to-bottom')[0]

2、标记正误

for (q,i) in enumerate(np.arange(0,len(questionCnts),5)):   # 生成一个0-25不包括25,步长为5的数组,即0,4,8,16,24五个值,enumerate生成可迭代对象,返回索引和值cnts = sort_contours(questionCnts[i:i+5])[0]  # 遍历每五个轮廓传入函数进行从左往右排序,索引0返回排序后的端点信息bubbled = None# 遍历每一个结果for (j,c) in enumerate(cnts):  # 遍历排序后的一行轮廓圆的索引和值# 创建掩膜maskmask = np.zeros(thresh.shape,dtype='uint8')   # 创建和thresh一样布局的0矩阵,thresh是二值化处理完的图像cv2.drawContours(mask,[c],-1,255,-1)   # 绘制掩膜,[c]为单个轮廓的列表,第一个-1表示绘制轮廓列表中的所有轮廓,轮廓为单通道,所以255表示白色,第二个-1表示全部填充cv_show('mask',mask)   # 绘制掩膜# 通过计算非零点数量来算是否选择这个答案# 利用掩膜(mask)进行“与”操作,只保留mask位置中的内容thresh_mask_and = cv2.bitwise_and(thresh,thresh,mask=mask)  # 按位与操作,按位与两张图片相同的地方,这里是thresh图像本身,返回结果为掩膜中为白色的区域cv_show('thresh_mask_and',thresh_mask_and)   # 展示保留掩膜白色区域的thresh图total = cv2.countNonZero(thresh_mask_and)   # 计算非0像素的数量,因为遍历的是一排选项轮廓的每一个轮廓,所以计算出非0像素值数量后再判断对应的选项if bubbled is None or total > bubbled[0]:bubbled = (total,j)  # 将轮廓的值和索引以元组的形式保存的bubbled,通过遍历不停更新筛选# 对比正确答案color = (0,0,255)   # 初始化颜色为红色k = ANSWER_KEY[q]   # q为上述遍历出来的每一个选项的索引,筛选出来对应答案kif k == bubbled[1]:  # 判断正确color = (0,255,0)   # 如果正确则为绿色correct += 1   # 正确就+1,总数为5,最后求正确率cv2.drawContours(warped_new,[cnts[k]],-1,color,3)   # 在warped_new为原图副本上绘制轮廓,[cnts[k]]为轮廓列表,-1表示绘制所有轮廓cv_show('warpeding',warped_new)
        运行结果:

3、打印正确率并批注

# 此处代码无缩进
score = (correct/5.0)*100   # 计算正确率百分比
print("[INFO] score :{:.2f}%".format(score))   # 输出正确率
cv2.putText(warped_new,'{:.2f}%'.format(score),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.9,(0,0,255),2)   # 在原图副本上绘制文本,文本内容为正确率百分比,绘制的位置坐标为(10,30),FONT_HERSHEY_SIMPLEX为文本字体类型,0.9为缩放因子
cv2.imshow('Original',image)   # 展示原图
cv2.imshow('Exam',warped_new)   # 展示绘制完的图
cv2.waitKey(0)
        运行结果:

三、完整代码

import numpy as np
import cv2ANSWER_KEY = {0:1,1:4,2:0,3:3,4:1}   # 定义选项的正确答案,键0表示选项A,1表示B、、、Edef cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)
def order_points(pts):   # 对输入的四个点按照左上、右上、右下、左下进行排序rect = np.zeros((4,2),dtype='float32')   # 创建一个4*2的数组,用来存储排序之后的坐标位置# 按顺序找到对应坐标0123分别是左上、右上、右下、左下s = pts.sum(axis=1)   # 对pts矩阵的每个点的x y相加rect[0] = pts[np.argmin(s)]    # np.argmin(s)表示数组s中最小值的索引,表示左上的点的坐标rect[2] = pts[np.argmax(s)]    # 返回最大值索引,即右下角的点坐标diff = np.diff(pts,axis=1)   # 对pts矩阵的每一行的点求差值rect[1] = pts[np.argmin(diff)]   # 差值最小的点为右上角点rect[3] = pts[np.argmax(diff)]   # 差值最大表示左下角点return rect   # 返回排序好的四个点的坐标# 将透视扭曲的矩形变换成一个规则的矩阵
def four_point_transform(image,pts):# 获取输入坐标点rect = order_points(pts)  # 为上述排序的四个点(tl,tr,br,bl) = rect   # 分别返回给四个值,分别表示为左上、右上、右下、左下# 计算四边形的宽高widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))   # 计算四边形底边的宽度widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))   # 计算顶边的宽度maxWidth = max(int(widthA), int(widthB))   # 返回最大宽度heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))   # 计算左上角到右下角的对角线长度heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))   # 计算右上角到左下角的高的长度maxHeight = max(int(heightA),int(heightB))   # 返回最长的高度# 变换后对应坐标位置dst = np.array([[0,0],   # 定义四个点,表示变换后的矩阵的角点[maxWidth-1,0],[maxWidth-1,maxHeight-1],[0,maxHeight-1]],dtype='float32')# 图像透视变换 cv2.getPerspectiveTransform(src,dst[,solveMethod])→ M获得转换之间的关系# cv2.warpPerspective(src, Mp, dsizel, dstl, flagsl, borderModel, borderValue]]1])- dst# #参数说明:# src:变换前图像四边形顶点坐标/第2个是原图# MP:透视变换矩阵,3行3列# dsize:输出图像的大小,二元元组(width,heiqht)M = cv2.getPerspectiveTransform(rect,dst)  # 根据原始点和变换后的点计算透视变换矩阵 Mwarped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))  # 对原始图像,针推变换矩阵和输出图像大小进行透视变换,返回变换后的图片# 返回变换后的结果return warpeddef 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]  # 遍历每一个轮廓,计算轮廓矩形,将轮廓矩形的坐标、宽高存入列表(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),    # 排序轮廓再打包key=lambda b: b[1][i],reverse=reverse))return cnts, boundingBoxes# 预处理
image = cv2.imread(r'./images/test_01.png')   # 读取待识别答题卡图片
contours_img = image.copy()   # 生成图片副本
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度图
blurred = cv2.GaussianBlur(gray,(5,5),0)  # 使用高斯滤波对灰度图进行处理,减小图像噪声和细节,高斯核大小为5*5,0表示自动计算标准差
cv_show('blurred',blurred)   # 展示
edged = cv2.Canny(blurred,75,200)  # 对处理完的图像进行边缘检测,灰度低于75的将其更改为0,高于200的设置为255
cv_show('edged',edged)# 轮廓检剽
cnts = cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[1]   # 对副本图像进行轮廓检测,cv2.RETR_EXTERNAL只检测最外层,cv2.CHAIN_APPROX_SIMPLE只保留轮廓端点
# cv2.findContours返回3个值,原图、轮廓信息、层次结构,其受OpenCV版本影响
cv2.drawContours(contours_img,cnts,-1,(0,0,255),4)  # 在原图的副本上绘制轮廓cnts,-1表示绘制所有轮廓
cv_show('contours',contours_img)  # 展示docCnt = None
cnts = sorted(cnts,key = cv2.contourArea,reverse=True)  # 对检测到的轮廓进行排序,排序方式为轮廓面积,降序形式
for c in cnts:  # 遍历排序后的每一个轮廓peri = cv2.arcLength(c,True)   # 计算每一个轮廓的周长,True表示闭合approx = cv2.approxPolyDP(c,0.002*peri,True)  # 多边形逼近,逼近的精度为周长的0.2%,返回逼近后的多边形轮廓if len(approx)==4:  # 判断多边形是否是4边的docCnt = approx  # 如果是将其传入docCnt,然后中断循环,因为精度很高,所以认定这个多边形为检测到的轮廓外接多边形break# 执行透视变換
warped_t = four_point_transform(image,docCnt.reshape(4,2))  # 传入原图,docCnt.reshape(4,2)表示将轮廓的4个点排成一列,进行透视变换,返回透视变换后的图片
warped_new = warped_t.copy()
cv_show('wraped',warped_t)   # 展示透视变换后的图片warped = cv2.cvtColor(warped_t,cv2.COLOR_BGR2GRAY)  # 将经过透视变换的图像转换为灰度图
# 阈值处理
thresh = cv2.threshold(warped,0,255,cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1]  # 对灰度图做二值化,THRESH_BINARY_INV反二值化处理,较亮的区域变为黑,较暗的区域变为白,THRESH_OTSU自动计算阈值,返回两个值,一个是阈值,一个是处理后的图像,
cv_show('thresh',thresh)  # 展示二值化处理后的图像thresh_Contours = thresh.copy()
# 找到每一个圆圈轮廓
cnts = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[1]   # 对二值化处理后的图像进行轮廓检测,检测最外层轮廓,只返回轮廓端点信息,返回值有三个见上文
warped_Contours = cv2.drawContours(warped_t,cnts,-1,(0,255,0),3)  # 绘制轮廓
cv_show('wraped_Contours',warped_Contours)questionCnts=[]
for c in cnts:  # 遍历绘制的每一个轮廓(x,y,w,h) = cv2.boundingRect(c)   # 计算轮廓矩形,返回边界矩形的坐标x,y,宽高ar = w / float(h)   # 计算宽高比# 根据实际情况指定标准if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:  # 筛选矩形轮廓的宽、高、宽高比questionCnts.append(c)   # 将满足条件的轮廓信息增加到列表
# 将圆圈轮廓按照从上到下进行排序
questionCnts = sort_contours(questionCnts,method='top-to-bottom')[0]
correct = 0
# 每排有5个选项
for (q,i) in enumerate(np.arange(0,len(questionCnts),5)):   # 生成一个0-25不包括25,步长为5的数组,即0,4,8,16,24五个值,enumerate生成可迭代对象,返回索引和值cnts = sort_contours(questionCnts[i:i+5])[0]  # 遍历每五个轮廓传入函数进行从左往右排序,索引0返回排序后的端点信息bubbled = None# 遍历每一个结果for (j,c) in enumerate(cnts):  # 遍历排序后的一行轮廓圆的索引和值# 创建掩膜maskmask = np.zeros(thresh.shape,dtype='uint8')   # 创建和thresh一样布局的0矩阵,thresh是二值化处理完的图像cv2.drawContours(mask,[c],-1,255,-1)   # 绘制掩膜,[c]为单个轮廓的列表,第一个-1表示绘制轮廓列表中的所有轮廓,轮廓为单通道,所以255表示白色,第二个-1表示全部填充cv_show('mask',mask)   # 绘制掩膜# 通过计算非零点数量来算是否选择这个答案# 利用掩膜(mask)进行“与”操作,只保留mask位置中的内容thresh_mask_and = cv2.bitwise_and(thresh,thresh,mask=mask)  # 按位与操作,按位与两张图片相同的地方,这里是thresh图像本身,返回结果为掩膜中为白色的区域cv_show('thresh_mask_and',thresh_mask_and)   # 展示保留掩膜白色区域的thresh图total = cv2.countNonZero(thresh_mask_and)   # 计算非0像素的数量,因为遍历的是一排选项轮廓的每一个轮廓,所以计算出非0像素值数量后再判断对应的选项if bubbled is None or total > bubbled[0]:bubbled = (total,j)  # 将轮廓的值和索引以元组的形式保存的bubbled,通过遍历不停更新筛选# 对比正确答案color = (0,0,255)   # 初始化颜色为红色k = ANSWER_KEY[q]   # q为上述遍历出来的每一个选项的索引,筛选出来对应答案kif k == bubbled[1]:  # 判断正确color = (0,255,0)   # 如果正确则为绿色correct += 1   # 正确就+1,总数为5,最后求正确率cv2.drawContours(warped_new,[cnts[k]],-1,color,3)   # 在warped_new为原图副本上绘制轮廓,[cnts[k]]为轮廓列表,-1表示绘制所有轮廓cv_show('warpeding',warped_new)score = (correct/5.0)*100   # 计算正确率百分比
print("[INFO] score :{:.2f}%".format(score))   # 输出正确率
cv2.putText(warped_new,'{:.2f}%'.format(score),(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.9,(0,0,255),2)   # 在原图副本上绘制文本,文本内容为正确率百分比,绘制的位置坐标为(10,30),FONT_HERSHEY_SIMPLEX为文本字体类型,0.9为缩放因子
cv2.imshow('Original',image)   # 展示原图
cv2.imshow('Exam',warped_new)   # 展示绘制完的图
cv2.waitKey(0)

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

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

相关文章

信息安全工程师(28)机房安全分析与防护

前言 机房安全分析与防护是一个复杂而细致的过程&#xff0c;涉及到物理安全、环境控制、电力供应、数据安全、设备管理、人员管理以及紧急预案等多个方面。 一、机房安全分析 1. 物理安全威胁 非法入侵&#xff1a;未经授权的人员可能通过门窗、通风口等进入机房&#xff0c;…

解决无法安装“vue.volar“扩展,跟vscode版本不兼容问题

问题&#xff1a;安装volar插件的时候提示跟vscode版本不兼容 解决方案 1、进入VSCode插件市场&#xff0c;搜索Vue.volar&#xff08;直达链接&#xff1a;volar下载界面&#xff09; 2、点击download Extension&#xff08;下载插件&#xff09; 3、下载.vsix文件完成后&a…

基于Opencv中的DNN模块实现图像/视频的风格迁移

一、DNN模块的介绍 1、简介 OpenCV中的DNN&#xff08;Deep Neural Network&#xff09;模块是一个功能强大的组件&#xff0c;它支持深度学习网络模型的加载和推理。虽然DNN模块不提供模型的训练功能&#xff0c;但它可以与主流的深度学习框架&#xff08;如TensorFlow、Caf…

Rope – 基于深度学习模型开源的AI换脸技术

Rope是什么 Rope是一款开源的AI换脸工具&#xff0c;基于insightface的inswapper_128模型构建&#xff0c;提供一个用户友好的图形界面。用户通过上传图片或视频&#xff0c;在几秒钟内完成换脸操作&#xff0c;效果逼真。Rope支持多种超分辨率算法&#xff0c;支持用户调整面…

深入探讨B+树索引的基本概念、工作原理以及在MySQL中的应用

文章目录 1. B树的基本概念2. B树在MySQL中的实现3. 示例代码4. 结论 在数据库管理系统中&#xff0c;索引是一种特殊的文件&#xff0c;它能够提高数据检索的速度。MySQL作为最流行的开源关系型数据库之一&#xff0c;提供了多种索引类型来满足不同的性能需求。其中&#xff0…

ARP断网攻击

ARP断网攻击 1.课前准备 kali 作为ARP攻击机&#xff0c;192.168.110.26 MAC地址&#xff1a;00:0c:29:fc:66:46 win10 作为被攻击方&#xff0c;192.168.110.12 MAC地址&#xff1a;1c:69:7a:a4:cf:92 网关&#xff08;路由器&#xff09;&#xff0c;192.168.110.1 MAC地…

[单master节点k8s部署]34.ingress 反向代理(一)

ingress是k8s中的标准API资源&#xff0c;作用是定义外部流量如何进入集群&#xff0c;并根据核心路由规则将流量转发到集群内的服务。 ingress和Istio工作栈中的virtual service都是基于service之上&#xff0c;更细致准确的一种流量规则。每一个pod对应的service是四层代理&…

ESP32接入扣子(Coze) API使用自定义智能体

使用ESP32接入Coze API实现聊天机器人的教程 本示例将使用ESP32开发板通过WiFi接入 Coze API&#xff0c;实现一个简单的聊天机器人功能。用户可以通过串口向机器人输入问题&#xff0c;ESP32将通过Coze API与智能体进行通信&#xff0c;并返回对应的回复。本文将详细介绍了如…

PyCharm打开及配置现有工程(详细图解)

本文详细介绍了如何利用Pycharm打开一个现有的工程&#xff0c;其中包括编译器的配置。 PyCharm打开及配置现有工程 1、打开工程2、配置编译器 1、打开工程 双击PyCharm软件&#xff0c;点击左上角 文件 >> 打开(O)… 选中想要打开的项目之后点击“确定” 2、配置编译器…

[Algorithm][贪心][可被三整除的最大和][距离相等的条形码][重构字符串]详细讲解

目录 1.可被三整除的最大和1.题目链接2.算法原理详解3.代码实现 2.距离相等的条形码1.题目链接2.算法原理详解3.代码实现 3.重构字符串1.题目链接2.算法原理详解3.代码实现 1.可被三整除的最大和 1.题目链接 可被三整除的最大和 2.算法原理详解 思路&#xff1a;正难则反 贪…

326. 3 的幂

文章目录 326. 3 的幂解题思路Go代码 326. 3 的幂 326. 3 的幂 给定一个整数&#xff0c;写一个函数来判断它是否是 3 的幂次方。如果是&#xff0c;返回true&#xff1b;否则&#xff0c;返回 false 。 整数 n 是 3 的幂次方需满足&#xff1a;存在整数 x 使得 n 3 x n …

Android设置状态栏隐藏、固定颜色

设置隐藏效果&#xff1a; <?xml version"1.0" encoding"utf-8"?> <resources><style name"Theme.XiaoShuang" parent"Theme.AppCompat.Light.NoActionBar"><!--设置沉浸式通知栏--><item name"an…

Nullinux:一款针对Linux操作系统的安全检测工具

关于Nullinux Nullinux是一款针对Linux操作系统的安全检测工具&#xff0c;广大研究人员可以利用该工具针对Linux目标设备执行网络侦查和安全检测。 该工具可以通过SMB枚举目标设备的安全状况信息&#xff0c;其中包括操作系统信息、域信息、共享信息、目录信息和用户信息。如…

292. Nim 游戏

文章目录 292. Nim 游戏解题思路Go代码 292. Nim 游戏 292. Nim 游戏 你和你的朋友&#xff0c;两个人一起玩 Nim 游戏&#xff1a; 桌子上有一堆石头。你们轮流进行自己的回合&#xff0c; 你作为先手 。每一回合&#xff0c;轮到的人拿掉 1 - 3 块石头。拿掉最后一块石头的…

Elasticsearch的安装与配置

注意&#xff1a;elasticsearch 禁止安装在/root路径下&#xff01; 1、创建用户组 groupadd elastic 2、创建用户 useradd es -d /home/es -g elastic echo es | passwd es --stdin 3、给新创建的用户进行授权 chown -R es:elastic /home/es chmod -R 775 /home/es 4…

『网络游戏』游戏数据库管理类查询插入账号存储【23】

新建数据库连接 新建数据库 打开数据库 新建表 账号数据 设计表 - 添加属性 对照服务器工程GameMsg增加对应字段 保存后在服务器脚本中操作数据库数据 添加数据层文件夹 创建脚本&#xff1a;DBMgr 编写脚本&#xff1a;DBMgr.cs 修改脚本&#xff1a;ServerRoot.cs 将MySql.d…

Java值传递、序列化详解

Java 值传递详解 说到参数&#xff0c;我们先来搞懂一下这两个概念 形参&实参 值传递&引用传递 形参&实参 方法的定义可能会用到 参数&#xff08;有参的方法&#xff09;&#xff0c;参数在程序语言中分为&#xff1a; 实参&#xff08;实际参数&#xff0c;…

10.11作业

实现简单数据库功能 &#xff08;增删改查&#xff09; widget.h #ifndef WIDGET_H #define WIDGET_H #include <QSqlDatabase> // 数据库管理类 #include <QWidget> // #include <QSqlQuery> #include <QSqlRecord> //记录类 #include …

若依 从字典类型跳到字典数据跳到了404

描述&#xff1a; 在字典类型从表中字典类型跳转到详情的字典数据时跳到了404 解决过程&#xff1a; 由于我的id统一是用GUID&#xff0c;所以想到了路由表相关路由的正则校验&#xff0c;若依是int类型&#xff0c;我直接删掉了&#xff0c;改了之后还是跳404 后面想是路由表…

【微服务】网关 - Gateway(下)(day8)

网关过滤工厂 在上一篇文章中&#xff0c;主要是对网关进行了一个总体的介绍&#xff0c;然后对网关中的断言进行了一个描述。在这篇文章中&#xff0c;主要是对网关中的最后一大核心——过滤进行介绍。 当客户端发送过来的请求经过断言之后&#xff0c;如果还想在请求前后添…