-
什么是交互式前景提取经典的前景提取技术主要使用纹理(颜色)信息,如魔术棒工具,或根据边缘(对比度)信息,如智能剪刀等完成。2004年,微软研究院的Rother等人在论文中提出了交互式前景提取技术。他们提出的算法,仅需要做很少的交互操作,就能够准确地提取出前景图像。在开始提取前景时,先用一个矩形框指定前景区域所在的大致位置范围,然后不断迭代地分割,直到达到最好的效果。经过上述处理后,提取前景的效果可能并不理想,存在前景没有提取出来,或者将背景提取为前景的情况,此时需要用户干预提取过程。用户在原始图像的副本中(也可以是原始图像大小相等的任意一副图像),用白色标注要提取为前景的区域,用黑色标注要作为背景的区域,然后,将标注后的图像作为掩模,让算法继续迭代提取前景从而得到最终的结果。比如上图这种人物图像,先用矩形框将要提取的前景框出来,在分别用白色和黑色对前景图像,背景图像进行标注。标注完成之后,使用交互式前景提取算法,就可以提取人物的完整前景了。下面,博主来详细介绍交互式前景提取算法GrabCut算法的原理:将前景所在的大致区域位置使用矩形框标注出来。值得注意的是,此时的矩形框框出的仅仅是前景的大致位置,其中即包含前景又包含背景,所以该区域实际上是未确定区域。但是该区域以外的区域被认为是“确定背景”根据矩形框外部的“确定背景”数据来区分矩形框区域内的前景和背景。用高斯混合模型(GMM)对前景和背景建模。GMM会根据用户的输入学习并创建新的像素分布。对未分类的像素(可能是背景也可能是前景),根据其与已知分类像素(前景和背景)的关系进行分类。根据像素的分布情况生成一副图,图中的节点就是各个像素点。除了像素点之外,还有两个节点:前景节点和背景节点。所有的前景像素都和前景节点相连,所有的背景像素都和背景节点相连。每个像素连接到前景节点或背景节点的边的权重由像素是前景或背景的概率来决定图中的每个像素除了与前景节点或背景节点相连外,彼此之间还存在着连接。两个像素连接的边的权重值由它们的相似性决定,两个像素值的颜色越接近,边的权重值越大。完成节点连接后,需要解决的问题变成了一副连通的图。在该图上根据各自边的权重关系进行切割,将不同的点划分为前景节点和背景节点。不断重复上述过程,直到分类收敛为止。
-
OpenCV在TEXT扩展模块中支持场景文字识别,最早的场景文字检测是基于级联检测器实现,OpenCV中早期的场景文字检测是基于极值区域文本定位与识别、最新的OpenCV3.4.x之后的版本添加了卷积神经网络实现场景文字检测,后者的准确性与稳定性比前者有了很大的改观,不再是鸡肋算法,是可以应用到实际场景中的。值得一提的是基于CNN实现场景文字检测算法OpenCV中采用了是华中科技大学贡献的模型,模型结构如下:代码演示 基于极值区域文本定位的方法实现场景文字检测演示如下:def cascade_classfier_text_detect(): img = cv.imread("D:/images/cover_01.jpg") vis = img.copy() # Extract channels to be processed individually channels = cv.text.computeNMChannels(img) cn = len(channels)-1 for c in range(0,cn): channels.append((255-channels[c])) # Apply the default cascade classifier to each independent channel (could be done in parallel) print("Extracting Class Specific Extremal Regions from "+str(len(channels))+" channels ...") print(" (...) this may take a while (...)") for channel in channels: erc1 = cv.text.loadClassifierNM1('trained_classifierNM1.xml') er1 = cv.text.createERFilterNM1(erc1,16,0.00015,0.13,0.2,True,0.1) erc2 = cv.text.loadClassifierNM2('trained_classifierNM2.xml') er2 = cv.text.createERFilterNM2(erc2,0.5) regions = cv.text.detectRegions(channel,er1,er2) rects = cv.text.erGrouping(img,channel,[r.tolist() for r in regions]) #Visualization for r in range(0,np.shape(rects)[0]): rect = rects[r] cv.rectangle(vis, (rect[0],rect[1]), (rect[0]+rect[2],rect[1]+rect[3]), (255, 0, 0), 2) cv.rectangle(vis, (rect[0],rect[1]), (rect[0]+rect[2],rect[1]+rect[3]), (0, 0, 255), 1) #Visualization cv.imshow("Text detection result", vis) cv.imwrite("D:/test_detection_demo_02.png", vis) cv.waitKey(0)基于卷积神经网络模型实现场景文字检测演示如下:def cnn_text_detect(): image = cv.imread("D:/images/cover_01.jpg") cv.imshow("input", image) result = image.copy() detector = cv.text.TextDetectorCNN_create("textbox.prototxt", "TextBoxes_icdar13.caffemodel") boxes, scores = detector.detect(image); threshold = 0.5 for r in range(np.shape(boxes)[0]): if scores[r] > threshold: rect = boxes[r] cv.rectangle(result, (rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3]), (255, 0, 0), 2) cv.imshow("Text detection result", result) cv.waitKey() cv.waitKey(0) cv.destroyAllWindows()运行结果 基于极值区域文本定位
-
OpenCVHalcon开发语言C++、C#(emgu)、Python、Ruby、MATLAB等语言C,C++,C#,Visual basic和Delphi等语言应用场合侧重计算机视觉领域,侧重研究领域侧重机器视觉领域,侧重应用领域费用免费收费开放性及版本更新速度开源(可看底层源码),版本和功能更新慢商业软件(底层代码封装),版本和功能更新快对使用者的门槛偏科研,有难度,有深度,完全从底层开发,对使用者门槛高,开发效率低,开发慢偏工程应用,使用封装好的功能函数,对使用者门槛低,开发效率高,开发快资料及技术支持资料少。遇到问题,难以获得技术支持资料多。遇到问题,可以及时、有效的获得技术支持Halcon 在工业视觉领域属于经常使用的软件,相对于opencv的开源精神Halcon属于商业非开源项目并且收费。Halcon起源于德国在国内的工业视觉领域市场占用率遥遥领先。作者在使用halcon的过程中也感受了其软件的人性化,有独立的调试编程环境。对应主流的语言C#、C++、VB等工业上常用的语言都能提供流程的调用。Halcon提供的每一年都有升级,在升级的过程中算子的速度更快能达到汇编级别的加速度,对比opencv在总体的算子性能领先程序在五到十年。与此同时Opencv在调试的过程中没有Halcon方便,opencv的使用需要用户有比较好的编程基础,并且图像并不是实时能够观察调整。Halcon:底层功能算法多,运算性能快,开发需要一定软件功底和图像处理理论。快速学习的做法:研究实例、做实战项目。halcon不能提供相应的界面编程需求,需要和vs来构造界面,才能构成一套完整软件。 OpenCV Opencv:计算机图像方面的图像库,开源的,可以用于商用,在很多高校和科研机构使用比较多,更多的人选择它,是为了写自己的算法,其调试不像Halcon那样方便,其项目开发周期也比Halcon要长,所以在工业应用上,还不是太多。 但是,如果你是搞算法的,并且项目周期长,公司不愿意购买/使用商业视觉软件的,可以考虑Opencv;如果你的项目周期短,公司可以承受商业软件的成本,选择Halcon会是比较明智的选择。
-
【功能模块】mindspore.dataset.vision.c_transforms.Resize【操作步骤&问题现象】mindspore Resize的Inter.BILINEAR双线性插值方法和opencv双线性插值方法(INTER_LINER)处理图片结果不同,并且也尝试了几个其他opencv的插值方法,结果都和mindspore的resize结果不同。【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
【功能模块】手头有一个3D人脸重建的onnx模型,使用mobilenet_V1训练的,输出结果为62个浮点数,分别是人脸的旋转平移、形状、和表情的系数。该模型通过opencv自带的dnn模块推理正常,我把他转成om模型使用Atlas 200 DK的NPU进行推理打算集成到项目中,当我改写了一个样例比较onnx的模型和om模型的输出差异,发现om模型输出的结果太离谱,基本上不能用,不知道问题出在哪里。项目说明和atc的转换步骤见项目的README_CN.md.【操作步骤&问题现象】1、使用opencv加载一个tiny_yolo的模型进行人脸检测,将检测到的人脸裁减出来分别送到onnx模型通过opencv::dnn进行推理和送到om模型通过NPU进行推理2、输出两者推理结果进行比较3、onnx的推理结果正常,om模型推理结果不正常4、推理结果比较见截图,前面是onnx的正常结果,后面的是om推理的不正常结果。项目代码见附件,项目描述见README。【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
【功能模块】OPENCV的内存是连续内存,DVPP的内存是STRIDE分割过的。我是不是可以理解用了OPENCV,再用DVPP,所有的流程都可能存在内存无法对齐的问题?我这么理解有没有问题?有没有什么例子OPENCV处理后接DVPP的?【操作步骤&问题现象】为了规避图片解码失败的问题,使用OPENCV读图。【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
【功能模块】APP_ERROR CenterNet::Resize_Affine(MxBase::TensorBase &input, MxBase::TensorBase *output) { int new_width, new_height; auto shape = input.GetShape(); new_height = static_cast<int>(shape[0]); new_width = static_cast<int>(shape[1]); std::cout <<"new_height:" << shape[0] << ";" << "new_width:" << shape[1] << std::endl; cv::Mat src(new_height, new_width, CV_8UC3, input.GetBuffer()); //运行到此行报错******** cv::Point2f srcPoint2f[3], dstPoint2f[3]; int max_h_w = std::max(static_cast<int>(shape[0]), static_cast<int>(shape[1])); srcPoint2f[0] = cv::Point2f(static_cast<float>(new_width / 2.0), static_cast<float>(new_height / 2.0)); srcPoint2f[1] = cv::Point2f(static_cast<float>(new_width / 2.0), static_cast<float>((new_height - max_h_w) / 2.0)); srcPoint2f[2] = cv::Point2f(static_cast<float>((new_width - max_h_w) / 2.0), static_cast<float>((new_height - max_h_w) / 2.0)); dstPoint2f[0] = cv::Point2f(static_cast<float>(MODEL_WIDTH) / 2.0, static_cast<float>(MODEL_HEIGHT) / 2.0); dstPoint2f[1] = cv::Point2f(static_cast<float>(MODEL_WIDTH) / 2.0, 0.0); dstPoint2f[2] = cv::Point2f(0.0, 0.0); cv::Mat warp_mat(2, 3, CV_32FC1); warp_mat = cv::getAffineTransform(srcPoint2f, dstPoint2f); cv::Mat warp_dst = cv::Mat::zeros(cv::Size(static_cast<int>(MODEL_HEIGHT), static_cast<int>(MODEL_WIDTH)), src.type()); cv::warpAffine(src, warp_dst, warp_mat, warp_dst.size()); void *affine_output; unsigned char *affine_address; affine_output = output -> GetBuffer(); affine_address = static_cast<unsigned char *>(affine_output); for (int nrow = 0; nrow < warp_dst.rows; nrow++) { for (int ncol = 0; ncol < warp_dst.cols; ncol++) { *affine_address = warp_dst.at<cv::Vec3b>(nrow, ncol)[0]; affine_address++; *affine_address = warp_dst.at<cv::Vec3b>(nrow, ncol)[1]; affine_address++; *affine_address = warp_dst.at<cv::Vec3b>(nrow, ncol)[2]; affine_address++; } } return APP_ERR_OK;}【操作步骤&问题现象】1、运行到上面函数的第六行报错:Segmentation fault (core dumped)2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
distanceTransform函数当图像内的各个子图没有连接时,可以直接使用形态学的腐蚀操作确定前景对象,但是如果图像内的子图连接在一起时,就很难确定前景对象了。这个时候,就需要借助变换函数cv2.distanceTransform()方便地将前景对象提取出来。cv2.distanceTransform()反应了各个像素点与背景(值为0的像素点)的距离关系。通常情况下:如果前景对象的中心距离值为0的像素点距离较远,会得到一个较大的值。如果前景对象的边缘距离值为0的像素点较近,会得到一个较小的值。下面,我们来使用该函数确定一副图像的前景,并观察效果。import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("36.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) k = np.ones((5, 5), dtype=np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, k, iterations=2) distTransform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret, fore = cv2.threshold(distTransform, 0.7 * distTransform.max(), 255, 0) plt.subplot(131) plt.imshow(img, cmap="gray") plt.axis('off') plt.subplot(132) plt.imshow(distTransform, cmap="gray") plt.axis('off') plt.subplot(133) plt.imshow(fore, cmap="gray") plt.axis('off') plt.show() 复制代码这里,我们使用cv2.morphologyEx函数进行开运算,同时使用cv2.distanceTransform得到距离图像,最后在通过cv2.threshold对距离图像进行阈值处理,确定前景。运行之后,效果如下:作者:极客学编程链接:https://juejin.cn/post/6989181801319268389来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
waterShed函数在OpenCV中,可以使用函数cv2.watershed()函数实现分水岭算法。不过,具体实现的过程,还需要借助形态学函数,距离变换函数cv2.distanceTransform(),cv2.connectedComponents()来完成图像分割。形态学分割在使用分水岭算法之前,我们需要对图像进行简单的形态学处理。一般情况下,我们都是使用形态学中的开运算,因为开运算是先腐蚀后膨胀的操作,能够去除图像内的噪声。import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("36.jpg") k=np.ones((5,5),dtype=np.uint8) e=cv2.erode(img,k) result=cv2.subtract(img,e) plt.subplot(131) plt.imshow(img, cmap="gray") plt.axis('off') plt.subplot(132) plt.imshow(e, cmap="gray") plt.axis('off') plt.subplot(133) plt.imshow(result, cmap="gray") plt.axis('off') plt.show() 复制代码回顾一下,我们前面的开运算函数为cv2.erode(),这里我们首先经过开运算去除噪声。然后减法运算cv2.subtract()获取图像边界。运行之后,效果如下:作者:极客学编程链接:https://juejin.cn/post/6989181801319268389来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
图像分割了解分水岭算法之前,我们需要了解什么是图像的分割。在图像的处理过程中,经常需要从图像中将前景对象作为目标图像分割或者提取出来。例如,在视频监控中,观测到的是固定背景下的视频内容,而我们对背景本身并无兴趣,感兴趣的是背景中出现的车辆,行人或者其他对象。我们希望将这些对象从视频中提取出来,而忽略那些没有对象进入背景的视频内容。分水岭算法图像分割是图像处理过程中一种非常重要的操作。分水岭算法将图像形象地比喻为地理学上的地形表面,实现图像分割,该算法非常有用。下面,博主对分水岭算法的相关内容做简单的介绍。(详细可以参考冈萨雷斯的《数字图像处理》一书)任何一副灰度图像,都可以被看作是地理学上的地形表面,灰度值越高的区域可以被看成是山峰,灰度值越低的区域可以被看成是山谷。如果我们向每个山谷中灌注不同颜色的水。那么随着水位的不断升高,不同山谷的水就汇聚到一起。在这个过程中,为了防止不同山谷的水交汇,我们需要在水流可能汇合的地方构建堤坝。该过程将图像分为两个不同的集合:集水盆地和分水岭线。我们构建的堤坝就是分水岭线,也即对原始图像的分割。这就是分水岭算法的原理。不过,一般的图像都存在着噪声,采用分水岭算法时,会经常得到过度分割的结果。为了改善图像分割的效果,人们提出了基于掩摸的改进的分水岭算法。改进的分水岭算法允许用户将它认为是同一个分割区域的部分标注出来。这样,分水岭算法在处理时,就会将标注的部分处理为同一个分割区域。如果对于该理论不怎么了解,可以使用软件PowerPoint中的“删除背景”功能进行观察配合理解。作者:极客学编程链接:https://juejin.cn/post/6989181801319268389来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
HoughCircles实战霍夫变换出来用来检测直线外,我们还可以用来检测其他的几何对象。实际上,只要是能用一个方程式表示的对象,都适合用霍夫变换来检测。其中,我们就可以使用霍夫圆变换来检测图像中的圆。这里我们只需要考虑圆心坐标(x,y)与半径r共3个参数。在OpenCV中要经过2个步骤:找出可能存在圆的位置(圆心)根据1计算半径在OpenCV中,它给我们提供的霍夫圆变换函数为cv2.HoughCircle()。该函数也是将Canny边缘检测与霍夫变换结合,唯一的区别是,不要我们进行Canny边缘检测,该函数自动先进行Canny边缘检测。其完整定义如下:def HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None): 复制代码image:原始图像,8位单通道灰度图像method:检测方法,HOUGH_GRADIENT是唯一可用的参数值。该参数代表霍夫圆检测中两轮检测所使用的方法dp:累计器分辨率,它是一个分割比例,用来指定图像分辨率与圆心累加器分辨率的比例。例如,如果dp=1,则输入图像和累加器具有相同的分辨率minDist:圆心间最小间距。该值被作为阈值来使用,如果存在圆心间距小于该值的多个圆,则仅有一个会被检测出来。因此,如果该值太小,则会有很多临近的圆被检测出来;如果该值很大,则可能会在检测时漏掉很多圆circles:返回值,有圆心坐标和半径构成的numpy.ndarray类型。param1:该参数缺省,默认100。它对应的是Canny边缘检测器的高阈值(低阈值是高阈值的二分之一)param2:圆心位置必须受到的投票数。只有在第1论筛选的过程中,投票数超过该值的圆,才有资格进入第2轮的筛选。因此,该值越大,检测到的圆越少;该值越小,检测到的圆越多。也是缺省值,默认100minRadius:圆半径最小值,小于该值的圆不会被检测出来。也是缺省值,默认0,不起作用maxRadius:圆半径的最大值,大于该值的圆不会被检测出来。也是缺省值,默认0,不起作用下面,我们来用一个奥运五环的照片,进行霍夫圆变换。代码如下:import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("35_1.jpg") plt.subplot(121) plt.imshow(img, cmap="gray") plt.axis('off') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 300, param1=50, param2=20,minRadius=0,maxRadius=0) circles = np.uint16(np.around(circles)) for i in circles[0, :]: cv2.circle(img, (i[0], i[1]), i[2], (255, 0, 0), 12) cv2.circle(img, (i[0], i[1]), 2, (255, 0, 0), 12) plt.subplot(122) plt.imshow(img, cmap="gray") plt.axis('off') plt.show() 复制代码运行之后,效果如下:作者:极客学编程链接:https://juejin.cn/post/6988803455154388999来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
使用HoughLines虽然可以完成霍夫变换,但其本身存在非常严重的误检测。为了解决这个问题,OpenCV加入了概率霍夫变换函数cv2.HoughLinesP()函数。其完整定义如下:def HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None): 复制代码image:原始图像,比如为8位单通道二值图像rho:同上theta:同上threshold:同上lines:同上minLineLength:用来控制”接受直线的最小长度“的值。默认值为0maxLineGap:用来控制接受共线线段之间的最小间隔,即在一条直线中两点的最大间隔。如果两点间的间隔超过了参数maxLineGap的值,就认为这两点不在一条直线上。默认值为0将上面的例子,使用HoughLinesP改进以下:import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("35.jpg") plt.subplot(121) plt.imshow(img, cmap="gray") plt.axis('off') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150, apertureSize=3) lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 10, 10) for line in lines: x1, y1, x2, y2 = line[0] cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 5) plt.subplot(122) plt.imshow(img, cmap="gray") plt.axis('off') plt.show() 复制代码运行之后,效果如下:作者:极客学编程链接:https://juejin.cn/post/6988803455154388999来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
什么是霍夫变换霍夫变换是一种在图像中寻找直线,圆形以及其他简单形状的方法。霍夫变换采用类似于投票的方式来获取当前图像内的形状集合,该变换由Paul Hough(霍夫)于1962年首次提出。最初的霍夫变换只能用于检测直线,经过发展后,霍夫变换不仅能够识别直线,还能识别其他简单的图形结构,常见的有圆形,椭圆等。HoughLines函数在OpenCV中,它给我们提供了cv2.HoughLines()函数来实现霍夫直线变换,该函数要求所有操作的原图是一个二值图像,所以在进行霍夫变换之前,需要将图像进行二值化处理。或者进行Canny边缘检测。其完整定义如下:def HoughLines(image, rho, theta, threshold, lines=None, srn=None, stn=None, min_theta=None, max_theta=None): 复制代码image:原始图形,必须是8位单通道的二值图像rho:以像素为单位的距离r的精度。一般情况下,使用的精度是1theta:为角度θ的精度。一般情况下,使用的精度是Π/180,表示要搜索所有可能的角度threshold:阈值。该值越小,判定出直线就越多。识别直线时,要判定多少个点位于该直线上。在判定直线是否存在时,对直线所穿过的点的数量进行评估,如果直线所穿过的点的数量小于阈值,则认为这些点恰好在算法构成直线,但是在原始图像中该直线并不存在;如果大于阈值,则认为直线存在。所以,如果阈值越小,就会得到较多的直线;阈值越大,就会得到较少的直线lines:返回值,它的每个元素都是一对浮点数,表示检测到的直线的参数,即(r,θ)。是numpy.ndarray类型。作者:极客学编程链接:https://juejin.cn/post/6988803455154388999来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
在循环中使用zip()函数zip()用可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。例如,我们获取的索引为x,y,z。下面我们使用zip()将它们打包成元组。代码如下:import cv2 import numpy as np import matplotlib.pyplot as plt img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]]) result = np.where(img > 5) for i in zip(*result): print(i) 复制代码这里我们还是使用上面的值,输出结果如下:这里自动将我们刚才满足条件的索引打包成了元素格式。是不是比刚才的控制台输出结果更加的直观呢?替换坐标我们上面得到的结果是符合条件的索引:(行号,列号),但我们需要绘制匹配位置的矩形,需要的是(列号,行号)。所以,在使用cv2.rectangle()绘制矩形前,要先将函数numpy.where()得到的位置索引行列互换,行列互换可以通过如下代码实现:import numpy as np img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]]) result = np.where(img > 5) for i in zip(*result[::-1]): print(i) 复制代码运行之后,输出结果如下:作者:极客学编程链接:https://juejin.cn/post/6988802789333794853来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
多模板匹配在上一篇的实战中,我们通过人物眼睛的子图,找出了其在图像中出现位置。但是,有些情况下,并不仅仅只有一次,比如我们讲解傅里叶变换时,曾介绍一张草原的狮子图。如果匹配某个草,可能单个图像内会有很多,这个时候就要找出多个匹配结果。而函数cv2.minMaxLoc()仅仅能找出最值,无法给出所有匹配区域的位置信息。所以,要想匹配多个结果,就需要进行如下4个步骤:获取匹配位置的集合首先,Numpy库中的函数where()能够获取模板匹配位置的集合。对于不同的输入,其返回值是不同的。当输入是一维数组时,返回值是一维索引,只是一组索引数组。当输入是二维数组时,返回的是匹配值的位置索引,因此会有两组索引数组表示返回值的位置。比如,我们的灰度图像一般都是二维数组。下面,我们来查找一个二维数组中,值大于8的元素索引:import numpy as np img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]]) result = np.where(img > 5) print(result) 复制代码运行之后,控制台会输出如下内容:如果你对Numpy不是很了解的化。下面博主在将数据转换以下,基本上都能看懂了。转换之后,格式如下:第一行为大于5的值的X坐标,第二行为大于5的值的Y坐标。那么上面大于5的数组索引为:[0,2],[0,3],[0,4],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3]。你可以回溯对比看看是不是一致的。通过np.where()函数可以找出在cv2.matchTemplate()函数的返回值中,哪些位置上的值是大于阈值threshold的。具体操作代码如下:loc=np.where(res>threshold) 复制代码循环因为我们找到的原图对应的模板图像不止一个,要处理多个值,肯定会用到循环。因此,在获取匹配值的索引后,可以采用如下语句遍历所有匹配的位置,对这些位置做标记:for i in 匹配位置集合: 标记匹配位置作者:极客学编程链接:https://juejin.cn/post/6988802789333794853来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
上滑加载中
推荐直播
-
OpenHarmony应用开发之网络数据请求与数据解析
2025/01/16 周四 19:00-20:30
华为开发者布道师、南京师范大学泰州学院副教授,硕士研究生导师,开放原子教育银牌认证讲师
科技浪潮中,鸿蒙生态强势崛起,OpenHarmony开启智能终端无限可能。当下,其原生应用开发适配潜力巨大,终端设备已广泛融入生活各场景,从家居到办公、穿戴至车载。 现在,机会敲门!我们的直播聚焦OpenHarmony关键的网络数据请求与解析,抛开晦涩理论,用真实案例带你掌握数据访问接口,轻松应对复杂网络请求、精准解析Json与Xml数据。参与直播,为开发鸿蒙App夯实基础,抢占科技新高地,别错过!
回顾中 -
Ascend C高层API设计原理与实现系列
2025/01/17 周五 15:30-17:00
Ascend C 技术专家
以LayerNorm算子开发为例,讲解开箱即用的Ascend C高层API
回顾中
热门标签