Skip to content

Commit 0af4632

Browse files
committed
added augmented reality using aruco markers
1 parent 3b12644 commit 0af4632

10 files changed

+399
-0
lines changed
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
### Augmented Reality using AruCo Markers in OpenCV
2+
3+
We show how to use the AruCo markers in OpenCV using an augmented reality application to replace the image inside a picture frame on a wall to display new images or videos in the frame.
4+
5+
### Compilation in C++
6+
7+
```
8+
g++ -std=c++11 augmented_reality_with_aruco.cpp -o augmented_reality_with_aruco.out `pkg-config --cflags --libs opencv4`
9+
```
10+
11+
### How to run the code
12+
13+
Command line usage for running the code
14+
15+
* Python
16+
17+
* A single image:
18+
19+
```
20+
python3 augmented_reality_with_aruco.py --image=test.jpg
21+
```
22+
23+
* A video file:
24+
25+
```
26+
python3 augmented_reality_with_aruco.py --video=test.mp4
27+
```
28+
29+
* C++:
30+
31+
* A single image:
32+
33+
```
34+
./augmented_reality_with_aruco.out --image=test.jpg
35+
```
36+
37+
* A video file:
38+
39+
```
40+
./augmented_reality_with_aruco.out --video=test.mp4
41+
```
42+
43+
### Results of YOLOv3
44+
<img src = "./augmented-reality-example.jpg" width = 1000 height = 282/>
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// 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
2+
3+
// Usage example: ./augmented_reality_with_aruco.out --image=test.jpg
4+
// ./augmented_reality_with_aruco.out --video=test.mp4
5+
#include <fstream>
6+
#include <sstream>
7+
#include <iostream>
8+
9+
#include <opencv2/aruco.hpp>
10+
#include <opencv2/imgproc.hpp>
11+
#include <opencv2/highgui.hpp>
12+
#include <opencv2/calib3d.hpp>
13+
14+
const char* keys =
15+
"{help h usage ? | | Usage examples: \n\t\t./augmented_reality_with_aruco.out --image=test.jpg \n\t\t./augmented_reality_with_aruco.out --video=test.mp4}"
16+
"{image i |<none>| input image }"
17+
"{video v |<none>| input video }"
18+
;
19+
using namespace cv;
20+
using namespace aruco;
21+
using namespace std;
22+
23+
int main(int argc, char** argv)
24+
{
25+
CommandLineParser parser(argc, argv, keys);
26+
parser.about("Use this script to do Augmented Reality using Aruco markers in OpenCV.");
27+
if (parser.has("help"))
28+
{
29+
parser.printMessage();
30+
return 0;
31+
}
32+
// Open a video file or an image file or a camera stream.
33+
string str, outputFile;
34+
VideoCapture cap;
35+
VideoWriter video;
36+
Mat frame, blob;
37+
38+
Mat im_src = imread("new_scenery.jpg");
39+
40+
try {
41+
42+
outputFile = "ar_out_cpp.avi";
43+
if (parser.has("image"))
44+
{
45+
// Open the image file
46+
str = parser.get<String>("image");
47+
ifstream ifile(str);
48+
if (!ifile) throw("error");
49+
cap.open(str);
50+
str.replace(str.end()-4, str.end(), "_ar_out_cpp.jpg");
51+
outputFile = str;
52+
}
53+
else if (parser.has("video"))
54+
{
55+
// Open the video file
56+
str = parser.get<String>("video");
57+
ifstream ifile(str);
58+
if (!ifile) throw("error");
59+
cap.open(str);
60+
str.replace(str.end()-4, str.end(), "_ar_out_cpp.avi");
61+
outputFile = str;
62+
}
63+
// Open the webcaom
64+
else cap.open(parser.get<int>("device"));
65+
66+
}
67+
catch(...) {
68+
cout << "Could not open the input image/video stream" << endl;
69+
return 0;
70+
}
71+
72+
// Get the video writer initialized to save the output video
73+
if (!parser.has("image")) {
74+
video.open(outputFile, VideoWriter::fourcc('M','J','P','G'), 28, Size(2*cap.get(CAP_PROP_FRAME_WIDTH), cap.get(CAP_PROP_FRAME_HEIGHT)));
75+
}
76+
77+
// Create a window
78+
static const string kWinName = "Augmented Reality using Aruco markers in OpenCV";
79+
namedWindow(kWinName, WINDOW_NORMAL);
80+
81+
// Process frames.
82+
while (waitKey(1) < 0)
83+
{
84+
// get frame from the video
85+
cap >> frame;
86+
87+
try {
88+
// Stop the program if reached end of video
89+
if (frame.empty()) {
90+
cout << "Done processing !!!" << endl;
91+
cout << "Output file is stored as " << outputFile << endl;
92+
waitKey(3000);
93+
break;
94+
}
95+
96+
vector<int> markerIds;
97+
98+
// Load the dictionary that was used to generate the markers.
99+
Ptr<Dictionary> dictionary = getPredefinedDictionary(DICT_6X6_250);
100+
101+
// Declare the vectors that would contain the detected marker corners and the rejected marker candidates
102+
vector<vector<Point2f>> markerCorners, rejectedCandidates;
103+
104+
// Initialize the detector parameters using default values
105+
Ptr<DetectorParameters> parameters = DetectorParameters::create();
106+
107+
// Detect the markers in the image
108+
detectMarkers(frame, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);
109+
110+
// Using the detected markers, locate the quadrilateral on the target frame where the new scene is going to be displayed.
111+
vector<Point> pts_dst;
112+
float scalingFac = 0.02;//0.015;
113+
114+
Point refPt1, refPt2, refPt3, refPt4;
115+
116+
// finding top left corner point of the target quadrilateral
117+
std::vector<int>::iterator it = std::find(markerIds.begin(), markerIds.end(), 25);
118+
int index = std::distance(markerIds.begin(), it);
119+
refPt1 = markerCorners.at(index).at(1);
120+
121+
// finding top right corner point of the target quadrilateral
122+
it = std::find(markerIds.begin(), markerIds.end(), 33);
123+
index = std::distance(markerIds.begin(), it);
124+
refPt2 = markerCorners.at(index).at(2);
125+
126+
float distance = norm(refPt1-refPt2);
127+
pts_dst.push_back(Point(refPt1.x - round(scalingFac*distance), refPt1.y - round(scalingFac*distance)));
128+
129+
pts_dst.push_back(Point(refPt2.x + round(scalingFac*distance), refPt2.y - round(scalingFac*distance)));
130+
131+
// finding bottom right corner point of the target quadrilateral
132+
it = std::find(markerIds.begin(), markerIds.end(), 30);
133+
index = std::distance(markerIds.begin(), it);
134+
refPt3 = markerCorners.at(index).at(0);
135+
pts_dst.push_back(Point(refPt3.x + round(scalingFac*distance), refPt3.y + round(scalingFac*distance)));
136+
137+
// finding bottom left corner point of the target quadrilateral
138+
it = std::find(markerIds.begin(), markerIds.end(), 23);
139+
index = std::distance(markerIds.begin(), it);
140+
refPt4 = markerCorners.at(index).at(0);
141+
pts_dst.push_back(Point(refPt4.x - round(scalingFac*distance), refPt4.y + round(scalingFac*distance)));
142+
143+
// Get the corner points of the new scene image.
144+
vector<Point> pts_src;
145+
pts_src.push_back(Point(0,0));
146+
pts_src.push_back(Point(im_src.cols, 0));
147+
pts_src.push_back(Point(im_src.cols, im_src.rows));
148+
pts_src.push_back(Point(0, im_src.rows));
149+
150+
// Compute homography from source and destination points
151+
Mat h = cv::findHomography(pts_src, pts_dst);
152+
153+
// Warped image
154+
Mat warpedImage;
155+
156+
// Warp source image to destination based on homography
157+
warpPerspective(im_src, warpedImage, h, frame.size(), INTER_CUBIC);
158+
159+
// Prepare a mask representing region to copy from the warped image into the original frame.
160+
Mat mask = Mat::zeros(frame.rows, frame.cols, CV_8UC1);
161+
fillConvexPoly(mask, pts_dst, Scalar(255, 255, 255), LINE_AA);
162+
163+
// Erode the mask to not copy the boundary effects from the warping
164+
Mat element = getStructuringElement( MORPH_RECT, Size(5,5));
165+
// Mat element = getStructuringElement( MORPH_RECT, Size(3,3));
166+
erode(mask, mask, element);
167+
168+
// Copy the warped image into the original frame in the mask region.
169+
Mat imOut = frame.clone();
170+
warpedImage.copyTo(imOut, mask);
171+
172+
// Showing the original image and the new output image side by side
173+
Mat concatenatedOutput;
174+
hconcat(frame, imOut, concatenatedOutput);
175+
176+
if (parser.has("image")) imwrite(outputFile, concatenatedOutput);
177+
else video.write(concatenatedOutput);
178+
179+
imshow(kWinName, concatenatedOutput);
180+
181+
}
182+
catch(const std::exception& e) {
183+
cout << endl << " e : " << e.what() << endl;
184+
cout << "Could not do homography !! " << endl;
185+
// return 0;
186+
}
187+
188+
}
189+
190+
cap.release();
191+
if (!parser.has("image")) video.release();
192+
193+
return 0;
194+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# 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
2+
3+
# Usage example: python3 augmented_reality_with_aruco.py --image=test.jpg
4+
# python3 augmented_reality_with_aruco.py --video=test.mp4
5+
6+
import cv2 as cv
7+
#from cv2 import aruco
8+
import argparse
9+
import sys
10+
import os.path
11+
import numpy as np
12+
13+
parser = argparse.ArgumentParser(description='Augmented Reality using Aruco markers in OpenCV')
14+
parser.add_argument('--image', help='Path to image file.')
15+
parser.add_argument('--video', help='Path to video file.')
16+
args = parser.parse_args()
17+
18+
im_src = cv.imread("new_scenery.jpg");
19+
20+
outputFile = "ar_out_py.avi"
21+
if (args.image):
22+
# Open the image file
23+
if not os.path.isfile(args.image):
24+
print("Input image file ", args.image, " doesn't exist")
25+
sys.exit(1)
26+
cap = cv.VideoCapture(args.image)
27+
outputFile = args.image[:-4]+'_ar_out_py.jpg'
28+
elif (args.video):
29+
# Open the video file
30+
if not os.path.isfile(args.video):
31+
print("Input video file ", args.video, " doesn't exist")
32+
sys.exit(1)
33+
cap = cv.VideoCapture(args.video)
34+
outputFile = args.video[:-4]+'_ar_out_py.avi'
35+
print("Storing it as :", outputFile)
36+
else:
37+
# Webcam input
38+
cap = cv.VideoCapture(0)
39+
40+
# Get the video writer initialized to save the output video
41+
if (not args.image):
42+
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))))
43+
44+
winName = "Augmented Reality using Aruco markers in OpenCV"
45+
46+
while cv.waitKey(1) < 0:
47+
try:
48+
# get frame from the video
49+
hasFrame, frame = cap.read()
50+
51+
# Stop the program if reached end of video
52+
if not hasFrame:
53+
print("Done processing !!!")
54+
print("Output file is stored as ", outputFile)
55+
cv.waitKey(3000)
56+
break
57+
58+
#Load the dictionary that was used to generate the markers.
59+
dictionary = cv.aruco.Dictionary_get(cv.aruco.DICT_6X6_250)
60+
61+
# Initialize the detector parameters using default values
62+
parameters = cv.aruco.DetectorParameters_create()
63+
64+
# Detect the markers in the image
65+
markerCorners, markerIds, rejectedCandidates = cv.aruco.detectMarkers(frame, dictionary, parameters=parameters)
66+
67+
index = np.squeeze(np.where(markerIds==25));
68+
refPt1 = np.squeeze(markerCorners[index[0]])[1];
69+
70+
index = np.squeeze(np.where(markerIds==33));
71+
refPt2 = np.squeeze(markerCorners[index[0]])[2];
72+
73+
distance = np.linalg.norm(refPt1-refPt2);
74+
75+
scalingFac = 0.02;
76+
pts_dst = [[refPt1[0] - round(scalingFac*distance), refPt1[1] - round(scalingFac*distance)]];
77+
pts_dst = pts_dst + [[refPt2[0] + round(scalingFac*distance), refPt2[1] - round(scalingFac*distance)]];
78+
79+
index = np.squeeze(np.where(markerIds==30));
80+
refPt3 = np.squeeze(markerCorners[index[0]])[0];
81+
pts_dst = pts_dst + [[refPt3[0] + round(scalingFac*distance), refPt3[1] + round(scalingFac*distance)]];
82+
83+
index = np.squeeze(np.where(markerIds==23));
84+
refPt4 = np.squeeze(markerCorners[index[0]])[0];
85+
pts_dst = pts_dst + [[refPt4[0] - round(scalingFac*distance), refPt4[1] + round(scalingFac*distance)]];
86+
87+
pts_src = [[0,0], [im_src.shape[1], 0], [im_src.shape[1], im_src.shape[0]], [0, im_src.shape[0]]];
88+
89+
pts_src_m = np.asarray(pts_src)
90+
pts_dst_m = np.asarray(pts_dst)
91+
92+
# Calculate Homography
93+
h, status = cv.findHomography(pts_src_m, pts_dst_m)
94+
95+
# Warp source image to destination based on homography
96+
warped_image = cv.warpPerspective(im_src, h, (frame.shape[1],frame.shape[0]))
97+
98+
# Prepare a mask representing region to copy from the warped image into the original frame.
99+
mask = np.zeros([frame.shape[0], frame.shape[1]], dtype=np.uint8);
100+
cv.fillConvexPoly(mask, np.int32([pts_dst_m]), (255, 255, 255), cv.LINE_AA);
101+
102+
# Erode the mask to not copy the boundary effects from the warping
103+
element = cv.getStructuringElement(cv.MORPH_RECT, (3,3));
104+
mask = cv.erode(mask, element, iterations=3);
105+
106+
# Copy the mask into 3 channels.
107+
warped_image = warped_image.astype(float)
108+
mask3 = np.zeros_like(warped_image)
109+
for i in range(0, 3):
110+
mask3[:,:,i] = mask/255
111+
112+
# Copy the warped image into the original frame in the mask region.
113+
warped_image_masked = cv.multiply(warped_image, mask3)
114+
frame_masked = cv.multiply(frame.astype(float), 1-mask3)
115+
im_out = cv.add(warped_image_masked, frame_masked)
116+
117+
# Showing the original image and the new output image side by side
118+
concatenatedOutput = cv.hconcat([frame.astype(float), im_out]);
119+
cv.imshow("AR using Aruco markers", concatenatedOutput.astype(np.uint8))
120+
121+
# Write the frame with the detection boxes
122+
if (args.image):
123+
cv.imwrite(outputFile, concatenatedOutput.astype(np.uint8));
124+
else:
125+
vid_writer.write(concatenatedOutput.astype(np.uint8))
126+
127+
128+
except Exception as inst:
129+
print(inst)
130+
131+
cv.destroyAllWindows()
132+
if 'vid_writer' in locals():
133+
vid_writer.release()
134+
print('Video writer released..')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <opencv2/highgui.hpp>
2+
#include <opencv2/aruco.hpp>
3+
4+
using namespace cv;
5+
6+
int main(int argc, char *argv[]) {
7+
8+
Mat markerImage;
9+
Ptr<cv::aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
10+
11+
aruco::drawMarker(dictionary, 33, 200, markerImage, 1);
12+
13+
imwrite("marker33.png", markerImage);
14+
15+
}

0 commit comments

Comments
 (0)