Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
O
opencv
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
opencv
Commits
6bb9342a
Commit
6bb9342a
authored
May 30, 2013
by
Vadim Pisarevsky
Committed by
OpenCV Buildbot
May 30, 2013
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #918 from bitwangyaoyao:2.4_samples
parents
5a4efe8b
fad96b95
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
938 additions
and
125 deletions
+938
-125
aloe-L.png
samples/ocl/aloe-L.png
+0
-0
aloe-R.png
samples/ocl/aloe-R.png
+0
-0
aloe-disp.png
samples/ocl/aloe-disp.png
+0
-0
facedetect.cpp
samples/ocl/facedetect.cpp
+167
-123
hog.cpp
samples/ocl/hog.cpp
+62
-2
pyrlk_optical_flow.cpp
samples/ocl/pyrlk_optical_flow.cpp
+290
-0
stereo_match.cpp
samples/ocl/stereo_match.cpp
+419
-0
No files found.
samples/ocl/aloe-L.png
deleted
100644 → 0
View file @
5a4efe8b
720 KB
samples/ocl/aloe-R.png
deleted
100644 → 0
View file @
5a4efe8b
722 KB
samples/ocl/aloe-disp.png
deleted
100644 → 0
View file @
5a4efe8b
59 KB
samples/ocl/facedetect.cpp
View file @
6bb9342a
//This sample is inherited from facedetect.cpp in smaple/c
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
...
...
@@ -9,78 +7,84 @@
using
namespace
std
;
using
namespace
cv
;
#define LOOP_NUM 10
const
static
Scalar
colors
[]
=
{
CV_RGB
(
0
,
0
,
255
),
CV_RGB
(
0
,
128
,
255
),
CV_RGB
(
0
,
255
,
255
),
CV_RGB
(
0
,
255
,
0
),
CV_RGB
(
255
,
128
,
0
),
CV_RGB
(
255
,
255
,
0
),
CV_RGB
(
255
,
0
,
0
),
CV_RGB
(
255
,
0
,
255
)}
;
static
void
help
()
int64
work_begin
=
0
;
int64
work_end
=
0
;
static
void
workBegin
()
{
work_begin
=
getTickCount
();
}
static
void
workEnd
()
{
cout
<<
"
\n
This program demonstrates the cascade recognizer.
\n
"
"This classifier can recognize many ~rigid objects, it's most known use is for faces.
\n
"
"Usage:
\n
"
"./facedetect [--cascade=<cascade_path> this is the primary trained classifier such as frontal face]
\n
"
" [--scale=<image scale greater or equal to 1, try 1.3 for example>
\n
"
" [filename|camera_index]
\n\n
"
"see facedetect.cmd for one call:
\n
"
"./facedetect --cascade=
\"
../../data/haarcascades/haarcascade_frontalface_alt.xml
\"
--scale=1.3
\n
"
"Hit any key to quit.
\n
"
"Using OpenCV version "
<<
CV_VERSION
<<
"
\n
"
<<
endl
;
work_end
+=
(
getTickCount
()
-
work_begin
);
}
struct
getRect
{
Rect
operator
()(
const
CvAvgComp
&
e
)
const
{
return
e
.
rect
;
}
};
void
detectAndDraw
(
Mat
&
img
,
cv
::
ocl
::
OclCascadeClassifier
&
cascade
,
CascadeClassifier
&
nestedCascade
,
double
scale
);
static
double
getTime
(){
return
work_end
/
((
double
)
cvGetTickFrequency
()
*
1000.
);
}
void
detect
(
Mat
&
img
,
vector
<
Rect
>&
faces
,
cv
::
ocl
::
OclCascadeClassifierBuf
&
cascade
,
double
scale
,
bool
calTime
);
String
cascadeName
=
"../../../data/haarcascades/haarcascade_frontalface_alt.xml"
;
void
detectCPU
(
Mat
&
img
,
vector
<
Rect
>&
faces
,
CascadeClassifier
&
cascade
,
double
scale
,
bool
calTime
);
void
Draw
(
Mat
&
img
,
vector
<
Rect
>&
faces
,
double
scale
);
// This function test if gpu_rst matches cpu_rst.
// If the two vectors are not equal, it will return the difference in vector size
// Else if will return (total diff of each cpu and gpu rects covered pixels)/(total cpu rects covered pixels)
double
checkRectSimilarity
(
Size
sz
,
std
::
vector
<
Rect
>&
cpu_rst
,
std
::
vector
<
Rect
>&
gpu_rst
);
int
main
(
int
argc
,
const
char
**
argv
)
{
CvCapture
*
capture
=
0
;
Mat
frame
,
frameCopy
,
image
;
const
String
scaleOpt
=
"--scale="
;
size_t
scaleOptLen
=
scaleOpt
.
length
();
const
String
cascadeOpt
=
"--cascade="
;
size_t
cascadeOptLen
=
cascadeOpt
.
length
();
String
inputName
;
help
();
cv
::
ocl
::
OclCascadeClassifier
cascade
;
CascadeClassifier
nestedCascade
;
double
scale
=
1
;
for
(
int
i
=
1
;
i
<
argc
;
i
++
)
const
char
*
keys
=
"{ h | help | false | print help message }"
"{ i | input | | specify input image }"
"{ t | template | ../../../data/haarcascades/haarcascade_frontalface_alt.xml | specify template file }"
"{ c | scale | 1.0 | scale image }"
"{ s | use_cpu | false | use cpu or gpu to process the image }"
;
CommandLineParser
cmd
(
argc
,
argv
,
keys
);
if
(
cmd
.
get
<
bool
>
(
"help"
))
{
cout
<<
"Processing "
<<
i
<<
" "
<<
argv
[
i
]
<<
endl
;
if
(
cascadeOpt
.
compare
(
0
,
cascadeOptLen
,
argv
[
i
],
cascadeOptLen
)
==
0
)
{
cascadeName
.
assign
(
argv
[
i
]
+
cascadeOptLen
);
cout
<<
" from which we have cascadeName= "
<<
cascadeName
<<
endl
;
}
else
if
(
scaleOpt
.
compare
(
0
,
scaleOptLen
,
argv
[
i
],
scaleOptLen
)
==
0
)
{
if
(
!
sscanf
(
argv
[
i
]
+
scaleOpt
.
length
(),
"%lf"
,
&
scale
)
||
scale
<
1
)
scale
=
1
;
cout
<<
" from which we read scale = "
<<
scale
<<
endl
;
}
else
if
(
argv
[
i
][
0
]
==
'-'
)
{
cerr
<<
"WARNING: Unknown option %s"
<<
argv
[
i
]
<<
endl
;
}
else
inputName
.
assign
(
argv
[
i
]
);
cout
<<
"Avaible options:"
<<
endl
;
cmd
.
printParams
();
return
0
;
}
CvCapture
*
capture
=
0
;
Mat
frame
,
frameCopy
,
image
;
if
(
!
cascade
.
load
(
cascadeName
)
)
bool
useCPU
=
cmd
.
get
<
bool
>
(
"s"
);
string
inputName
=
cmd
.
get
<
string
>
(
"i"
);
string
cascadeName
=
cmd
.
get
<
string
>
(
"t"
);
double
scale
=
cmd
.
get
<
double
>
(
"c"
);
cv
::
ocl
::
OclCascadeClassifierBuf
cascade
;
CascadeClassifier
cpu_cascade
;
if
(
!
cascade
.
load
(
cascadeName
)
||
!
cpu_cascade
.
load
(
cascadeName
)
)
{
cerr
<<
"ERROR: Could not load classifier cascade"
<<
endl
;
cerr
<<
"Usage: facedetect [--cascade=<cascade_path>]
\n
"
" [--scale[=<image scale>
\n
"
" [filename|camera_index]
\n
"
<<
endl
;
return
-
1
;
}
if
(
inputName
.
empty
()
||
(
isdigit
(
inputName
.
c_str
()[
0
])
&&
inputName
.
c_str
()[
1
]
==
'\0'
)
)
if
(
inputName
.
empty
()
)
{
capture
=
cvCaptureFromCAM
(
inputName
.
empty
()
?
0
:
inputName
.
c_str
()[
0
]
-
'0'
);
i
nt
c
=
inputName
.
empty
()
?
0
:
inputName
.
c_str
()[
0
]
-
'0'
;
if
(
!
capture
)
cout
<<
"Capture from CAM "
<<
c
<<
"
didn't work"
<<
endl
;
capture
=
cvCaptureFromCAM
(
0
);
i
f
(
!
capture
)
cout
<<
"Capture from CAM 0
didn't work"
<<
endl
;
}
else
if
(
inputName
.
size
()
)
{
...
...
@@ -88,26 +92,30 @@ int main( int argc, const char** argv )
if
(
image
.
empty
()
)
{
capture
=
cvCaptureFromAVI
(
inputName
.
c_str
()
);
if
(
!
capture
)
cout
<<
"Capture from AVI didn't work"
<<
endl
;
if
(
!
capture
)
cout
<<
"Capture from AVI didn't work"
<<
endl
;
return
-
1
;
}
}
else
{
image
=
imread
(
"lena.jpg"
,
1
);
if
(
image
.
empty
())
cout
<<
"Couldn't read lena.jpg"
<<
endl
;
if
(
image
.
empty
())
cout
<<
"Couldn't read lena.jpg"
<<
endl
;
return
-
1
;
}
cvNamedWindow
(
"result"
,
1
);
std
::
vector
<
cv
::
ocl
::
Info
>
oclinfo
;
int
devnums
=
cv
::
ocl
::
getDevice
(
oclinfo
);
if
(
devnums
<
1
)
if
(
devnums
<
1
)
{
std
::
cout
<<
"no device found
\n
"
;
return
-
1
;
}
//if you want to use undefault device, set it here
//setDevice(oclinfo[0]);
//setBinpath(CLBINPATH
);
ocl
::
setBinpath
(
"./"
);
if
(
capture
)
{
cout
<<
"In capture ..."
<<
endl
;
...
...
@@ -115,15 +123,20 @@ int main( int argc, const char** argv )
{
IplImage
*
iplImg
=
cvQueryFrame
(
capture
);
frame
=
iplImg
;
vector
<
Rect
>
faces
;
if
(
frame
.
empty
()
)
break
;
if
(
iplImg
->
origin
==
IPL_ORIGIN_TL
)
frame
.
copyTo
(
frameCopy
);
else
flip
(
frame
,
frameCopy
,
0
);
detectAndDraw
(
frameCopy
,
cascade
,
nestedCascade
,
scale
);
if
(
useCPU
){
detectCPU
(
frameCopy
,
faces
,
cpu_cascade
,
scale
,
false
);
}
else
{
detect
(
frameCopy
,
faces
,
cascade
,
scale
,
false
);
}
Draw
(
frameCopy
,
faces
,
scale
);
if
(
waitKey
(
10
)
>=
0
)
goto
_cleanup_
;
}
...
...
@@ -136,42 +149,34 @@ _cleanup_:
else
{
cout
<<
"In image read"
<<
endl
;
if
(
!
image
.
empty
()
)
{
detectAndDraw
(
image
,
cascade
,
nestedCascade
,
scale
);
waitKey
(
0
);
}
else
if
(
!
inputName
.
empty
()
)
vector
<
Rect
>
faces
;
vector
<
Rect
>
ref_rst
;
double
accuracy
=
0.
;
for
(
int
i
=
0
;
i
<=
LOOP_NUM
;
i
++
)
{
/* assume it is a text file containing the
list of the image filenames to be processed - one per line */
FILE
*
f
=
fopen
(
inputName
.
c_str
(),
"rt"
);
if
(
f
)
cout
<<
"loop"
<<
i
<<
endl
;
if
(
useCPU
){
detectCPU
(
image
,
faces
,
cpu_cascade
,
scale
,
i
==
0
?
false
:
true
);
}
else
{
detect
(
image
,
faces
,
cascade
,
scale
,
i
==
0
?
false
:
true
);
if
(
i
==
0
){
detectCPU
(
image
,
ref_rst
,
cpu_cascade
,
scale
,
false
);
accuracy
=
checkRectSimilarity
(
image
.
size
(),
ref_rst
,
faces
);
}
}
if
(
i
==
LOOP_NUM
)
{
char
buf
[
1000
+
1
];
while
(
fgets
(
buf
,
1000
,
f
)
)
{
int
len
=
(
int
)
strlen
(
buf
),
c
;
while
(
len
>
0
&&
isspace
(
buf
[
len
-
1
])
)
len
--
;
buf
[
len
]
=
'\0'
;
cout
<<
"file "
<<
buf
<<
endl
;
image
=
imread
(
buf
,
1
);
if
(
!
image
.
empty
()
)
{
detectAndDraw
(
image
,
cascade
,
nestedCascade
,
scale
);
c
=
waitKey
(
0
);
if
(
c
==
27
||
c
==
'q'
||
c
==
'Q'
)
break
;
}
else
{
cerr
<<
"Aw snap, couldn't read image "
<<
buf
<<
endl
;
}
}
fclose
(
f
);
if
(
useCPU
)
cout
<<
"average CPU time (noCamera) : "
;
else
cout
<<
"average GPU time (noCamera) : "
;
cout
<<
getTime
()
/
LOOP_NUM
<<
" ms"
<<
endl
;
cout
<<
"accuracy value: "
<<
accuracy
<<
endl
;
}
}
Draw
(
image
,
faces
,
scale
);
waitKey
(
0
);
}
cvDestroyWindow
(
"result"
);
...
...
@@ -179,44 +184,44 @@ _cleanup_:
return
0
;
}
void
detect
AndDraw
(
Mat
&
img
,
cv
::
ocl
::
OclCascadeClassifier
&
cascade
,
CascadeClassifier
&
,
double
scale
)
void
detect
(
Mat
&
img
,
vector
<
Rect
>&
faces
,
cv
::
ocl
::
OclCascadeClassifier
Buf
&
cascade
,
double
scale
,
bool
calTime
)
{
int
i
=
0
;
double
t
=
0
;
vector
<
Rect
>
faces
;
const
static
Scalar
colors
[]
=
{
CV_RGB
(
0
,
0
,
255
),
CV_RGB
(
0
,
128
,
255
),
CV_RGB
(
0
,
255
,
255
),
CV_RGB
(
0
,
255
,
0
),
CV_RGB
(
255
,
128
,
0
),
CV_RGB
(
255
,
255
,
0
),
CV_RGB
(
255
,
0
,
0
),
CV_RGB
(
255
,
0
,
255
)}
;
cv
::
ocl
::
oclMat
image
(
img
);
cv
::
ocl
::
oclMat
gray
,
smallImg
(
cvRound
(
img
.
rows
/
scale
),
cvRound
(
img
.
cols
/
scale
),
CV_8UC1
);
if
(
calTime
)
workBegin
();
cv
::
ocl
::
cvtColor
(
image
,
gray
,
CV_BGR2GRAY
);
cv
::
ocl
::
resize
(
gray
,
smallImg
,
smallImg
.
size
(),
0
,
0
,
INTER_LINEAR
);
cv
::
ocl
::
equalizeHist
(
smallImg
,
smallImg
);
CvSeq
*
_objects
;
MemStorage
storage
(
cvCreateMemStorage
(
0
));
t
=
(
double
)
cvGetTickCount
();
_objects
=
cascade
.
oclHaarDetectObjects
(
smallImg
,
storage
,
1.1
,
cascade
.
detectMultiScale
(
smallImg
,
faces
,
1.1
,
3
,
0
|
CV_HAAR_SCALE_IMAGE
,
Size
(
30
,
30
),
Size
(
0
,
0
)
);
vector
<
CvAvgComp
>
vecAvgComp
;
Seq
<
CvAvgComp
>
(
_objects
).
copyTo
(
vecAvgComp
);
faces
.
resize
(
vecAvgComp
.
size
());
std
::
transform
(
vecAvgComp
.
begin
(),
vecAvgComp
.
end
(),
faces
.
begin
(),
getRect
());
t
=
(
double
)
cvGetTickCount
()
-
t
;
printf
(
"detection time = %g ms
\n
"
,
t
/
((
double
)
cvGetTickFrequency
()
*
1000.
)
);
if
(
calTime
)
workEnd
();
}
void
detectCPU
(
Mat
&
img
,
vector
<
Rect
>&
faces
,
CascadeClassifier
&
cascade
,
double
scale
,
bool
calTime
)
{
if
(
calTime
)
workBegin
();
Mat
cpu_gray
,
cpu_smallImg
(
cvRound
(
img
.
rows
/
scale
),
cvRound
(
img
.
cols
/
scale
),
CV_8UC1
);
cvtColor
(
img
,
cpu_gray
,
CV_BGR2GRAY
);
resize
(
cpu_gray
,
cpu_smallImg
,
cpu_smallImg
.
size
(),
0
,
0
,
INTER_LINEAR
);
equalizeHist
(
cpu_smallImg
,
cpu_smallImg
);
cascade
.
detectMultiScale
(
cpu_smallImg
,
faces
,
1.1
,
3
,
0
|
CV_HAAR_SCALE_IMAGE
,
Size
(
30
,
30
),
Size
(
0
,
0
));
if
(
calTime
)
workEnd
();
}
void
Draw
(
Mat
&
img
,
vector
<
Rect
>&
faces
,
double
scale
)
{
int
i
=
0
;
for
(
vector
<
Rect
>::
const_iterator
r
=
faces
.
begin
();
r
!=
faces
.
end
();
r
++
,
i
++
)
{
Mat
smallImgROI
;
Point
center
;
Scalar
color
=
colors
[
i
%
8
];
int
radius
;
...
...
@@ -227,3 +232,42 @@ void detectAndDraw( Mat& img,
}
cv
::
imshow
(
"result"
,
img
);
}
double
checkRectSimilarity
(
Size
sz
,
std
::
vector
<
Rect
>&
ob1
,
std
::
vector
<
Rect
>&
ob2
)
{
double
final_test_result
=
0.0
;
size_t
sz1
=
ob1
.
size
();
size_t
sz2
=
ob2
.
size
();
if
(
sz1
!=
sz2
)
return
sz1
>
sz2
?
(
double
)(
sz1
-
sz2
)
:
(
double
)(
sz2
-
sz1
);
else
{
cv
::
Mat
cpu_result
(
sz
,
CV_8UC1
);
cpu_result
.
setTo
(
0
);
for
(
vector
<
Rect
>::
const_iterator
r
=
ob1
.
begin
();
r
!=
ob1
.
end
();
r
++
)
{
cv
::
Mat
cpu_result_roi
(
cpu_result
,
*
r
);
cpu_result_roi
.
setTo
(
1
);
cpu_result
.
copyTo
(
cpu_result
);
}
int
cpu_area
=
cv
::
countNonZero
(
cpu_result
>
0
);
cv
::
Mat
gpu_result
(
sz
,
CV_8UC1
);
gpu_result
.
setTo
(
0
);
for
(
vector
<
Rect
>::
const_iterator
r2
=
ob2
.
begin
();
r2
!=
ob2
.
end
();
r2
++
)
{
cv
::
Mat
gpu_result_roi
(
gpu_result
,
*
r2
);
gpu_result_roi
.
setTo
(
1
);
gpu_result
.
copyTo
(
gpu_result
);
}
cv
::
Mat
result_
;
multiply
(
cpu_result
,
gpu_result
,
result_
);
int
result
=
cv
::
countNonZero
(
result_
>
0
);
final_test_result
=
1.0
-
(
double
)
result
/
(
double
)
cpu_area
;
}
return
final_test_result
;
}
samples/ocl/hog.cpp
View file @
6bb9342a
...
...
@@ -45,7 +45,6 @@ public:
bool
gamma_corr
;
};
class
App
{
public
:
...
...
@@ -64,6 +63,13 @@ public:
string
message
()
const
;
// This function test if gpu_rst matches cpu_rst.
// If the two vectors are not equal, it will return the difference in vector size
// Else if will return
// (total diff of each cpu and gpu rects covered pixels)/(total cpu rects covered pixels)
double
checkRectSimilarity
(
Size
sz
,
std
::
vector
<
Rect
>&
cpu_rst
,
std
::
vector
<
Rect
>&
gpu_rst
);
private
:
App
operator
=
(
App
&
);
...
...
@@ -290,6 +296,7 @@ void App::run()
ocl
::
oclMat
gpu_img
;
// Iterate over all frames
bool
verify
=
false
;
while
(
running
&&
!
frame
.
empty
())
{
workBegin
();
...
...
@@ -316,7 +323,18 @@ void App::run()
gpu_img
.
upload
(
img
);
gpu_hog
.
detectMultiScale
(
gpu_img
,
found
,
hit_threshold
,
win_stride
,
Size
(
0
,
0
),
scale
,
gr_threshold
);
}
if
(
!
verify
)
{
// verify if GPU output same objects with CPU at 1st run
verify
=
true
;
vector
<
Rect
>
ref_rst
;
cvtColor
(
img
,
img
,
CV_BGRA2BGR
);
cpu_hog
.
detectMultiScale
(
img
,
ref_rst
,
hit_threshold
,
win_stride
,
Size
(
0
,
0
),
scale
,
gr_threshold
-
2
);
double
accuracy
=
checkRectSimilarity
(
img
.
size
(),
ref_rst
,
found
);
cout
<<
"
\n
accuracy value: "
<<
accuracy
<<
endl
;
}
}
else
cpu_hog
.
detectMultiScale
(
img
,
found
,
hit_threshold
,
win_stride
,
Size
(
0
,
0
),
scale
,
gr_threshold
);
hogWorkEnd
();
...
...
@@ -457,3 +475,45 @@ inline string App::workFps() const
return
ss
.
str
();
}
double
App
::
checkRectSimilarity
(
Size
sz
,
std
::
vector
<
Rect
>&
ob1
,
std
::
vector
<
Rect
>&
ob2
)
{
double
final_test_result
=
0.0
;
size_t
sz1
=
ob1
.
size
();
size_t
sz2
=
ob2
.
size
();
if
(
sz1
!=
sz2
)
return
sz1
>
sz2
?
(
double
)(
sz1
-
sz2
)
:
(
double
)(
sz2
-
sz1
);
else
{
cv
::
Mat
cpu_result
(
sz
,
CV_8UC1
);
cpu_result
.
setTo
(
0
);
for
(
vector
<
Rect
>::
const_iterator
r
=
ob1
.
begin
();
r
!=
ob1
.
end
();
r
++
)
{
cv
::
Mat
cpu_result_roi
(
cpu_result
,
*
r
);
cpu_result_roi
.
setTo
(
1
);
cpu_result
.
copyTo
(
cpu_result
);
}
int
cpu_area
=
cv
::
countNonZero
(
cpu_result
>
0
);
cv
::
Mat
gpu_result
(
sz
,
CV_8UC1
);
gpu_result
.
setTo
(
0
);
for
(
vector
<
Rect
>::
const_iterator
r2
=
ob2
.
begin
();
r2
!=
ob2
.
end
();
r2
++
)
{
cv
::
Mat
gpu_result_roi
(
gpu_result
,
*
r2
);
gpu_result_roi
.
setTo
(
1
);
gpu_result
.
copyTo
(
gpu_result
);
}
cv
::
Mat
result_
;
multiply
(
cpu_result
,
gpu_result
,
result_
);
int
result
=
cv
::
countNonZero
(
result_
>
0
);
final_test_result
=
1.0
-
(
double
)
result
/
(
double
)
cpu_area
;
}
return
final_test_result
;
}
samples/ocl/pyrlk_optical_flow.cpp
0 → 100644
View file @
6bb9342a
#include <iostream>
#include <vector>
#include <iomanip>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/ocl/ocl.hpp"
#include "opencv2/video/video.hpp"
using
namespace
std
;
using
namespace
cv
;
using
namespace
cv
::
ocl
;
typedef
unsigned
char
uchar
;
#define LOOP_NUM 10
int64
work_begin
=
0
;
int64
work_end
=
0
;
static
void
workBegin
()
{
work_begin
=
getTickCount
();
}
static
void
workEnd
()
{
work_end
+=
(
getTickCount
()
-
work_begin
);
}
static
double
getTime
(){
return
work_end
*
1000.
/
getTickFrequency
();
}
static
void
download
(
const
oclMat
&
d_mat
,
vector
<
Point2f
>&
vec
)
{
vec
.
resize
(
d_mat
.
cols
);
Mat
mat
(
1
,
d_mat
.
cols
,
CV_32FC2
,
(
void
*
)
&
vec
[
0
]);
d_mat
.
download
(
mat
);
}
static
void
download
(
const
oclMat
&
d_mat
,
vector
<
uchar
>&
vec
)
{
vec
.
resize
(
d_mat
.
cols
);
Mat
mat
(
1
,
d_mat
.
cols
,
CV_8UC1
,
(
void
*
)
&
vec
[
0
]);
d_mat
.
download
(
mat
);
}
static
void
drawArrows
(
Mat
&
frame
,
const
vector
<
Point2f
>&
prevPts
,
const
vector
<
Point2f
>&
nextPts
,
const
vector
<
uchar
>&
status
,
Scalar
line_color
=
Scalar
(
0
,
0
,
255
))
{
for
(
size_t
i
=
0
;
i
<
prevPts
.
size
();
++
i
)
{
if
(
status
[
i
])
{
int
line_thickness
=
1
;
Point
p
=
prevPts
[
i
];
Point
q
=
nextPts
[
i
];
double
angle
=
atan2
((
double
)
p
.
y
-
q
.
y
,
(
double
)
p
.
x
-
q
.
x
);
double
hypotenuse
=
sqrt
(
(
double
)(
p
.
y
-
q
.
y
)
*
(
p
.
y
-
q
.
y
)
+
(
double
)(
p
.
x
-
q
.
x
)
*
(
p
.
x
-
q
.
x
)
);
if
(
hypotenuse
<
1.0
)
continue
;
// Here we lengthen the arrow by a factor of three.
q
.
x
=
(
int
)
(
p
.
x
-
3
*
hypotenuse
*
cos
(
angle
));
q
.
y
=
(
int
)
(
p
.
y
-
3
*
hypotenuse
*
sin
(
angle
));
// Now we draw the main line of the arrow.
line
(
frame
,
p
,
q
,
line_color
,
line_thickness
);
// Now draw the tips of the arrow. I do some scaling so that the
// tips look proportional to the main line of the arrow.
p
.
x
=
(
int
)
(
q
.
x
+
9
*
cos
(
angle
+
CV_PI
/
4
));
p
.
y
=
(
int
)
(
q
.
y
+
9
*
sin
(
angle
+
CV_PI
/
4
));
line
(
frame
,
p
,
q
,
line_color
,
line_thickness
);
p
.
x
=
(
int
)
(
q
.
x
+
9
*
cos
(
angle
-
CV_PI
/
4
));
p
.
y
=
(
int
)
(
q
.
y
+
9
*
sin
(
angle
-
CV_PI
/
4
));
line
(
frame
,
p
,
q
,
line_color
,
line_thickness
);
}
}
}
int
main
(
int
argc
,
const
char
*
argv
[])
{
static
std
::
vector
<
Info
>
ocl_info
;
ocl
::
getDevice
(
ocl_info
);
//if you want to use undefault device, set it here
setDevice
(
ocl_info
[
0
]);
//set this to save kernel compile time from second time you run
ocl
::
setBinpath
(
"./"
);
const
char
*
keys
=
"{ h | help | false | print help message }"
"{ l | left | | specify left image }"
"{ r | right | | specify right image }"
"{ c | camera | 0 | enable camera capturing }"
"{ s | use_cpu | false | use cpu or gpu to process the image }"
"{ v | video | | use video as input }"
"{ points | points | 1000 | specify points count [GoodFeatureToTrack] }"
"{ min_dist | min_dist | 0 | specify minimal distance between points [GoodFeatureToTrack] }"
;
CommandLineParser
cmd
(
argc
,
argv
,
keys
);
if
(
cmd
.
get
<
bool
>
(
"help"
))
{
cout
<<
"Usage: pyrlk_optical_flow [options]"
<<
endl
;
cout
<<
"Avaible options:"
<<
endl
;
cmd
.
printParams
();
return
0
;
}
bool
defaultPicturesFail
=
false
;
string
fname0
=
cmd
.
get
<
string
>
(
"left"
);
string
fname1
=
cmd
.
get
<
string
>
(
"right"
);
string
vdofile
=
cmd
.
get
<
string
>
(
"video"
);
int
points
=
cmd
.
get
<
int
>
(
"points"
);
double
minDist
=
cmd
.
get
<
double
>
(
"min_dist"
);
bool
useCPU
=
cmd
.
get
<
bool
>
(
"s"
);
bool
useCamera
=
cmd
.
get
<
bool
>
(
"c"
);
int
inputName
=
cmd
.
get
<
int
>
(
"c"
);
oclMat
d_nextPts
,
d_status
;
Mat
frame0
=
imread
(
fname0
,
cv
::
IMREAD_GRAYSCALE
);
Mat
frame1
=
imread
(
fname1
,
cv
::
IMREAD_GRAYSCALE
);
PyrLKOpticalFlow
d_pyrLK
;
vector
<
cv
::
Point2f
>
pts
;
vector
<
cv
::
Point2f
>
nextPts
;
vector
<
unsigned
char
>
status
;
vector
<
float
>
err
;
if
(
frame0
.
empty
()
||
frame1
.
empty
())
{
useCamera
=
true
;
defaultPicturesFail
=
true
;
CvCapture
*
capture
=
0
;
capture
=
cvCaptureFromCAM
(
inputName
);
if
(
!
capture
)
{
cout
<<
"Can't load input images"
<<
endl
;
return
-
1
;
}
}
cout
<<
"Points count : "
<<
points
<<
endl
<<
endl
;
if
(
useCamera
)
{
CvCapture
*
capture
=
0
;
Mat
frame
,
frameCopy
;
Mat
frame0Gray
,
frame1Gray
;
Mat
ptr0
,
ptr1
;
if
(
vdofile
==
""
)
capture
=
cvCaptureFromCAM
(
inputName
);
else
capture
=
cvCreateFileCapture
(
vdofile
.
c_str
());
int
c
=
inputName
;
if
(
!
capture
)
{
if
(
vdofile
==
""
)
cout
<<
"Capture from CAM "
<<
c
<<
" didn't work"
<<
endl
;
else
cout
<<
"Capture from file "
<<
vdofile
<<
" failed"
<<
endl
;
if
(
defaultPicturesFail
)
{
return
-
1
;
}
goto
nocamera
;
}
cout
<<
"In capture ..."
<<
endl
;
for
(
int
i
=
0
;;
i
++
)
{
frame
=
cvQueryFrame
(
capture
);
if
(
frame
.
empty
()
)
break
;
if
(
i
==
0
)
{
frame
.
copyTo
(
frame0
);
cvtColor
(
frame0
,
frame0Gray
,
COLOR_BGR2GRAY
);
}
else
{
if
(
i
%
2
==
1
)
{
frame
.
copyTo
(
frame1
);
cvtColor
(
frame1
,
frame1Gray
,
COLOR_BGR2GRAY
);
ptr0
=
frame0Gray
;
ptr1
=
frame1Gray
;
}
else
{
frame
.
copyTo
(
frame0
);
cvtColor
(
frame0
,
frame0Gray
,
COLOR_BGR2GRAY
);
ptr0
=
frame1Gray
;
ptr1
=
frame0Gray
;
}
pts
.
clear
();
cv
::
goodFeaturesToTrack
(
ptr0
,
pts
,
points
,
0.01
,
0.0
);
if
(
pts
.
size
()
==
0
)
{
continue
;
}
if
(
useCPU
)
{
cv
::
calcOpticalFlowPyrLK
(
ptr0
,
ptr1
,
pts
,
nextPts
,
status
,
err
);
}
else
{
oclMat
d_prevPts
(
1
,
points
,
CV_32FC2
,
(
void
*
)
&
pts
[
0
]);
d_pyrLK
.
sparse
(
oclMat
(
ptr0
),
oclMat
(
ptr1
),
d_prevPts
,
d_nextPts
,
d_status
);
download
(
d_prevPts
,
pts
);
download
(
d_nextPts
,
nextPts
);
download
(
d_status
,
status
);
}
if
(
i
%
2
==
1
)
frame1
.
copyTo
(
frameCopy
);
else
frame0
.
copyTo
(
frameCopy
);
drawArrows
(
frameCopy
,
pts
,
nextPts
,
status
,
Scalar
(
255
,
0
,
0
));
imshow
(
"PyrLK [Sparse]"
,
frameCopy
);
}
if
(
waitKey
(
10
)
>=
0
)
goto
_cleanup_
;
}
waitKey
(
0
);
_cleanup_
:
cvReleaseCapture
(
&
capture
);
}
else
{
nocamera
:
for
(
int
i
=
0
;
i
<=
LOOP_NUM
;
i
++
)
{
cout
<<
"loop"
<<
i
<<
endl
;
if
(
i
>
0
)
workBegin
();
cv
::
goodFeaturesToTrack
(
frame0
,
pts
,
points
,
0.01
,
minDist
);
if
(
useCPU
)
{
cv
::
calcOpticalFlowPyrLK
(
frame0
,
frame1
,
pts
,
nextPts
,
status
,
err
);
}
else
{
oclMat
d_prevPts
(
1
,
points
,
CV_32FC2
,
(
void
*
)
&
pts
[
0
]);
d_pyrLK
.
sparse
(
oclMat
(
frame0
),
oclMat
(
frame1
),
d_prevPts
,
d_nextPts
,
d_status
);
download
(
d_prevPts
,
pts
);
download
(
d_nextPts
,
nextPts
);
download
(
d_status
,
status
);
}
if
(
i
>
0
&&
i
<=
LOOP_NUM
)
workEnd
();
if
(
i
==
LOOP_NUM
)
{
if
(
useCPU
)
cout
<<
"average CPU time (noCamera) : "
;
else
cout
<<
"average GPU time (noCamera) : "
;
cout
<<
getTime
()
/
LOOP_NUM
<<
" ms"
<<
endl
;
drawArrows
(
frame0
,
pts
,
nextPts
,
status
,
Scalar
(
255
,
0
,
0
));
imshow
(
"PyrLK [Sparse]"
,
frame0
);
}
}
}
waitKey
();
return
0
;
}
samples/ocl/stereo_match.cpp
0 → 100644
View file @
6bb9342a
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <stdexcept>
#include "opencv2/ocl/ocl.hpp"
#include "opencv2/highgui/highgui.hpp"
using
namespace
cv
;
using
namespace
std
;
using
namespace
ocl
;
bool
help_showed
=
false
;
struct
Params
{
Params
();
static
Params
read
(
int
argc
,
char
**
argv
);
string
left
;
string
right
;
string
method_str
()
const
{
switch
(
method
)
{
case
BM
:
return
"BM"
;
case
BP
:
return
"BP"
;
case
CSBP
:
return
"CSBP"
;
}
return
""
;
}
enum
{
BM
,
BP
,
CSBP
}
method
;
int
ndisp
;
// Max disparity + 1
enum
{
GPU
,
CPU
}
type
;
};
struct
App
{
App
(
const
Params
&
p
);
void
run
();
void
handleKey
(
char
key
);
void
printParams
()
const
;
void
workBegin
()
{
work_begin
=
getTickCount
();
}
void
workEnd
()
{
int64
d
=
getTickCount
()
-
work_begin
;
double
f
=
getTickFrequency
();
work_fps
=
f
/
d
;
}
string
text
()
const
{
stringstream
ss
;
ss
<<
"("
<<
p
.
method_str
()
<<
") FPS: "
<<
setiosflags
(
ios
::
left
)
<<
setprecision
(
4
)
<<
work_fps
;
return
ss
.
str
();
}
private
:
Params
p
;
bool
running
;
Mat
left_src
,
right_src
;
Mat
left
,
right
;
oclMat
d_left
,
d_right
;
StereoBM_OCL
bm
;
StereoBeliefPropagation
bp
;
StereoConstantSpaceBP
csbp
;
int64
work_begin
;
double
work_fps
;
};
static
void
printHelp
()
{
cout
<<
"Usage: stereo_match_gpu
\n
"
<<
"
\t
--left <left_view> --right <right_view> # must be rectified
\n
"
<<
"
\t
--method <stereo_match_method> # BM | BP | CSBP
\n
"
<<
"
\t
--ndisp <number> # number of disparity levels
\n
"
<<
"
\t
--type <device_type> # cpu | CPU | gpu | GPU
\n
"
;
help_showed
=
true
;
}
int
main
(
int
argc
,
char
**
argv
)
{
try
{
if
(
argc
<
2
)
{
printHelp
();
return
1
;
}
Params
args
=
Params
::
read
(
argc
,
argv
);
if
(
help_showed
)
return
-
1
;
int
flags
[
2
]
=
{
CVCL_DEVICE_TYPE_GPU
,
CVCL_DEVICE_TYPE_CPU
};
vector
<
Info
>
info
;
if
(
getDevice
(
info
,
flags
[
args
.
type
])
==
0
)
{
throw
runtime_error
(
"Error: Did not find a valid OpenCL device!"
);
}
cout
<<
"Device name:"
<<
info
[
0
].
DeviceName
[
0
]
<<
endl
;
App
app
(
args
);
app
.
run
();
}
catch
(
const
exception
&
e
)
{
cout
<<
"error: "
<<
e
.
what
()
<<
endl
;
}
return
0
;
}
Params
::
Params
()
{
method
=
BM
;
ndisp
=
64
;
type
=
GPU
;
}
Params
Params
::
read
(
int
argc
,
char
**
argv
)
{
Params
p
;
for
(
int
i
=
1
;
i
<
argc
;
i
++
)
{
if
(
string
(
argv
[
i
])
==
"--left"
)
p
.
left
=
argv
[
++
i
];
else
if
(
string
(
argv
[
i
])
==
"--right"
)
p
.
right
=
argv
[
++
i
];
else
if
(
string
(
argv
[
i
])
==
"--method"
)
{
if
(
string
(
argv
[
i
+
1
])
==
"BM"
)
p
.
method
=
BM
;
else
if
(
string
(
argv
[
i
+
1
])
==
"BP"
)
p
.
method
=
BP
;
else
if
(
string
(
argv
[
i
+
1
])
==
"CSBP"
)
p
.
method
=
CSBP
;
else
throw
runtime_error
(
"unknown stereo match method: "
+
string
(
argv
[
i
+
1
]));
i
++
;
}
else
if
(
string
(
argv
[
i
])
==
"--ndisp"
)
p
.
ndisp
=
atoi
(
argv
[
++
i
]);
else
if
(
string
(
argv
[
i
])
==
"--type"
)
{
string
t
(
argv
[
++
i
]);
if
(
t
==
"cpu"
||
t
==
"CPU"
)
{
p
.
type
=
CPU
;
}
else
if
(
t
==
"gpu"
||
t
==
"GPU"
)
{
p
.
type
=
GPU
;
}
else
throw
runtime_error
(
"unknown device type: "
+
t
);
}
else
if
(
string
(
argv
[
i
])
==
"--help"
)
printHelp
();
else
throw
runtime_error
(
"unknown key: "
+
string
(
argv
[
i
]));
}
return
p
;
}
App
::
App
(
const
Params
&
params
)
:
p
(
params
),
running
(
false
)
{
cout
<<
"stereo_match_ocl sample
\n
"
;
cout
<<
"
\n
Controls:
\n
"
<<
"
\t
esc - exit
\n
"
<<
"
\t
p - print current parameters
\n
"
<<
"
\t
g - convert source images into gray
\n
"
<<
"
\t
m - change stereo match method
\n
"
<<
"
\t
s - change Sobel prefiltering flag (for BM only)
\n
"
<<
"
\t
1/q - increase/decrease maximum disparity
\n
"
<<
"
\t
2/w - increase/decrease window size (for BM only)
\n
"
<<
"
\t
3/e - increase/decrease iteration count (for BP and CSBP only)
\n
"
<<
"
\t
4/r - increase/decrease level count (for BP and CSBP only)
\n
"
;
}
void
App
::
run
()
{
// Load images
left_src
=
imread
(
p
.
left
);
right_src
=
imread
(
p
.
right
);
if
(
left_src
.
empty
())
throw
runtime_error
(
"can't open file
\"
"
+
p
.
left
+
"
\"
"
);
if
(
right_src
.
empty
())
throw
runtime_error
(
"can't open file
\"
"
+
p
.
right
+
"
\"
"
);
cvtColor
(
left_src
,
left
,
CV_BGR2GRAY
);
cvtColor
(
right_src
,
right
,
CV_BGR2GRAY
);
d_left
.
upload
(
left
);
d_right
.
upload
(
right
);
imshow
(
"left"
,
left
);
imshow
(
"right"
,
right
);
// Set common parameters
bm
.
ndisp
=
p
.
ndisp
;
bp
.
ndisp
=
p
.
ndisp
;
csbp
.
ndisp
=
p
.
ndisp
;
cout
<<
endl
;
printParams
();
running
=
true
;
while
(
running
)
{
// Prepare disparity map of specified type
Mat
disp
;
oclMat
d_disp
;
workBegin
();
switch
(
p
.
method
)
{
case
Params
:
:
BM
:
if
(
d_left
.
channels
()
>
1
||
d_right
.
channels
()
>
1
)
{
cout
<<
"BM doesn't support color images
\n
"
;
cvtColor
(
left_src
,
left
,
CV_BGR2GRAY
);
cvtColor
(
right_src
,
right
,
CV_BGR2GRAY
);
cout
<<
"image_channels: "
<<
left
.
channels
()
<<
endl
;
d_left
.
upload
(
left
);
d_right
.
upload
(
right
);
imshow
(
"left"
,
left
);
imshow
(
"right"
,
right
);
}
bm
(
d_left
,
d_right
,
d_disp
);
break
;
case
Params
:
:
BP
:
bp
(
d_left
,
d_right
,
d_disp
);
break
;
case
Params
:
:
CSBP
:
csbp
(
d_left
,
d_right
,
d_disp
);
break
;
}
ocl
::
finish
();
workEnd
();
// Show results
d_disp
.
download
(
disp
);
if
(
p
.
method
!=
Params
::
BM
)
{
disp
.
convertTo
(
disp
,
0
);
}
putText
(
disp
,
text
(),
Point
(
5
,
25
),
FONT_HERSHEY_SIMPLEX
,
1.0
,
Scalar
::
all
(
255
));
imshow
(
"disparity"
,
disp
);
handleKey
((
char
)
waitKey
(
3
));
}
}
void
App
::
printParams
()
const
{
cout
<<
"--- Parameters ---
\n
"
;
cout
<<
"image_size: ("
<<
left
.
cols
<<
", "
<<
left
.
rows
<<
")
\n
"
;
cout
<<
"image_channels: "
<<
left
.
channels
()
<<
endl
;
cout
<<
"method: "
<<
p
.
method_str
()
<<
endl
<<
"ndisp: "
<<
p
.
ndisp
<<
endl
;
switch
(
p
.
method
)
{
case
Params
:
:
BM
:
cout
<<
"win_size: "
<<
bm
.
winSize
<<
endl
;
cout
<<
"prefilter_sobel: "
<<
bm
.
preset
<<
endl
;
break
;
case
Params
:
:
BP
:
cout
<<
"iter_count: "
<<
bp
.
iters
<<
endl
;
cout
<<
"level_count: "
<<
bp
.
levels
<<
endl
;
break
;
case
Params
:
:
CSBP
:
cout
<<
"iter_count: "
<<
csbp
.
iters
<<
endl
;
cout
<<
"level_count: "
<<
csbp
.
levels
<<
endl
;
break
;
}
cout
<<
endl
;
}
void
App
::
handleKey
(
char
key
)
{
switch
(
key
)
{
case
27
:
running
=
false
;
break
;
case
'p'
:
case
'P'
:
printParams
();
break
;
case
'g'
:
case
'G'
:
if
(
left
.
channels
()
==
1
&&
p
.
method
!=
Params
::
BM
)
{
left
=
left_src
;
right
=
right_src
;
}
else
{
cvtColor
(
left_src
,
left
,
CV_BGR2GRAY
);
cvtColor
(
right_src
,
right
,
CV_BGR2GRAY
);
}
d_left
.
upload
(
left
);
d_right
.
upload
(
right
);
cout
<<
"image_channels: "
<<
left
.
channels
()
<<
endl
;
imshow
(
"left"
,
left
);
imshow
(
"right"
,
right
);
break
;
case
'm'
:
case
'M'
:
switch
(
p
.
method
)
{
case
Params
:
:
BM
:
p
.
method
=
Params
::
BP
;
break
;
case
Params
:
:
BP
:
p
.
method
=
Params
::
CSBP
;
break
;
case
Params
:
:
CSBP
:
p
.
method
=
Params
::
BM
;
break
;
}
cout
<<
"method: "
<<
p
.
method_str
()
<<
endl
;
break
;
case
's'
:
case
'S'
:
if
(
p
.
method
==
Params
::
BM
)
{
switch
(
bm
.
preset
)
{
case
StereoBM_OCL
:
:
BASIC_PRESET
:
bm
.
preset
=
StereoBM_OCL
::
PREFILTER_XSOBEL
;
break
;
case
StereoBM_OCL
:
:
PREFILTER_XSOBEL
:
bm
.
preset
=
StereoBM_OCL
::
BASIC_PRESET
;
break
;
}
cout
<<
"prefilter_sobel: "
<<
bm
.
preset
<<
endl
;
}
break
;
case
'1'
:
p
.
ndisp
=
p
.
ndisp
==
1
?
8
:
p
.
ndisp
+
8
;
cout
<<
"ndisp: "
<<
p
.
ndisp
<<
endl
;
bm
.
ndisp
=
p
.
ndisp
;
bp
.
ndisp
=
p
.
ndisp
;
csbp
.
ndisp
=
p
.
ndisp
;
break
;
case
'q'
:
case
'Q'
:
p
.
ndisp
=
max
(
p
.
ndisp
-
8
,
1
);
cout
<<
"ndisp: "
<<
p
.
ndisp
<<
endl
;
bm
.
ndisp
=
p
.
ndisp
;
bp
.
ndisp
=
p
.
ndisp
;
csbp
.
ndisp
=
p
.
ndisp
;
break
;
case
'2'
:
if
(
p
.
method
==
Params
::
BM
)
{
bm
.
winSize
=
min
(
bm
.
winSize
+
1
,
51
);
cout
<<
"win_size: "
<<
bm
.
winSize
<<
endl
;
}
break
;
case
'w'
:
case
'W'
:
if
(
p
.
method
==
Params
::
BM
)
{
bm
.
winSize
=
max
(
bm
.
winSize
-
1
,
2
);
cout
<<
"win_size: "
<<
bm
.
winSize
<<
endl
;
}
break
;
case
'3'
:
if
(
p
.
method
==
Params
::
BP
)
{
bp
.
iters
+=
1
;
cout
<<
"iter_count: "
<<
bp
.
iters
<<
endl
;
}
else
if
(
p
.
method
==
Params
::
CSBP
)
{
csbp
.
iters
+=
1
;
cout
<<
"iter_count: "
<<
csbp
.
iters
<<
endl
;
}
break
;
case
'e'
:
case
'E'
:
if
(
p
.
method
==
Params
::
BP
)
{
bp
.
iters
=
max
(
bp
.
iters
-
1
,
1
);
cout
<<
"iter_count: "
<<
bp
.
iters
<<
endl
;
}
else
if
(
p
.
method
==
Params
::
CSBP
)
{
csbp
.
iters
=
max
(
csbp
.
iters
-
1
,
1
);
cout
<<
"iter_count: "
<<
csbp
.
iters
<<
endl
;
}
break
;
case
'4'
:
if
(
p
.
method
==
Params
::
BP
)
{
bp
.
levels
+=
1
;
cout
<<
"level_count: "
<<
bp
.
levels
<<
endl
;
}
else
if
(
p
.
method
==
Params
::
CSBP
)
{
csbp
.
levels
+=
1
;
cout
<<
"level_count: "
<<
csbp
.
levels
<<
endl
;
}
break
;
case
'r'
:
case
'R'
:
if
(
p
.
method
==
Params
::
BP
)
{
bp
.
levels
=
max
(
bp
.
levels
-
1
,
1
);
cout
<<
"level_count: "
<<
bp
.
levels
<<
endl
;
}
else
if
(
p
.
method
==
Params
::
CSBP
)
{
csbp
.
levels
=
max
(
csbp
.
levels
-
1
,
1
);
cout
<<
"level_count: "
<<
csbp
.
levels
<<
endl
;
}
break
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment