一、信用卡卡号识别

一、思路分析

大体思路:首先拿到一张银行卡,我们得有银行卡号数字的0-9样式的模板,然后再通过不同数字的轮廓的外接矩形来进行匹配,最终识别出银行卡号所对应的数字。
银行卡数字模板:
在这里插入图片描述
银行卡信息:
在这里插入图片描述

拿到银行卡的时候,因为银行卡上面不仅仅只是银行卡号,还会存在一些干扰项,这时候需要对这些干扰项进行过滤,这些干扰项和数字所在的外接轮廓大小是不相同的,故可以以此进行过滤筛选。当然,一下次不会将单个数字给定位到,拿到的也不是单个数字,绝大多数的银行卡号都是四个为一组,接下来再对每一组中的四个数字进行拆分。

二、导包以及相关函数

from imutils import contours
import numpy as np
import argparse
import cv2
# 绘图展示
def cv_show(name,img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()
#因为银行卡分为四个大轮廓,每个大轮廓中又包括四个数字,这里需要定义一个函数用于将识别出的四个大轮廓依次排序存放
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#返回轮廓以及轮廓信息一个列表
#因为银行卡数字这块图像会较小,需要重新指定一下图像大小
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resized

三、获取模板上的数字信息

指定测试图像以及模板图像位置

# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,help="path to input image")#指定输入图像
ap.add_argument("-t", "--template", required=True,help="path to template OCR-A image")#指定模板图像
args = vars(ap.parse_args())

设置银行卡类型参数

# 指定信用卡类型
FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"
}

首先处理模板图像,模板图像存放这0-9数字,读取模板图像,灰度图、二值化

# 读取一个模板图像
img = cv2.imread(args["template"])
cv_show('img',img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)

对二值化之后的模板图像获取轮廓

# 计算轮廓
#cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

用红色线进行绘制模板轮廓,定义一个空字典digits,用于存储后续得到模板图像轮廓信息

cv2.drawContours(img,refCnts,-1,(0,0,255),3) #用红色线画轮廓
cv_show('img',img)
print (np.array(refCnts).shape)#出现10个轮廓,0-9数字所对应的图像信息
refCnts = sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下  得到模板排序完之后的轮廓
digits = {}#指定一个空字典

对模板图像中0-9个轮廓依次去遍历,得到信息存储到digits字典中

# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):#在模板轮廓中不断的去遍历,i为轮廓的索引,c为轮廓# 计算外接矩形并且resize成合适大小(x, y, w, h) = cv2.boundingRect(c)#得到第i个轮廓的外接矩形信息roi = ref[y:y + h, x:x + w]#用外接矩形对模板图像相同位置抠出来roi = cv2.resize(roi, (57, 88))#抠出来的模板图像的大小可能会较小,把图像给resize成一个差不多大小的图像# 每一个数字对应每一个模板digits[i] = roi#把从模板中抠出来的数字存入到字典中,模板为value,索引为key
#循环结束之后,digits字典中就存放有了0-9数字的模板图像信息

三、对测试图像进行形态学操作,消除其他的干扰因素

根据不同情景选取合适的卷积核

# 初始化卷积核 这里主要是根据具体情况具体分析  卷积核的定义需要看具体情况而定
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 = resize(image, width=300)#重新resize一下图像大小
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)#因为传入的测试图像是彩色图,需要转换为灰度图
cv_show('gray',gray)#显示一下灰度图

将得到的灰度图图像通过礼帽操作,使得数字特征信息更加明显

#礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
cv_show('tophat',tophat) 
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)#ksize=-1相当于用3*3的
gradX = np.absolute(gradX)
(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)

将数字部分进行融合到一块,分成四个大轮廓

#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 
cv_show('gradX',gradX)
#THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 
cv_show('thresh',thresh)#因为实际效果并不好,没有完全融合到一块,故再来一个闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作
cv_show('thresh',thresh)

绘制测试图像中的轮廓信息

# 计算轮廓
thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,	cv2.CHAIN_APPROX_SIMPLE)cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) #将处理之后的图像轮廓绘制到原始图像中去
cv_show('img',cur_img)
locs = []#因为对测试图像轮廓检测会得到很多个轮廓,其中只有数字部分轮廓是我们需要的,这里定义一个列表,用于存储我们需要的数字轮廓

四、开始将测试图像与模板图像数字轮廓依次进行比较

因为测试图像中的轮廓会有好多,我们只需要数字轮廓部分,故图像轮廓的宽高比进行筛选

# 遍历轮廓
for (i, c) in enumerate(cnts):# 计算矩形(x, y, w, h) = cv2.boundingRect(c)#绘制外接矩形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))#将符合我们所设定的轮廓进行存储,这些都是四个数字为一组,一共有四组轮廓信息

将筛选之后得出的轮廓进行排序存放

# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x:x[0])#对获取的大轮廓从左到右排序
output = []

遍历每个轮廓中的数字信息与模板中的数字信息比较评分

# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):# initialize the list of group digitsgroupOutput = []# 根据坐标提取每一个组group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]#为了使得效果处理较好,将拿到的大轮廓数据往上往下往左往右都扩大点cv_show('group',group)#显示第i个大轮廓# 预处理group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#对第i个大轮廓二值化,为了找大轮廓中的四个数字轮廓cv_show('group',group)# 计算每一组的轮廓group_,digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#对第i个大轮廓依次进行轮廓检测digitCnts = contours.sort_contours(digitCnts,method="left-to-right")[0]#对检测出来的小轮廓从左到右排序# 计算每一组中的每一个数值for c in digitCnts:#每个大轮廓都有四个数字,对每一个大轮廓中的每一个小数字分别进行计算,此时c有4个值,因为测试图像中有每个大轮廓有四个数字# 找到当前数值的轮廓,resize成合适的的大小(x, y, w, h) = cv2.boundingRect(c)#对大轮廓找取外接轮廓roi = group[y:y + h, x:x + w]#找到每个数字的轮廓信息roi = cv2.resize(roi, (57, 88))#这个resize的参数要与上列的模板设定的大小一致,到时候需要做模板匹配大小应该一致cv_show('roi',roi)#依次展示第i个大轮廓中的四个数字# 计算匹配得分scores = []#得到数字轮廓信息,要对模板进行比较,存储得分信息# 在模板中计算每一个得分for (digit, digitROI) in digits.items():#将这个数字模板依次与标准模板中的十个数进行匹配# 模板匹配result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)(_, score, _, _) = cv2.minMaxLoc(result)scores.append(score)#依次将得分信息存入# 得到最合适的数字groupOutput.append(str(np.argmax(scores)))#选出得分最高的那个数即可# 画出来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)#得到第c组大轮廓的四个数

五、输出展示

# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

六、Pycharm参数设定方法

在这里插入图片描述
找到Edit Configurations
在这里插入图片描述
--image images/credit_card_04.png --template ocr_a_reference.png
其中:images/credit_card_04.png为测试图片的位置、ocr_a_reference.png为银行卡数字模板图片的位置

七、项目完整代码

# 导入工具包
from imutils import contours
import numpy as np
import argparse
import cv2
#import myutils# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,help="path to input image")#指定输入图像
ap.add_argument("-t", "--template", required=True,help="path to template OCR-A image")#指定模板图像
args = vars(ap.parse_args())# 指定信用卡类型
FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"
}
# 绘图展示
def cv_show(name,img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()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#返回轮廓以及轮廓信息一个列表def resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resized# 读取一个模板图像
img = cv2.imread(args["template"])
cv_show('img',img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)# 计算轮廓
#cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img,refCnts,-1,(0,0,255),3) #用红色线画轮廓
cv_show('img',img)
print (np.array(refCnts).shape)#出现十个轮廓
refCnts = sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下  得到模板排序完之后的轮廓
digits = {}#指定一个空字典# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):#在模板轮廓中不断的去遍历,i为轮廓的索引,c为轮廓# 计算外接矩形并且resize成合适大小(x, y, w, h) = cv2.boundingRect(c)#得到第i个轮廓的外接矩形信息roi = ref[y:y + h, x:x + w]#用外接矩形对模板图像相同位置抠出来roi = cv2.resize(roi, (57, 88))#抠出来的模板图像的大小可能会较小,把图像给resize成一个差不多大小的图像# 每一个数字对应每一个模板digits[i] = roi#把从模板中抠出来的数字存入到字典中,模板为value,索引为key
#循环结束之后,digits字典中就存放有了0-9数字的模板图像信息# 初始化卷积核
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 = resize(image, width=300)#重新resize一下图像大小
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)#因为传入的测试图像是彩色图,需要转换为灰度图
cv_show('gray',gray)#显示一下灰度图#礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
cv_show('tophat',tophat) 
# 
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)#ksize=-1相当于用3*3的gradX = np.absolute(gradX)
(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)#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 
cv_show('gradX',gradX)
#THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 
cv_show('thresh',thresh)#再来一个闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作
cv_show('thresh',thresh)# 计算轮廓
thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,	cv2.CHAIN_APPROX_SIMPLE)cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) #将处理之后的图像轮廓绘制到原始图像中去
cv_show('img',cur_img)
locs = []#因为对测试图像轮廓检测会得到很多个轮廓,其中只有数字部分轮廓是我们需要的,这里定义一个列表,用于存储我们需要的数字轮廓# 遍历轮廓
for (i, c) in enumerate(cnts):# 计算矩形(x, y, w, h) = cv2.boundingRect(c)#绘制外接矩形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))#将符合我们所设定的轮廓进行存储,这些都是四个数字为一组,一共有四组轮廓信息# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x:x[0])#对获取的大轮廓从左到右排序
output = []# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):# initialize the list of group digitsgroupOutput = []# 根据坐标提取每一个组group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]#为了使得效果处理较好,将拿到的大轮廓数据往上往下往左往右都扩大点cv_show('group',group)#显示第i个大轮廓# 预处理group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#对第i个大轮廓二值化,为了找大轮廓中的四个数字轮廓cv_show('group',group)# 计算每一组的轮廓group_,digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#对第i个大轮廓依次进行轮廓检测digitCnts = contours.sort_contours(digitCnts,method="left-to-right")[0]#对检测出来的小轮廓从左到右排序# 计算每一组中的每一个数值for c in digitCnts:#每个大轮廓都有四个数字,对每一个大轮廓中的每一个小数字分别进行计算,此时c有4个值,因为测试图像中有每个大轮廓有四个数字# 找到当前数值的轮廓,resize成合适的的大小(x, y, w, h) = cv2.boundingRect(c)#对大轮廓找取外接轮廓roi = group[y:y + h, x:x + w]#找到每个数字的轮廓信息roi = cv2.resize(roi, (57, 88))#这个resize的参数要与上列的模板设定的大小一致,到时候需要做模板匹配大小应该一致cv_show('roi',roi)#依次展示第i个大轮廓中的四个数字# 计算匹配得分scores = []#得到数字轮廓信息,要对模板进行比较,存储得分信息# 在模板中计算每一个得分for (digit, digitROI) in digits.items():#将这个数字模板依次与标准模板中的十个数进行匹配# 模板匹配result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)(_, score, _, _) = cv2.minMaxLoc(result)scores.append(score)#依次将得分信息存入# 得到最合适的数字groupOutput.append(str(np.argmax(scores)))#选出得分最高的那个数即可# 画出来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)#得到第c组大轮廓的四个数# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

八、测试图片和模板图片

模板图像:
模板图片
测试图像:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

九、运行效果展示

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

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

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

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

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

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

相关文章

bootstrap网格系统_如何使用Bootstrap网格系统?

bootstrap网格系统In the last article, we learned how to create a simple page of Bootstrap? Now, we will learn what is "Grid System" in Bootstrap and how we can use or implement it in our bootstrap page? As you know bootstrap is a mobile-friendl…

有关WriteableBitmap和BitmapImage之间的相互转换

对于WP7中图形处理有关WriteableBitmap和BitmapImage之间的相互转换&#xff0c;给大家几个简单实用的方法。一、WriteableBitmap转为BitmapImage对象var bi new BitmapImage(); bi.SetSource(wb.ToImage().ToStream()); //其中wb是WriteableBitmap对象。 二、BitmapImage转为…

回溯法初步

本文为参考公众号所做的笔记。 代码随想录原文 回溯法本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案&#xff0c;所以它并不是一个高效的算法。但是由于有些问题本身能用暴力搜出来就不错了&#xff0c;所以回溯法也有很多的应用。 回溯法解决的问题…

QEMU中smp,socket,cores,threads几个参数的理解

在用QEMU创建KVM guest的时候&#xff0c;为了指定guest cpu资源&#xff0c;用到了-smp, -sockets, -cores, -threads几个参数&#xff0c; #/usr/bin/qemu-system-x86_64 -name pqsfc085 -enable-kvm -m 2048 -smp 2,sockets2,cores1,threads1 \ -boot ordernc,onced \ -hda …

二、文档扫描OCR

一、思路分析 首先&#xff0c;拿到一张文档&#xff0c;我们需要对文档进行预处理操作&#xff0c;再进行轮廓检测&#xff0c;因为就算拿到文档轮廓&#xff0c;但是这些轮廓也有可能是歪歪扭扭的&#xff0c;这时候需要通过一系列的透视变换操作&#xff0c;将文档摆正。通…

ruby hash方法_Ruby中带有示例的Hash.select方法

ruby hash方法哈希选择方法 (Hash.select Method) In this article, we will study about Hash.select Method. The working of this method can be predicted with the help of its name but it is not as simple as it seems. Well, we will understand this method with the…

leetcode 77. 组合 思考分析

目录1、题目2、回溯法思路3、参考其他思路&#xff0c;更深入了解这个问题4、剪枝优化可能需要回顾到的知识文章&#xff1a;1、常用算法总结(穷举法、贪心算法、递归与分治算法、回溯算法、数值概率算法)2、回溯法初步删除vector容器中的对象元素的三种方法:pop_back, erase与…

ASP 调用dll(VB)及封装dll实例

ASP调用dll及封装dll实例&#xff0c;封装为dll可以提供运行效率&#xff0c;加密代码。 打开VB6&#xff0c;新建ActiveX DLL 2、在工程引用中加入Microsoft Active Server Pages Object Library选择 3、填加代码如下Code Start 声明部分 Private MyScriptingContext As Scrip…

三、全景拼接

一、项目所涉及到的一些知识点 Ⅰ&#xff0c;BF(Brute-Force)暴力匹配&#xff1a;把两张图像的特征点全部给算出来&#xff0c;然后使用归一化的欧氏距离比较这两张图像上特征点之间的大小关系&#xff0c;越小越相似。 SIFT算法 import cv2 import numpy as np import ma…

找回自建SVN密码

自建了一个SVN Repo自己用。重装系统之后密码忘了。 经过了漫长的Google过程&#xff0c;才知道Repo中的密码居然是明文保存的。 在yourRepoDir/conf/svnserve.conf下的password-db处设置&#xff0c;通常是yourRepoDir/conf/passwd文件。 打开passwd文件&#xff0c;就是明文保…

ruby hash方法_Ruby中带有示例的Hash.invert方法

ruby hash方法Hash.invert方法 (Hash.invert Method) In this article, we will study about Hash.invert Method. The working of this method can be predicted with the help of its name but it is not as simple as it seems. Well, we will understand this method with …

leetcode 216. 组合总和 III 思考分析

可能需要回顾的文章; leetcode 77. 组合 思考分析 1、题目 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数&#xff0c;并且每种组合中不存在重复的数字。 说明&#xff1a; 所有数字都是正整数。 解集不能包含重复的组合。 2、递归 这一题和之前…

【Unity】Update()和FixedUpdate()

Update()每帧调用&#xff0c;FixedUpdate&#xff08;&#xff09;以指定频率被调用。可以在 Edit -> project settings -> Time -> Fixed Timestep 中设定该频率。转载于:https://www.cnblogs.com/xiayong123/p/3717002.html

约束执行区域(CER)

受约束的执行区域 (CER) 是创作可靠托管代码的机制的一部分。CER 定义一个区域&#xff0c;在该区域中公共语言运行库 (CLR) 会受到约束&#xff0c;不能引发可使区域中的代码无法完全执行的带外异常。在该区域中&#xff0c;用户代码受到约束&#xff0c;不能执行会导致引发带…

python 抓取网页链接_从Python中的网页抓取链接

python 抓取网页链接Prerequisite: 先决条件&#xff1a; Urllib3: It is a powerful, sanity-friendly HTTP client for Python with having many features like thread safety, client-side SSL/TSL verification, connection pooling, file uploading with multipart encod…

四、模拟英语四六级答题卡识别阅卷评分

一、思路分析 首先拿到答题卡照片的时候&#xff0c;需要对照片进行一系列预处理操作&#xff0c;通过透视变换将图像摆正方便后续的操作。每一道题五个选项&#xff0c;有五道题&#xff0c;通过字典存放准确答案。没有依次对答题卡进行轮廓检测&#xff0c;这里采用的是正方…

leetcode 17. 电话号码的字母组合 思考分析

题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 思考与递归程序 解空间树的宽度是输入数字对应的字符的个数&#xff0c;深度是输入的数字的个数…

Blockquotes,引用,html里面,经常用到的一个!

blockquote元素的使用已经非常多样化&#xff0c;但语义上它只适用于一件事–标记了一段你的网页被引用从另一来源。这意味着&#xff0c;如果你想让那些花俏的引文&#xff0c;<blockquote>是不是你应该使用元素。让我们看一看如何你应该使用此元素&#xff1a; <art…

仔细分析了下这7行,貌似时间复杂度,空间复杂度都不大,为嘛就是执行效率这么低?...

for(Girl girl Girls.first(); !myGirlFriend.like(me); girl Girls.next()){if(!girl.hasBoyFriend(now) && i.like(girl)) { GirlFriend myGirlFriend (GirlFriend)girl; }} 转载于:https://www.cnblogs.com/naran/archive/2011/12/28/2305467.html…

BHMS的完整形式是什么?

BHMS&#xff1a;顺势疗法医学和外科学士 (BHMS: Bachelor of Homeopathic Medicine and Surgery) BHMS is an abbreviation of Bachelor of Homeopathic Medicine and Surgery. It is a medical degree program for under graduation in Homeopathy; an alternative move towa…