文章目录

  • 1 什么是ArUco标记?
  • 2 在OpenCV中生成ArUco标记
  • 3 检测Aruco标记
  • 4 增强现实应用
  • 5 总结和代码
    • 5.1 生成aruco标记
    • 5.2 使用aruco增强现实
  • 6 参考

在本文中,我们将解释什么是ArUco标记以及如何使用OpenCV将其用于简单的增强现实任务。ArUco标记器已在增强现实,相机姿态估计和相机校准中使用了一段时间。让我们进一步了解它们。

1 什么是ArUco标记?

ArUco标记最初由S.Garrido-Jurado等人于2014年开发,具体见文献:

Automatic generation and detection of highly reliable fiducial markers under occlusion

ArUco代表科尔多瓦大学的增强现实图书馆。这就是它在西班牙开发的地方。下面是一些ArUco标记的例子。

aruco标记是放置在被成像的对象或场景上的基准标记。它是一个具有黑色背景和边界的二元正方形,其内部生成的白色图案唯一地标识了它。黑边界有助于他们更容易被发现。它们可以产生多种大小。根据对象大小和场景选择大小,以便成功检测。如果很小的标记没有被检测到,仅仅增加它们的大小就可以使它们的检测更容易。
想法是您打印这些标记并将其放置在现实世界中。您可以拍摄现实世界并独特地检测这些标记。如果您是初学者,您可能会在想这有什么用?让我们看几个用例。
在我们在帖子中分享的示例中,我们将打印的内容和标记放在相框的角上。当我们唯一地标识标记时,我们可以用任意视频或图像替换相框。当我们移动相机时,新图片具有正确的透视失真。
在机器人应用程序中,您可以将这些标记沿着配备有摄像头的仓库机器人的路径放置。当安装在机器人上的摄像头检测到一个这些标记时,它可以知道它在仓库中的精确位置,因为每个标记都有一个唯一的ID,我们知道标记在仓库中的位置。

2 在OpenCV中生成ArUco标记

我们可以使用OpenCV很容易地生成这些标记。OpenCV中的aruco模块共有25个预定义的标记字典。字典中的所有标记包含相同数量的块或位(4×4、5×5、6×6或7×7),每个字典包含固定数量的标记(50、100、250或1000)。下面我们将展示如何在C++和Python中生成和检测各种ARUCO标记。我们将需要在代码中使用aruco模块。
下面的函数调用getPredefinedDictionary演示如何加载一个包含250个标记的字典,其中每个标记包含一个6×6位二进制模式。

C++

// Import the aruco module in OpenCV
#include <opencv2/aruco.hpp>Mat markerImage;
// Load the predefined dictionary
Ptr<cv::aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);// Generate the marker
aruco::drawMarker(dictionary, 33, 200, markerImage, 1);

Python

import cv2 as cv
import numpy as np# Load the predefined dictionary
dictionary = cv.aruco.Dictionary_get(cv.aruco.DICT_6X6_250)# Generate the marker
markerImage = np.zeros((200, 200), dtype=np.uint8)
markerImage = cv.aruco.drawMarker(dictionary, 33, 200, markerImage, 1);cv.imwrite("marker33.png", markerImage);

上面的drawMarker函数允许我们从标号为0到249的标记集合中选择具有给定id的标记(第二个参数为33)。函数的第三个参数决定了所生成标记的大小。在上面的例子中,它将生成200×200像素的图像。第四个参数表示将存储生成的标记的对象(上面的markerImage)。最后,第五个参数是宽度参数,它决定了在生成的二值模式中应该添加多少块作为边界。
drawMarker函数的结构:

void cv::aruco::drawMarker(const Ptr<Dictionary> &dictionary,int id,int sidePixels,OutputArray img,int borderBits = 1
);

具体参数如下:

参数 说明
dictionary 指示标记类型的标记词典
id 将返回的标记的标识符,它必须是指定字典中的有效id
sidePixels 图像的像素大小
img 用标记器输出图像
borderBits 标记边框的宽度

在上面的例子中,将在6×6生成的图案周围添加1位的边界,以在200×200像素的图像中生成7×7位的图像。使用上述代码生成的标记将类似于下图。

3 检测Aruco标记

用aruco标记为场景成像后,我们需要检测它们并将其用于进一步处理。下面我们展示了如何检测标记。

C++

// Load the dictionary that was used to generate the markers.
Ptr<Dictionary> dictionary = getPredefinedDictionary(DICT_6X6_250);// Initialize the detector parameters using default values
Ptr<DetectorParameters> parameters = DetectorParameters::create();// Declare the vectors that would contain the detected marker corners and the rejected marker candidates
vector<vector<Point2f>> markerCorners, rejectedCandidates;// The ids of the detected markers are stored in a vector
vector<int> markerIds;// Detect the markers in the image
detectMarkers(frame, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);

Python

#Load the dictionary that was used to generate the markers.
dictionary = cv.aruco.Dictionary_get(cv.aruco.DICT_6X6_250)# Initialize the detector parameters using default values
parameters =  cv.aruco.DetectorParameters_create()# Detect the markers in the image
markerCorners, markerIds, rejectedCandidates = cv.aruco.detectMarkers(frame, dictionary, parameters=parameters)

我们首先加载与用于生成标记的字典类似的字典。使用DetectorParameters::create()检测初始参数集。OpenCV允许我们在检测过程中更改多个参数。在大多数情况下,默认参数效果很好,OpenCV建议使用这些参数。因此,我们将坚持默认参数。
对于每次成功的标记检测,将按从左上,右上,右下和左下的顺序检测标记的四个角点。在C ++中,将这4个检测到的角点存储为点矢量,并将图像中的多个标记一起存储在点矢量中。在Python中,它们存储为Numpy数组数组。
该detectMarkers功能用于检测和定位标记的角。第一个参数是带有标记的场景图像。第二个参数是用于生成标记的字典。成功检测到的标记将存储在中markerCorners,其ID存储在中markerIds。先前初始化的DetectorParameters对象也作为参数传递。最后,被拒绝的候选人存储在中rejectedCandidates。
在将标记打印,剪切和放置在场景中时,重要的是我们在标记的黑色边界周围保留一些白色边框,以便可以轻松检测到它们。

4 增强现实应用

ArUco标记器主要是为解决包括增强现实在内的各种应用的相机姿态估计问题而开发的。OpenCV在其文档中详细描述了姿势估计过程。

https://docs.opencv.org/trunk/d5/dae/tutorial_aruco_detection.html

在此博客文章中,我们将把它们用于增强现实应用程序,该应用程序允许我们将任何新场景叠加到现有图像或视频上。我们在家中选择一个带有大型相框的场景,并希望用新的相框替换相框中的图片,以查看它们在墙上的外观。然后,我们继续尝试在影片中插入视频。为此,我们将大型的aruco标记打印,剪切并粘贴到图像区域的角落,如下图所示,然后捕获视频。捕获的视频在博客顶部的视频左侧。然后,我们按顺序分别处理视频的每一帧。

对于每个图像,首先检测标记。下图显示了以绿色绘制的检测到的标记。第一点标记有一个红色小圆圈。可以通过顺时针移动标记的边界来访问第二,第三和第四点。
输入图像和新场景图像中的四个对应点集用于计算单应性。我们在较早的一篇文章中解释了单应性。文章地址如下:

Opencv中的单应性矩阵Homography

给定场景不同视图中的对应点,单应性是将一个对应点映射到另一对应点的变换。

在我们的案例中,单应性矩阵用于将新场景图像扭曲为由我们捕获的图像中的标记定义的四边形。我们在下面的代码中展示了如何做到这一点。
C++

// Compute homography from source and destination points
Mat h = cv::findHomography(pts_src, pts_dst);// Warped image
Mat warpedImage;// Warp source image to destination based on homography
warpPerspective(im_src, warpedImage, h, frame.size(), INTER_CUBIC);// Prepare a mask representing region to copy from the warped image into the original frame.
Mat mask = Mat::zeros(frame.rows, frame.cols, CV_8UC1);
fillConvexPoly(mask, pts_dst, Scalar(255, 255, 255));// Erode the mask to not copy the boundary effects from the warping
Mat element = getStructuringElement( MORPH_RECT, Size(3,3) );
erode(mask, mask, element);// Copy the masked warped image into the original frame in the mask region.
Mat imOut = frame.clone();
warpedImage.copyTo(imOut, mask);

python

# Calculate Homography
h, status = cv.findHomography(pts_src, pts_dst)# Warp source image to destination based on homography
warped_image = cv.warpPerspective(im_src, h, (frame.shape[1],frame.shape[0]))# Prepare a mask representing region to copy from the warped image into the original frame.
mask = np.zeros([frame.shape[0], frame.shape[1]], dtype=np.uint8);
cv.fillConvexPoly(mask, np.int32([pts_dst_m]), (255, 255, 255), cv.LINE_AA);# Erode the mask to not copy the boundary effects from the warping
element = cv.getStructuringElement(cv.MORPH_RECT, (3,3));
mask = cv.erode(mask, element, iterations=3);# Copy the mask into 3 channels.
warped_image = warped_image.astype(float)
mask3 = np.zeros_like(warped_image)
for i in range(0, 3):mask3[:,:,i] = mask/255# Copy the masked warped image into the original frame in the mask region.
warped_image_masked = cv.multiply(warped_image, mask3)
frame_masked = cv.multiply(frame.astype(float), 1-mask3)
im_out = cv.add(warped_image_masked, frame_masked)

我们将新的场景图像角点用作源点(pts_src),并将捕获的图像中的相框内部相应的图像角点用作目标点(dst_src)。OpenCV函数findHomography计算源点和目标点之间的单应性函数h。单应性矩阵然后用于使新图像变形以适合目标框架。变形的图像被遮罩并复制到目标帧中。对于视频,此过程在每个帧上重复。

5 总结和代码

本文只是简单的介绍下aruco标记,还有很多关于aruco的知识没有说。具体应用还需要看文档。进一步了解更多见opencv官方文档:

https://docs.opencv.org/4.2.0/d9/d6d/tutorial_table_of_content_aruco.html

本文所有代码见:

https://github.com/luohenyueji/OpenCV-Practical-Exercise

具体代码如下

5.1 生成aruco标记

C++

// 生成aruco标志
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>using namespace cv;// 用于生成aruco图标
int main()
{Mat markerImage;// 生成字典Ptr<cv::aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);// 生成图像// 参数分别为字典,第几个标识,图像输出大小为200X200,输出图像,标记边框的宽度aruco::drawMarker(dictionary, 33, 200, markerImage, 1);imwrite("marker33.png", markerImage);return 0;
}

python

# 生成aruco标记import cv2 as cv
import numpy as np# Load the predefined dictionary
dictionary = cv.aruco.Dictionary_get(cv.aruco.DICT_6X6_250)# Generate the marker
markerImage = np.zeros((200, 200), dtype=np.uint8)
markerImage = cv.aruco.drawMarker(dictionary, 33, 200, markerImage, 1)cv.imwrite("marker33.png", markerImage)

5.2 使用aruco增强现实

C++

// This code is written by Sunita Nayak at BigVision LLC. It is based on the OpenCV project. It is subject to the license terms in the LICENSE file found in this distribution and at http://opencv.org/license.html
// 虚拟现实#include "pch.h"
#include <fstream>
#include <sstream>
#include <iostream>#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>using namespace cv;
using namespace aruco;
using namespace std;int main()
{// Open a video file or an image file or a camera stream.string str, outputFile;VideoCapture cap;VideoWriter video;Mat frame, blob;// 新场景图像Mat im_src = imread("./image/new_scenery.jpg");// 检测类型String detectType = "video";String detectPath = "./video/test.mp4";try{// 输出文件名outputFile = "ar_out_cpp.avi";// 如果检测类型是图像if (detectType == "image"){// Open the image filestr = detectPath;// 判断文件是否存在ifstream ifile(str);if (!ifile){throw("error");}cap.open(str);// 重命名str.replace(str.end() - 4, str.end(), "_ar_out_cpp.jpg");// 输出文件outputFile = str;}// 如果检测类型是视频else if (detectType == "video"){// Open the video file// 打开视频str = detectPath;ifstream ifile(str);if (!ifile){throw("error");}cap.open(str);str.replace(str.end() - 4, str.end(), "_ar_out_cpp.avi");outputFile = str;}// Open the webcaom// 打开网络摄像头elsecap.open(0);}// 错误解决办法catch (...){cout << "Could not open the input image/video stream" << endl;return 0;}// Get the video writer initialized to save the output video// 如果检测类别不是图像,则生成输出视频if (detectType != "image"){video.open(outputFile, VideoWriter::fourcc('M', 'J', 'P', 'G'), 28, Size(2 * cap.get(CAP_PROP_FRAME_WIDTH), cap.get(CAP_PROP_FRAME_HEIGHT)));}// Create a window// 创建显示窗口static const string kWinName = "Augmented Reality using Aruco markers in OpenCV";namedWindow(kWinName, WINDOW_NORMAL);// Process frames.// 逐帧处理while (waitKey(1) < 0){// get frame from the videocap >> frame;try{// Stop the program if reached end of video// 如果到了视频的结尾if (frame.empty()){cout << "Done processing !!!" << endl;cout << "Output file is stored as " << outputFile << endl;waitKey(3000);break;}vector<int> markerIds;// Load the dictionary that was used to generate the markers.// 加载用于标记的词典Ptr<Dictionary> dictionary = getPredefinedDictionary(DICT_6X6_250);// Declare the vectors that would contain the detected marker corners and the rejected marker candidates// 声明标记到的角点和没有被标记到的角点vector<vector<Point2f>> markerCorners, rejectedCandidates;// Initialize the detector parameters using default values// 使用默认值初始化检测器参数Ptr<DetectorParameters> parameters = DetectorParameters::create();// Detect the markers in the image// 检测标记/***  frame 待检测marker的图像* dictionary 字典对象*    markerCorners 检测出的图像的角的列表,从左下角顺时针开始,返回角的各个顶点的坐标*  markerIds markerCorners检测出的maker的id列表*  parameters 检测器参数*   rejectedCandidates 返回不是有效的角相关信息*/detectMarkers(frame, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);// Using the detected markers, locate the quadrilateral on the target frame where the new scene is going to be displayed.、// 使用检测到的标记,在目标帧上定位要显示新场景的四边形。vector<Point> pts_dst;// 0.015;// 计算缩减距离float scalingFac = 0.02;Point refPt1, refPt2, refPt3, refPt4;// finding top left corner point of the target quadrilateral// 寻找目标四边形的左上角点// 查找字典中id为25的标志,返回一个vectorstd::vector<int>::iterator it = std::find(markerIds.begin(), markerIds.end(), 25);// 返回markerIds中25的下标int index = std::distance(markerIds.begin(), it);// 返回markerIds中25的左上角坐标refPt1 = markerCorners.at(index).at(1);// finding top right corner point of the target quadrilateral// 求目标四边形的右上角点// 查找字典中id为33的标志,返回一个vectorit = std::find(markerIds.begin(), markerIds.end(), 33);// 返回markerIds中33的下标index = std::distance(markerIds.begin(), it);// 返回markerIds中33的右上角坐标refPt2 = markerCorners.at(index).at(2);// 返回欧式距离float distance = norm(refPt1 - refPt2);// 将缩减后的坐标放入标记点容器pts_dst.push_back(Point(refPt1.x - round(scalingFac * distance), refPt1.y - round(scalingFac * distance)));pts_dst.push_back(Point(refPt2.x + round(scalingFac * distance), refPt2.y - round(scalingFac * distance)));// finding bottom right corner point of the target quadrilateral// 求目标四边形的右下角点it = std::find(markerIds.begin(), markerIds.end(), 30);index = std::distance(markerIds.begin(), it);refPt3 = markerCorners.at(index).at(0);pts_dst.push_back(Point(refPt3.x + round(scalingFac * distance), refPt3.y + round(scalingFac * distance)));// finding bottom left corner point of the target quadrilateral// 寻找目标四边形的左下角点it = std::find(markerIds.begin(), markerIds.end(), 23);index = std::distance(markerIds.begin(), it);refPt4 = markerCorners.at(index).at(0);pts_dst.push_back(Point(refPt4.x - round(scalingFac * distance), refPt4.y + round(scalingFac * distance)));// Get the corner points of the new scene image.// 全新图像的角点vector<Point> pts_src;// 从左上角开始顺时针存入pts_src中pts_src.push_back(Point(0, 0));pts_src.push_back(Point(im_src.cols, 0));pts_src.push_back(Point(im_src.cols, im_src.rows));pts_src.push_back(Point(0, im_src.rows));// Compute homography from source and destination points// 计算单应性矩阵Mat h = cv::findHomography(pts_src, pts_dst);// Warped image// 仿射变换后的图像Mat warpedImage;// Warp source image to destination based on homography// 基于单应性矩阵映射图像warpPerspective(im_src, warpedImage, h, frame.size(), INTER_CUBIC);// Prepare a mask representing region to copy from the warped image into the original frame.// 准备一个表示要从仿射图像图像复制到原始帧中的区域的遮罩。Mat mask = Mat::zeros(frame.rows, frame.cols, CV_8UC1);// 计算单应性矩阵fillConvexPoly(mask, pts_dst, Scalar(255, 255, 255), LINE_AA);// Erode the mask to not copy the boundary effects from the warping// 侵蚀mask以不复制仿射图像的边界效果Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));// Mat element = getStructuringElement( MORPH_RECT, Size(3,3));erode(mask, mask, element);// Copy the warped image into the original frame in the mask region.// 将仿射的图像复制到遮罩区域中的原始帧中。Mat imOut = frame.clone();warpedImage.copyTo(imOut, mask);// Showing the original image and the new output image side by sideMat concatenatedOutput;// 并排显示原始图像和新输出图像hconcat(frame, imOut, concatenatedOutput);// 保存图像if (detectType == "image"){imwrite(outputFile, concatenatedOutput);}// 写视频else{video.write(concatenatedOutput);}imshow(kWinName, concatenatedOutput);}// 输出错误catch (const std::exception &e){cout << endl<< " e : " << e.what() << endl;cout << "Could not do homography !! " << endl;// return 0;}}cap.release();video.release();return 0;
}

python

# This code is written by Sunita Nayak at BigVision LLC. It is based on the OpenCV project. It is subject to the license terms in the LICENSE file found in this distribution and at http://opencv.org/license.html
# 增强现实import cv2 as cv
#from cv2 import aruco
import sys
import os.path
import numpy as np# image or video or other
detectType = 'video'
detectPath = 'video/test.mp4'
im_src = cv.imread("image/new_scenery.jpg")outputFile = "ar_out_py.avi"
if (detectType is "image"):# Open the image fileif not os.path.isfile(detectPath):print("Input image file ", detectPath, " doesn't exist")sys.exit(1)cap = cv.VideoCapture(detectPath)outputFile = detectPath[:-4]+'_ar_out_py.jpg'
elif (detectType is "video"):# Open the video fileif not os.path.isfile(detectPath):print("Input video file ", detectPath, " doesn't exist")sys.exit(1)cap = cv.VideoCapture(detectPath)outputFile = detectPath[:-4]+'_ar_out_py.avi'print("Storing it as :", outputFile)
else:# Webcam inputcap = cv.VideoCapture(0)# Get the video writer initialized to save the output video
if (detectType is not "image"):vid_writer = cv.VideoWriter(outputFile, cv.VideoWriter_fourcc('M', 'J', 'P', 'G'), 28, (round(2*cap.get(cv.CAP_PROP_FRAME_WIDTH)), round(cap.get(cv.CAP_PROP_FRAME_HEIGHT))))winName = "Augmented Reality using Aruco markers in OpenCV"while cv.waitKey(1) < 0:try:# get frame from the videohasFrame, frame = cap.read()# Stop the program if reached end of videoif not hasFrame:print("Done processing !!!")print("Output file is stored as ", outputFile)cv.waitKey(3000)break# Load the dictionary that was used to generate the markers.dictionary = cv.aruco.Dictionary_get(cv.aruco.DICT_6X6_250)# Initialize the detector parameters using default valuesparameters = cv.aruco.DetectorParameters_create()# Detect the markers in the imagemarkerCorners, markerIds, rejectedCandidates = cv.aruco.detectMarkers(frame, dictionary, parameters=parameters)index = np.squeeze(np.where(markerIds == 25))refPt1 = np.squeeze(markerCorners[index[0]])[1]index = np.squeeze(np.where(markerIds == 33))refPt2 = np.squeeze(markerCorners[index[0]])[2]distance = np.linalg.norm(refPt1-refPt2)scalingFac = 0.02pts_dst = [[refPt1[0] - round(scalingFac*distance), refPt1[1] - round(scalingFac*distance)]]pts_dst = pts_dst + \[[refPt2[0] + round(scalingFac*distance),refPt2[1] - round(scalingFac*distance)]]index = np.squeeze(np.where(markerIds == 30))refPt3 = np.squeeze(markerCorners[index[0]])[0]pts_dst = pts_dst + \[[refPt3[0] + round(scalingFac*distance),refPt3[1] + round(scalingFac*distance)]]index = np.squeeze(np.where(markerIds == 23))refPt4 = np.squeeze(markerCorners[index[0]])[0]pts_dst = pts_dst + \[[refPt4[0] - round(scalingFac*distance),refPt4[1] + round(scalingFac*distance)]]pts_src = [[0, 0], [im_src.shape[1], 0], [im_src.shape[1], im_src.shape[0]], [0, im_src.shape[0]]]pts_src_m = np.asarray(pts_src)pts_dst_m = np.asarray(pts_dst)# Calculate Homographyh, status = cv.findHomography(pts_src_m, pts_dst_m)# Warp source image to destination based on homographywarped_image = cv.warpPerspective(im_src, h, (frame.shape[1], frame.shape[0]))# Prepare a mask representing region to copy from the warped image into the original frame.mask = np.zeros([frame.shape[0], frame.shape[1]], dtype=np.uint8)cv.fillConvexPoly(mask, np.int32([pts_dst_m]), (255, 255, 255), cv.LINE_AA)# Erode the mask to not copy the boundary effects from the warpingelement = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))mask = cv.erode(mask, element, iterations=3)# Copy the mask into 3 channels.warped_image = warped_image.astype(float)mask3 = np.zeros_like(warped_image)for i in range(0, 3):mask3[:, :, i] = mask/255# Copy the warped image into the original frame in the mask region.warped_image_masked = cv.multiply(warped_image, mask3)frame_masked = cv.multiply(frame.astype(float), 1-mask3)im_out = cv.add(warped_image_masked, frame_masked)# Showing the original image and the new output image side by sideconcatenatedOutput = cv.hconcat([frame.astype(float), im_out])cv.imshow("AR using Aruco markers",concatenatedOutput.astype(np.uint8))# Write the frame with the detection boxesif (detectType is "image"):cv.imwrite(outputFile, concatenatedOutput.astype(np.uint8))else:vid_writer.write(concatenatedOutput.astype(np.uint8))except Exception as inst:print(inst)cv.destroyAllWindows()
if 'vid_writer' in locals():vid_writer.release()print('Video writer released..')

6 参考

https://www.learnopencv.com/augmented-reality-using-aruco-markers-in-opencv-c-python/
https://www.learnopencv.com/?s=homography

[OpenCV实战]39 在OpenCV中使用ArUco标记的增强现实相关推荐

  1. OpenCV进阶(10)在 OpenCV 中使用 ArUco 标记的增强现实

    在这篇文章中,我们将解释什么是 ArUco 标记,以及如何使用 OpenCV 将它们用于简单的增强现实任务. ArUco 标记已经在增强现实.相机姿态估计和相机校准中使用了一段时间.让我们更多地了解它 ...

  2. 增强现实入门实战,使用ArUco标记实现增强现实

    点击上方"AI算法修炼营",选择加星标或"置顶" 标题以下,全是干货 在本文中,我们将介绍ArUco标记以及如何使用OpenCV将其用于简单的增强现实任务,具体 ...

  3. [OpenCV实战]47 基于OpenCV实现视觉显著性检测

    人类具有一种视觉注意机制,即当面对一个场景时,会选择性地忽略不感兴趣的区域,聚焦于感兴趣的区域.这些感兴趣的区域称为显著性区域.视觉显著性检测(Visual Saliency Detection,VS ...

  4. [OpenCV实战]52 在OpenCV中使用颜色直方图

    颜色直方图是一种常见的图像特征,顾名思义颜色直方图就是用来反映图像颜色组成分布的直方图.颜色直方图的横轴表示像素值或像素值范围,纵轴表示该像素值范围内像素点的个数或出现频率.颜色直方图属于计算机视觉中 ...

  5. [OpenCV实战]24 使用OpenCV进行曝光融合

    目录 1 什么是曝光融合 2 曝光融合的原理 3 代码与结果 4 参考 本教程中,我们将了解使用OpenCV的Exposure Fusion(曝光融合). 1 什么是曝光融合 曝光融合是一种将使用不同 ...

  6. [OpenCV实战]29 使用OpenCV实现红眼自动去除

    目录 1 红眼消除 1.1 眼部检测 1.2 红眼遮掩 1.3 清除瞳孔掩模空洞 1.4 红眼修复 2 结果与完整代码 2.1 结果 2.2 代码 3 参考 在本教程中,我们将学习如何完全自动地从照片 ...

  7. 使用Python,OpenCV生成Aruco标记

    这篇博客将介绍Aruco标记是什么,以及如何使用Python,OpenCV生成Aruco标记.本文提供俩种生成方式:在线生成和代码生成: 使用OpenCV处理ArUco标签非常简单,因为OpenCV库 ...

  8. 《OpenCv视觉之眼》Python图像处理十六:Opencv图像处理实战一之图像中的硬币检测

    本专栏主要介绍如果通过OpenCv-Python进行图像处理,通过原理理解OpenCv-Python的函数处理原型,在具体情况中,针对不同的图像进行不同等级的.不同方法的处理,以达到对图像进行去噪.锐 ...

  9. OpenCV实战——提取视频中的前景对象

    OpenCV实战--提取视频中的前景对象 0. 前言 1. 提取视频中的前景对象 2. 混合高斯方法 3. 完整代码 相关链接 0. 前言 当固定摄像机观察场景时,背景基本保持不变.在这种情况下,我们 ...

最新文章

  1. 马斯克新视频:Boring公司将优先解决公交快速通勤
  2. pycharm 报错 out of memory 解决方法
  3. matlab verilog 接口,使用SystemVerilog简化FPGA中的接口
  4. JavaScript实现binarySearch二分查找算法(附完整源码)
  5. 公众号向特定用户主动推送消息_公众号助手——消息不仅可以群发,还不限制次数!...
  6. 7-207 排序 (25 分)
  7. 安卓seekbar 怎么判断正负_如果没有万用表,我们要怎么区分电源DC线的正负极呢...
  8. python软件下载3版本-Python 3.7.2和3.6.8版本发布下载,附更新说明
  9. 对php的感受100字_【php实训心得】php心得体会
  10. maven中引用JDK中的tools jar
  11. Java 中判断char 是否为空格 和空
  12. 学习Vue电商后台管理系统剩余功能模块的bug
  13. yarn安装详细教程说明、升级教程、修改yarn的全局和缓存目录、yarn基本命令
  14. 螺旋线java_java怎么画布画阿基米螺旋线
  15. 手把手教你,如何用Python做副业月入10000+!
  16. 平行因子-三维荧光-PARAFAC数据前处理
  17. 部编版三下《燕子》教学反思
  18. android 恢复出厂,安卓(Android)手机恢复出厂设置的方法
  19. 【Java】实现计算器
  20. 刘慈欣 计算机工程师,刘慈欣为什么这么厉害 科幻作家刘慈欣作品有哪些

热门文章

  1. unittest教程(2w字实例合集)——Python自动化测试一文入门
  2. 解决路由器接电信光猫win10出现ipv6不稳定的问题
  3. Juniper SRX NAT46/NAT64配置
  4. 苹果x重启方法_iPhone无法开机怎么办?三种快速维修方法
  5. 前端url编码解码方法
  6. 禁止复制服务器文件夹,远程桌面服务器 禁止复制文件夹
  7. 稳定获取Android设备唯一码(UUID)的解决方案
  8. 查看手机IMEI IMSI
  9. 如何在Axure中使用Iconfont图标字体
  10. 跑步戴哪款无线耳机好?适合跑步用的运动耳机分享