首页
统计
关于
Search
1
Sealos3.0离线部署K8s集群
1,272 阅读
2
类的加载
832 阅读
3
Spring Cloud OAuth2.0
827 阅读
4
SpringBoot自动装配原理
735 阅读
5
集合不安全问题
631 阅读
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
Linux
容器
Docker
Containerd
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
登录
Search
标签搜索
Java
CSS
mysql
RabbitMQ
JavaScript
Redis
OpenCV
JVM
Mybatis-Plus
Camunda
多线程
CSS3
Python
Canvas
Spring Cloud
注解和反射
Activiti
工作流
SpringBoot
ndarray
蘇阿細
累计撰写
435
篇文章
累计收到
4
条评论
首页
栏目
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
Linux
容器
Docker
Containerd
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
页面
统计
关于
搜索到
435
篇与
的结果
2025-11-03
十、文档扫描
需扫描的文档scan.pyimport numpy as np import cv2 # 获取四个点 def get_point(pts): rect = np.zeros((4, 2), dtype="float32") # 按顺序计算四个坐标:左上,右上,右下,左下 s = pts.sum(axis=1) # 左上,右下 rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # 右上,左下 diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] return rect def point_transform(image, pts): # 获取输入坐标点 rect = get_point(pts) (tl, tr, br, bl) = rect # 计算输入的w、h 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") # 计算变换矩阵 M = cv2.getPerspectiveTransform(rect, dst) warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight)) return warped 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 image if 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 # 读取文档 image = cv2.imread("images/document_1.jpg") ratio = image.shape[0] / 500.0 image_copy = image.copy() image = resize(image_copy, height=500) # 灰度处理 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 高斯处理 gray = cv2.GaussianBlur(gray, (5, 5), 0) edged = cv2.Canny(gray, 50, 150) # 预处理完的结果 cv2.imshow("Image", image) cv2.imshow("Edged", edged) cv2.waitKey(0) cv2.destroyAllWindows() # 轮廓检测 screenCnt = None cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1] cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5] for c in cnts: # 计算近似轮廓 peri = cv2.arcLength(c, True) # 参数说明:输入的点集、准确度(从原始轮廓到近似轮廓的最大距离)、是否封闭 approx = cv2.approxPolyDP(c, 0.02 * peri, True) if len(approx) == 4: screenCnt = approx break # 绘制轮廓 cv2.drawContours(image, [screenCnt], -1, (2, 255, 0), 2) cv2.imshow("image", image) cv2.waitKey(0) cv2.destroyAllWindows() # 透视变换(二维源图 ---> 三维 ---> 二维) warped = point_transform(image_copy, screenCnt.reshape(4, 2) * ratio) # 二值处理 warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY) ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1] cv2.imwrite("scan.jpg", ref) # 输出结果 cv2.imshow("origin", resize(image_copy, height=500)) cv2.imshow("result", resize(ref, height=500)) cv2.waitKey(0) cv2.destroyAllWindows() 扫描结果流程说明边缘检测获取轮廓变换
2025年11月03日
5 阅读
0 评论
0 点赞
2025-11-02
九、信用卡数字识别
PyCharm env选择 Python3.6, opencv-python 3.4.1.15templatecredit_cardutils.pyimport cv2 def sort_contours(cnts, method="left-to-right"): reverse = False i = 0 if method == "right-to-left" or method == "bottom-to-top": reverse = True if method == "top-to-bottom" or method == "bottom-to-top": i = 1 boundingBoxes = [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): (h, w) = image.shape[:2] ratio = width / float(w) height = int(h * ratio) resize_img = cv2.resize(image, (width, height)) return resize_img ocr_bank_card.pyfrom imutils import contours import numpy as np import cv2 import utils # 信用卡类型 BANK_CARD_TYPE = { "3": "American Express", "4": "Visa", "5": "MasterCard", "6": "Discover Card" } def cv_show(name, img): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows() # 读取模板 template = cv2.imread("images/ocr_template.png") # cv_show("template", template) ref = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) # cv_show("template_gray", ref) ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1] # cv_show("binary", ref) # 计算轮廓 # 参数:二值图,轮廓,要保留的部分 ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(template, refCnts, -1, (0, 0, 255), 3) # cv_show("template", template) print(np.array(refCnts).shape) refCnts = utils.sort_contours(refCnts, method="left-to-right")[0] digits = {} for (i, c) in enumerate(refCnts): # 计算外接矩形并进行resize (x, y, w, h) = cv2.boundingRect(c) roi = ref[y:y + h, x:x + w] roi = cv2.resize(roi, (57, 88)) digits[i] = roi # 初始化卷积核 rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 读取输入图像,预处理 image = cv2.imread("images/credit_card_01.png") # cv_show("image", image) image = utils.resize(image, width=300) 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) 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) # 通过OpenCV THRESH_OTSU 自动寻找合适的阈值 thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 再次执行闭运算 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("cur_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): 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) # 计算每一组的轮廓 group_, digitsCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) digitsCnts = contours.sort_contours(digitsCnts, method="left-to-right")[0] # 计算每一组中的每一个数值 for c in digitsCnts: # 当前数值的轮廓并进行resize (x, y, w, h) = cv2.boundingRect(c) roi = group[y:y + h, x:x + w] 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, _, _) = 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) # 打印结果 print("Bank Card Type:{}".format(BANK_CARD_TYPE[output[0]])) print("Bank Card:{}".format("".join(output))) cv_show("Image", image) 识别结果识别流程说明读取(导入)模板灰度处理二值处理绘制轮廓(外)轮廓排序(分离轮廓并按顺序存储)读取要识别的银行卡将银行卡(源图)resize并灰度处理礼帽操作Sobel算子闭运算(先膨胀,再腐蚀),突出要识别的区域二值处理绘制要识别区域的外轮廓提取数字轮廓绘制外接矩阵二值处理与模板数字一一比对(score)输出结果
2025年11月02日
6 阅读
0 评论
0 点赞
2025-10-29
八、直方图与傅里叶变换
1. 直方图统计每个像素点有多少个cv2.calcHist(images, channels, mask, histSize, ranges)images:原图,格式为uint8或float32,传入函数时须带中括号 [img]channels:统幅图像的直方图,入度图为灰度图时值为:[0],彩色图像时值为0[2]mask:掩模图像,统整幅图像的直方图时值为None,统一部分时使用自定义的掩模图像histSize:BIN的数目,如:[0-10] [11-20] 等等ranges:像素值范围,如:[0, 256]import cv2 # OpenCV 读取的格式是 BGR import matplotlib.pyplot as plt import numpy as np %matplotlib inline # 灰度图 img = cv2.imread('ysg.png', 0) hist = cv2.calcHist([img], [0], None, [256], [0, 256]) hist.shape (256, 1)plt.hist(img.ravel(), 256) plt.show()# 彩色图 img = cv2.imread('ysg.png') color = ('b', 'g', 'r') for i, col in enumerate(color): hist = cv2.calcHist([img], [i], None, [256], [0, 256]) plt.plot(hist, color = col) plt.xlim([0, 256])mask操作img = cv2.imread('ysg.png', 0) # 创建mask mask = np.zeros(img.shape[:2], np.uint8) # 掩模图像区域(需要取的地方设置为白色) mask[100:300, 100:400] = 255 masked_img = cv2.bitwise_and(img, img, mask=mask) hist_full = cv2.calcHist([img], [0], None, [256], [0, 256]) hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256]) plt.subplot(221), plt.imshow(img, 'gray') plt.subplot(222), plt.imshow(mask, 'gray') plt.subplot(223), plt.imshow(masked_img, 'gray') plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0, 256]) plt.show()2. 直方图均衡化img = cv2.imread('ysg.png', 0) plt.hist(img.ravel(), 256) plt.show()plt.imshow(img, 'gray') plt.show()# 均衡化 equ = cv2.equalizeHist(img) plt.hist(equ.ravel(), 256) plt.show()plt.imshow(equ, 'gray') plt.show()# 自适应直方图均衡化 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(1, 1)) img_clahe = clahe.apply(img) plt.imshow(img_clahe, 'gray') plt.show()3. 傅里叶变换(1)傅里叶变换的作用高频:变化剧烈的灰度分量,如:边界低频:变化缓慢的灰度分量,如:大海(2)滤波低通滤波器:只保留低频,会使图像模糊高通滤波器:只保留高频,会使图像的细节增强(3)OpenCV cv2.dft()/cv2.idft()输入的图像需要先转换为 np.float32 格式输出结果中频率为0的部分会在左上角,通常需转换到中心位置(通过 shift 变换实现)cv2.dft()返回结果是双通道的(实部、虚部),需转换为图像格式才能展示(0, 255)img = cv2.imread('ysg.png', 0) img_float32 = np.float32(img) dft = cv2.dft(img_float32, flags=cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) # 转换为灰度图能表示的形式 magnitued_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1])) plt.subplot(121), plt.imshow(img, cmap='gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(magnitued_spectrum, cmap='gray') plt.title('Magnitued Spectrum'), plt.xticks([]), plt.yticks([]) plt.show()# 低通滤波器 img = cv2.imread('ysg.png', 0) img_float32 = np.float32(img) dft = cv2.dft(img_float32, flags=cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) rows, cols = img.shape # 中心位置 crow, ccol = int(rows/2), int(cols/2) mask = np.zeros((rows, cols, 2), np.uint8) mask[crow - 30 : crow + 30, ccol - 30 : ccol + 30] = 1 # IDFT fshift = dft_shift * mask f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1]) plt.subplot(121), plt.imshow(img, cmap='gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img_back, cmap='gray') plt.title('Result'), plt.xticks([]), plt.yticks([]) plt.show()# 高通滤波器 img = cv2.imread('ysg.png', 0) img_float32 = np.float32(img) dft = cv2.dft(img_float32, flags=cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft) rows, cols = img.shape # 中心位置 crow, ccol = int(rows/2), int(cols/2) mask = np.ones((rows, cols, 2), np.uint8) mask[crow - 30 : crow + 30, ccol - 30 : ccol + 30] = 0 # IDFT fshift = dft_shift * mask f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift) img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1]) plt.subplot(121), plt.imshow(img, cmap='gray') plt.title('Input Image'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img_back, cmap='gray') plt.title('Result'), plt.xticks([]), plt.yticks([]) plt.show()
2025年10月29日
8 阅读
0 评论
0 点赞
2025-10-27
七、图像金字塔与轮廓检测
七、图像金字塔与轮廓检测1. 图像轮廓(1)概念cv2.findContours(img, mode, method)mode(轮廓检测模式):RETR_EXTERNAL:只检测最外面的轮廓RETR_LIST:检测所有的轮廓,并将其保存到链表中RETR_CCOMP:检索所有的轮廓,并将他们组织为两层,第一层为各部分的外部边界,第二层为空洞的边界RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次method(轮廓逼近的方法):CHAIN_APPROX_NONE:以 Freeman 链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,即函数只保留他们的终点部分import cv2 # OpenCV 读取的格式是 BGR import matplotlib.pyplot as plt import numpy as np %matplotlib inline img = cv2.imread('ysg.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 使用二值图像提高准确率 ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) plt.imshow(thresh, cmap='gray') plt.show()binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)(2)轮廓绘制img_cp = img.copy() # 参数:需绘制的图像,轮廓(-1为绘制所有轮廓),颜色模式,线条厚度 res = cv2.drawContours(img_cp, contours, -1, (0, 0, 255), 1) plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)) plt.show()(3)轮廓特征cnt = contours[0] # 面积 cv2.contourArea(cnt) 136.0# 周长,True表示闭合 cv2.arcLength(cnt, True) 49.656854152679442. 模板匹配(1)概念模板匹配类似于卷积原理,模板在原图像上从原点开始滑动,计算模板与(图像被覆盖的地方)的差别程度(OpenCV中共6种计算方法),然后将每一次计算结果放入一个矩阵,作为结果输出。6种计算方法:TM_SQDIFF:计算平方不同,计算结果值越小,越相关TM_CCORR:计算相关性,计算结果值越大,越相关TM_CCOEFF:计算相关系数,计算结果值越大,越相关TM_SQDIFF_NORMED:计算归一化平方不同,计算结果值越接近0,越相关TM_CCORR_NORMED:计算归一化相关性,计算结果值越接近1,越相关TM_CCOEFF_NORMED:计算归一化相关系数,计算结果值越接近1,越相关如:原图像大小为 A x B,模板大小为 a x b,则输出结果矩阵是(A - a + 1) x (B - b + 1)img = cv2.imread('ysg.png', 0) template = cv2.imread('ysg_template.png', 0) h, w = template.shape[:2]img.shape (310, 341)template.shape (161, 144)res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF) res.shape (150, 198)min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 最小值 min_val 0.0 # 最大值 max_val 217423616.0 # 最小值位置 min_loc (67, 10) # 最大值位置 max_loc (189, 97)(2)示例methods = ['cv2.TM_SQDIFF', 'cv2.TM_CCORR', 'cv2.TM_CCOEFF', 'cv2.TM_SQDIFF_NORMED', 'cv2.TM_CCORR_NORMED', 'cv2.TM_CCOEFF_NORMED'] for item in methods: img_copy = img.copy() # 匹配方法的真值 method = eval(item) print(method) res = cv2.matchTemplate(img, template, method) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 如果平方差匹配TM_SQDIFF,或归一化平方差匹配TM_SQDIFF_NORMED,取最小值 if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]: top_left = min_loc else: top_left = max_loc bottom_right = (top_left[0] + w, top_left[1] + h) cv2.rectangle(img_copy, top_left, bottom_right, 255, 2) plt.subplot(121), plt.imshow(res, cmap='gray') plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(img_copy, cmap='gray') plt.xticks([]), plt.yticks([]) plt.suptitle(item) plt.show()# 匹配多个对象 match = cv2.imread('ysg_match.png') match_gray = cv2.cvtColor(match, cv2.COLOR_BGR2GRAY) template = cv2.imread('ysg_template.png', 0) h, w = template.shape[:2] res = cv2.matchTemplate(match_gray, template, cv2.TM_CCOEFF_NORMED) threshold = 0.8 loc = np.where(res >= threshold) # 筛选匹配程度大于等于80%的 for pt in zip(*loc[::-1]): bottom_right = (pt[0] + w, pt[1] + h) cv2.rectangle(match, pt, bottom_right, (0, 0, 255), 1) plt.imshow(cv2.cvtColor(match, cv2.COLOR_BGR2RGB)) plt.show()3. 图像金字塔高斯金字塔向下采样(缩小)# 向下采样 img_down = cv2.pyrDown(img) plt.imshow(cv2.cvtColor(img_down, cv2.COLOR_BGR2RGB)) plt.show()img.shape (310, 341, 3) img_down.shape (155, 171, 3)向上采样(放大)# 向上采样 img = cv2.imread('ysg.png') img_up = cv2.pyrUp(img) plt.imshow(cv2.cvtColor(img_up, cv2.COLOR_BGR2RGB)) plt.show()img.shape (310, 341, 3) img_up.shape (620, 682, 3)拉普拉斯金字塔img = cv2.imread('ysg.png') down = cv2.pyrDown(img) down_up = cv2.pyrUp(down) if img.shape != down_up.shape: img = cv2.resize(img, (down_up.shape[1], down_up.shape[0])) res = img - down_up plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)) plt.show()4. 轮廓近似outline = cv2.imread('outline.png') gray = cv2.cvtColor(outline, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = contours[0] outline_cp = outline.copy() res = cv2.drawContours(outline_cp, [cnt], -1, (0, 0, 255), 2) plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)) plt.show()# 值越小,越接近原始轮廓 epsilon = 0.1 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) outline_cp1 = outline.copy() res = cv2.drawContours(outline_cp1, [approx], -1, (0, 0, 255), 2) plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)) plt.show()5. 边界矩形outline = cv2.imread('outline.png') gray = cv2.cvtColor(outline, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = contours[0] x, y, w, h = cv2.boundingRect(cnt) res = cv2.rectangle(outline, (x, y), (x + w, y + h), (0, 0, 255), 2) plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)) plt.show()# 轮廓面积与边界矩形比 area = cv2.contourArea(cnt) x, y, w, h = cv2.boundingRect(cnt) rect_area = w * h extent = float(area) / rect_area print(extent) 0.50601736972704716. 外接圆(x, y), radius = cv2.minEnclosingCircle(cnt) center = (int(x), int(y)) radius = int(radius) res = cv2.circle(res, center, radius, (0, 0, 255), 2) plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)) plt.show()
2025年10月27日
10 阅读
0 评论
0 点赞
2025-10-26
六、Canny 边缘检测
1. 流程(1) 使用高斯滤波器,以平滑图像,滤除噪声(2) 计算图像中每个像素点的梯度强度和方向(3) 应用非极大值(Nono-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应(4) 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘(5) 通过抑制孤立的弱边缘最终完成边缘检测2. 高斯滤波器3. 梯度和方向4. 非极大值抑制线性插值法设g1的梯度幅值M(g1),g2的梯度幅值M(g2),则dtmp1可以得到:M(dtmp1) = w x M(g2) + (1-w) x M(g1)其中w = distance(dtmp1, g2)/distance(g1, g2)distance(g1, g2) 表示两点之间的距离简化为了简化计算,由于一个像素周围有八个像素,可以将一个像素的梯度方向离散为八个方向,如此一来计算前后即可,简化插值法5. 双阈值检测梯度值 > maxVal:则该点处理为边界minVal < 梯度值 < maxVal:与现有的点(A点、C点)有关联(能连线),则保留,否则舍弃(B点)梯度值 < minVal:舍弃6. 演示import cv2 # OpenCV 读取的格式是 BGR import matplotlib.pyplot as plt import numpy as np %matplotlib inline img = cv2.imread('ysg.png', cv2.IMREAD_GRAYSCALE) plt.imshow(img, cmap='gray') plt.show() # 参数说明:源图,最小值,最大值 c1 = cv2.Canny(img, 60, 150) plt.imshow(c1, cmap='gray') plt.show() c2 = cv2.Canny(img, 50, 95) plt.imshow(c2, cmap='gray') plt.show() # 阈值范围越小细节越多
2025年10月26日
7 阅读
0 评论
0 点赞
2025-10-25
五、图像梯度计算
1. Sobel算子共p1,p2,p3,p4,p5,p6,p7,p8,p9 九个像素值Gx = p3 - p1 + 2 x p6 - 2 x p4 + p9 - p7Gy = p9 + 2 x p8 + p7 - p3 - 2 x p2 - p1import cv2 # OpenCV 读取的格式是 BGR import matplotlib.pyplot as plt import numpy as np %matplotlib inlineimg = cv2.imread('yuan.jpg', cv2.IMREAD_GRAYSCALE) # cv2.imshow('img', img) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(img, cmap='gray') plt.show()dst = cv2.Sobel(src, ddepth, dx, dy, ksize)src:源图像ddepth:图像的深度dx:水平方向dy:垂直方向ksize:Sobel算子的大小sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3) # cv2.imshow('sobel_x', sobel_x) # cv2.waitKey(0) # cv2.destroyAllWindows() # 白 --> 黑 是正数,黑 --> 白 是负数,负数会被截断为0,需要进行绝对值转换 sobel_abs_x = cv2.convertScaleAbs(sobel_x) plt.imshow(sobel_abs_x, cmap='gray') plt.show()sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3) # cv2.imshow('sobel_y', sobel_y) # cv2.waitKey(0) # cv2.destroyAllWindows() # 白 --> 黑 是正数,黑 --> 白 是负数,负数会被截断为0,需要进行绝对值转换 sobel_abs_y = cv2.convertScaleAbs(sobel_y) plt.imshow(sobel_abs_y, cmap='gray') plt.show()# 分别计算x和y,再求和 sobel_x_y = cv2.addWeighted(sobel_display_x, 0.5, sobel_display_y, 0.5, 0) plt.imshow(sobel_x_y, cmap='gray') plt.show()例:分别计算和整体计算的效果进行对比ble = cv2.imread('ble.jpg', cv2.IMREAD_GRAYSCALE) # cv2.imshow('ble', ble) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(ble, cmap='gray') plt.show()# 分别计算 (建议的操作方式) ble_sobel_x = cv2.Sobel(ble, cv2.CV_64F, 1, 0, ksize=3) ble_sobel_y = cv2.Sobel(ble, cv2.CV_64F, 0, 1, ksize=3) ble_sobel_abs_x = cv2.convertScaleAbs(ble_sobel_x) ble_sobel_abs_y = cv2.convertScaleAbs(ble_sobel_y) ble_sobel_x_y = cv2.addWeighted(ble_sobel_abs_x, 0.5, ble_sobel_abs_y, 0.5, 0) plt.imshow(ble_sobel_x_y, cmap='gray') plt.show()# 整体计算 ble_sobel_xy = cv2.Sobel(ble, cv2.CV_64F, 1, 1, ksize=3) ble_sobel_abs_xy = cv2.convertScaleAbs(ble_sobel_xy) plt.imshow(ble_sobel_abs_xy, cmap='gray') plt.show()2. Scharr算子共 p1,p2,p3,p4,p5,p6,p7,p8,p9 九个像素值Gx = 3 x p3 - 3 x p1 + 10 x p6 - 10 x p4 + 3 x p9 - 3 x p7Gy = -3 x p1 - 10 x p2 - 3 x p3 - 3 x p7 - 10 x p8 - 3 x p9ble_scharr_x = cv2.Scharr(ble, cv2.CV_64F, 1, 0) ble_scharr_y = cv2.Scharr(ble, cv2.CV_64F, 0, 1) ble_scharr_abs_x = cv2.convertScaleAbs(ble_scharr_x) ble_scharr_abs_y = cv2.convertScaleAbs(ble_scharr_y) ble_scharr_x_y = cv2.addWeighted(ble_scharr_abs_x, 0.5, ble_scharr_abs_y, 0.5, 0) plt.imshow(ble_scharr_x_y, cmap='gray') plt.show()3. Laplacian算子共 p1,p2,p3,p4,p5,p6,p7,p8,p9 九个像素值G = p2 + p6 - 4 x p5 + p3 + p8ble_laplacian = cv2.Laplacian(ble, cv2.CV_64F) ble_laplacian_abs = cv2.convertScaleAbs(ble_laplacian) plt.imshow(ble_laplacian_abs, cmap='gray') plt.show()
2025年10月25日
8 阅读
0 评论
0 点赞
2025-10-23
四、图像形态学操作
1. 腐蚀操作import cv2 # OpenCV 读取的格式是 BGR import matplotlib.pyplot as plt import numpy as np %matplotlib inlineimg = cv2.imread('jswyn.jpg') # cv2.imshow('img', img) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917d142438>kernel = np.ones((3, 3), np.uint8) erosion = cv2.erode(img, kernel, iterations=2) # cv2.imshow('erosion', erosion) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(erosion, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917e451dd8>yuan = cv2.imread('yuan.jpg') # cv2.imshow('yuan', yuan) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(yuan, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917df154a8>kernel = np.ones((3, 3), np.uint8) erosion_1 = cv2.erode(yuan, kernel, iterations=1) plt.imshow(cv2.cvtColor(erosion_1, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917e749828>erosion_2 = cv2.erode(yuan, kernel, iterations=2) plt.imshow(cv2.cvtColor(erosion_2, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917e791048>erosion_3 = cv2.erode(yuan, kernel, iterations=3) plt.imshow(cv2.cvtColor(erosion_3, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917e7caa58>2. 膨胀操作img = cv2.imread('jswyn.jpg') # cv2.imshow('img', img) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917e80f358>kernel = np.ones((3, 3), np.uint8) erosion = cv2.dilate(img, kernel, iterations=2) # cv2.imshow('erosion', erosion) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(erosion, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917fd32048>yuan = cv2.imread('yuan.jpg') # cv2.imshow('yuan', yuan) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(yuan, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917fd747b8>kernel = np.ones((6, 6), np.uint8) erosion_1 = cv2.dilate(yuan, kernel, iterations=1) plt.imshow(cv2.cvtColor(erosion_1, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917fdcc438>erosion_2 = cv2.dilate(yuan, kernel, iterations=2) plt.imshow(cv2.cvtColor(erosion_2, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917fecd6a0>erosion_3 = cv2.dilate(yuan, kernel, iterations=3) plt.imshow(cv2.cvtColor(erosion_3, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917ff28320>3. 开运算与闭运算jswyn1 = cv2.imread('jswyn1.jpg') # cv2.imshow('jswyn1', jswyn1) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(jswyn1, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2917df080b8># 开运算:先腐蚀,再膨胀 kernel = np.ones((5, 5), np.uint8) open = cv2.morphologyEx(jswyn1, cv2.MORPH_OPEN, kernel) # cv2.imshow('open', open) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(open, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x291410e8e48># 闭运算:先膨胀,再腐蚀 kernel = np.ones((5, 5), np.uint8) close = cv2.morphologyEx(jswyn1, cv2.MORPH_CLOSE, kernel) # cv2.imshow('close', close) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(close, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2914114a860>4.梯度运算# 梯度运算:膨胀 - 腐蚀 img = cv2.imread('yuan.jpg') kernel = np.ones((5, 5), np.uint8) gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) # cv2.imshow('gradient', gradient) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(gradient, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x291412e8da0>5.礼帽与黑帽礼帽:原始输入 - 开运算结果黑帽:闭运算结果 - 原始输入# 礼帽 img = cv2.imread('jswyn1.jpg') kernel = np.ones((3, 3), np.uint8) top_hat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel) # cv2.imshow('top_hat', top_hat) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(top_hat, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x291415164e0># 黑帽 black_hat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel) # cv2.imshow('black_hat', black_hat) # cv2.waitKey(0) # cv2.destroyAllWindows() plt.imshow(cv2.cvtColor(black_hat, cv2.COLOR_BGR2RGB))<matplotlib.image.AxesImage at 0x2914253fb00>
2025年10月23日
7 阅读
0 评论
0 点赞
2025-10-23
Perplexity推广活动,可免费获取Pro账户使用权一个月
通过邀请链接:https://pplx.ai/suaxi 下载Perplexity浏览器,向GPT提问一次即可获得Pro账户一个月使用权包含模型:SonarClaude Sonnet 4.5GPT-5Gemini 2.5 ProGrok 4Claude Opus 4.1o3-pro2025.10.31 更新绑定 PayPal 账户,即可获取一年Pro账户订阅,绑定链接 https://www.perplexity.ai/join/p/paypal-subscription
2025年10月23日
40 阅读
0 评论
0 点赞
1
2
3
4
...
55