Skip to content

Commit dbcca4a

Browse files
committed
Added cvDisplacementMapFilter to the project.
1 parent bd5a66d commit dbcca4a

File tree

6 files changed

+233
-0
lines changed

6 files changed

+233
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ This application uses OpenCV to retrieve frames from the camera and display them
2222
ipcQTgesture
2323
--------------
2424
A Qt application that uses UtilPipeline (Intel® Perceptual Computing SDK 2013) for gesture recognition. This one is Windows only!
25+
26+
cvDisplacementMapFilter
27+
--------------
28+
An app that implements the Displacement Map Filter to distort a movie clip.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
SOURCES += \
2+
main.cpp
3+
4+
# On my system I have to specify g++ as compiler else it will use clang++ by default
5+
QMAKE_CXX=g++
6+
QMAKE_CC=gcc
7+
8+
## OpenCV settings for Unix/Linux
9+
unix:!mac {
10+
message("* Using settings for Unix/Linux.")
11+
INCLUDEPATH += /usr/local/include/opencv
12+
13+
LIBS += -L/usr/local/lib/ \
14+
-lopencv_core \
15+
-lopencv_highgui \
16+
-lopencv_imgproc
17+
}
18+
19+
## OpenCV settings for Mac OS X
20+
macx {
21+
message("* Using settings for Mac OS X.")
22+
INCLUDEPATH += /usr/local/include/opencv
23+
24+
LIBS += -L/usr/local/lib/ \
25+
-lopencv_core \
26+
-lopencv_highgui \
27+
-lopencv_imgproc
28+
}
29+
30+
## OpenCV settings for Windows and OpenCV 2.4.2
31+
win32 {
32+
message("* Using settings for Windows.")
33+
INCLUDEPATH += "C:\\opencv\\build\\include" \
34+
"C:\\opencv\\build\\include\\opencv" \
35+
"C:\\opencv\\build\\include\\opencv2"
36+
37+
LIBS += -L"C:\\opencv\\build\\x86\\vc10\\lib" \
38+
-lopencv_core242 \
39+
-lopencv_highgui242 \
40+
-lopencv_imgproc242
41+
}
42+
43+
# Runs this app automatically after the building has succeeded
44+
#QMAKE_POST_LINK=./$$TARGET

cvDisplacementMapFilter/main.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#include <iostream>
2+
#include <vector>
3+
4+
#include <opencv2/highgui/highgui.hpp>
5+
#include <opencv2/imgproc/imgproc.hpp>
6+
7+
/* overlayImage: copies a transparent 4-channel image over a solid background image.
8+
* Original source: http://jepsonsblog.blogspot.com.br/2012/10/overlay-transparent-image-in-opencv.html
9+
*
10+
* background: must be 3-channel BGR.
11+
* foreground: must be 4-channel RGBA.
12+
* output: the destination Mat.
13+
* location: offset starting point.
14+
*/
15+
void overlayImage(const cv::Mat &background, const cv::Mat &foreground, cv::Mat &output, cv::Point2i location)
16+
{
17+
background.copyTo(output);
18+
19+
// start at the row indicated by location, or at row 0 if location.y is negative.
20+
for (int y = std::max(location.y , 0); y < background.rows; ++y) {
21+
int fY = y - location.y; // because of the translation
22+
23+
// we are done of we have processed all rows of the foreground image.
24+
if (fY >= foreground.rows)
25+
break;
26+
27+
// start at the column indicated by location, or at column 0 if location.x is negative.
28+
for (int x = std::max(location.x, 0); x < background.cols; ++x) {
29+
int fX = x - location.x; // because of the translation.
30+
31+
// we are done with this row if the column is outside of the foreground image.
32+
if (fX >= foreground.cols)
33+
break;
34+
35+
// determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
36+
double opacity = ((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3]) / 255.;
37+
38+
// and now combine the background and foreground pixel, using the opacity, but only if opacity > 0.
39+
for (int c = 0; opacity > 0 && c < output.channels(); ++c) {
40+
unsigned char foregroundPx = foreground.data[fY * foreground.step + fX * foreground.channels() + c];
41+
unsigned char backgroundPx = background.data[y * background.step + x * background.channels() + c];
42+
output.data[y*output.step + output.channels()*x + c] = backgroundPx * (1.-opacity) + foregroundPx * opacity;
43+
}
44+
}
45+
}
46+
}
47+
48+
/* getComponent: helper function that returns the color of a specific component.
49+
*
50+
* bgr_pixel: the input BGR pixel of type cv::Vec3b.
51+
* bgr_component: the index of the channel. 0 for Blue, 1 for Green, 2 for Red.
52+
* This function returns the color of a pixel as unsigned char.
53+
*/
54+
uchar getComponent(cv::Vec3b bgr_pixel, int bgr_component)
55+
{
56+
switch (bgr_component)
57+
{
58+
case 0: // Blue
59+
return bgr_pixel[0];
60+
61+
case 1: // Green
62+
return bgr_pixel[1];
63+
64+
case 2: // Red
65+
return bgr_pixel[2];
66+
67+
default:
68+
std::cout << "!!! getComponent: " << bgr_component << " is not a valid component" << std::endl;
69+
break;
70+
}
71+
72+
return 0;
73+
}
74+
75+
/* displacementMapFilter: uses the pixel values of a map to displace the pixels of the target image.
76+
*
77+
* map: the input map image (BGR).
78+
* target: the input foreground image (BGRA).
79+
* componentX: describes which color channel to use in the map image to displace the x result. Blue:0 Green:1 Red:2.
80+
* componentY: describes which color channel to use in the map image to displace the y result. Blue:0 Green:1 Red:2.
81+
* scaleX: the strength of the effect in the x plane.
82+
* scaleY: the strength of the effect in the y plane.
83+
* output: the destination Mat (BGRA).
84+
*/
85+
void displacementMapFilter(const cv::Mat& map, const cv::Mat& target, int componentX, int componentY, int scaleX, int scaleY, cv::Mat& output)
86+
{
87+
if (componentX < 0 || componentX > 2 || componentY < 0 || componentY > 2)
88+
{
89+
std::cout << "!!! displacementMapFilter: componentX and componentY values must be in range [0,2]" << std::endl;
90+
return;
91+
}
92+
93+
if (target.size().width != map.size().width || target.size().height != map.size().height || target.type() != CV_8UC4)
94+
{
95+
std::cout << "!!! displacementMapFilter: map and target need to have the same dimensions, and target type must be CV_8UC4" << std::endl;
96+
return;
97+
}
98+
99+
output.create(target.rows, target.cols, target.type());
100+
101+
for (int x = 0; x < output.rows; x++)
102+
for (int y = 0; y < output.cols; y++)
103+
{
104+
/* Formula:
105+
* dstPixel[x, y] = srcPixel[x + ((componentX(x, y) - 128) * scaleX) / 256,
106+
* y + ((componentY(x, y) - 128) * scaleY) / 256)]
107+
*/
108+
109+
int dx = x + (getComponent(map.at<cv::Vec3b>(x, y), componentX) - 128) * scaleX / 256;
110+
if (dx < 0) dx = 0;
111+
if (dx >= output.rows) dx = output.rows;
112+
113+
int dy = y + (getComponent(map.at<cv::Vec3b>(x, y), componentY) - 128) * scaleY / 256;
114+
if (dy < 0) dy = 0;
115+
if (dy >= output.cols) dy = output.cols;
116+
117+
output.at<cv::Vec4b>(x, y) = target.at<cv::Vec4b>(dx, dy);
118+
}
119+
}
120+
121+
122+
int main(int argc, char* argv[])
123+
{
124+
// Load input map (colored, 3-channel, BGR)
125+
cv::Mat map = cv::imread("map.jpg");
126+
if (map.empty())
127+
{
128+
std::cout << "!!! Failed imread() #1" << std::endl;
129+
return -1;
130+
}
131+
std::cout << "map size: " << map.cols << "x" << map.rows << " channels:" << map.channels() << " type:" << map.type() << std::endl;
132+
133+
// Load input target (colored, 4-channel, BGRA)
134+
cv::Mat target = cv::imread("target.png", -1);
135+
if (target.empty())
136+
{
137+
std::cout << "!!! Failed imread() #2" << std::endl;
138+
return -1;
139+
}
140+
std::cout << "target size: " << target.cols << "x" << target.rows << "channels: " << target.channels() << " type: " << target.type() << std::endl;
141+
142+
if (target.channels() != 4)
143+
{
144+
std::cout << "!!! A PNG image with transparent layer is required" << std::endl;
145+
return -1;
146+
}
147+
148+
if (target.size().width > map.size().width || target.size().height > map.size().height)
149+
{
150+
std::cout << "!!! Target needs to have smaller dimensions than map" << std::endl;
151+
return -1;
152+
}
153+
154+
/* Display the map as movie clip */
155+
156+
int offset_x = 0;
157+
158+
while (1)
159+
{
160+
// Crop the map (which is larger) to the size of the target image
161+
cv::Rect roi = cv::Rect(offset_x, 0, target.size().width, target.size().height);
162+
cv::Mat cropped_map = map(roi);
163+
164+
// Execute the Displacement Map Filter
165+
cv::Mat output;
166+
displacementMapFilter(cropped_map, target, 2, 2, 20, 20, output);
167+
168+
// Display the results on the screen
169+
cv::Mat frame;
170+
overlayImage(cropped_map, output, frame, cv::Point(0,0));
171+
cv::imshow("OpenCV - Displacement Map Filter", frame);
172+
173+
// Detect if ESC was pressed and quit. Frames are displayed every 33ms.
174+
char key = cv::waitKey(33);
175+
if (key == 27)
176+
break;
177+
178+
// You can increase the value of offset_x to play the animation faster
179+
offset_x += 3;
180+
if ((map.size().width - target.size().width) <= offset_x)
181+
break;
182+
}
183+
184+
return 0;
185+
}

cvDisplacementMapFilter/map.jpg

74.3 KB
Loading

cvDisplacementMapFilter/target.png

12.5 KB
Loading

ipcQTgesture/screenshot.png

-439 KB
Binary file not shown.

0 commit comments

Comments
 (0)