Skip to content

Commit efa3f57

Browse files
committed
First commit
1 parent 3054618 commit efa3f57

File tree

5 files changed

+444
-0
lines changed

5 files changed

+444
-0
lines changed

modules/ximgproc/include/opencv2/ximgproc.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "ximgproc/brightedges.hpp"
5959
#include "ximgproc/run_length_morphology.hpp"
6060
#include "ximgproc/edgepreserving_filter.hpp"
61+
#include "ximgproc/color_match.hpp"
6162

6263

6364
/** @defgroup ximgproc Extended Image Processing
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#ifndef __OPENCV_COLOR_MATCH_HPP__
6+
#define __OPENCV_COLOR_MATCH_HPP__
7+
8+
#include <opencv2/core.hpp>
9+
10+
namespace cv {
11+
namespace ximgproc {
12+
13+
//! @addtogroup ximgproc_filters
14+
//! @{
15+
16+
/**
17+
* @brief creates a quaternion image.
18+
*
19+
* @param img Source 8-bit, 32-bit or 64-bit image, with 3-channel image.
20+
* @param qimg result CV_64FC4 a quaternion image( 4 chanels zero channel and B,G,R).
21+
*/
22+
CV_EXPORTS_W void createQuaternionImage(InputArray img, OutputArray qimg);
23+
24+
/**
25+
* @brief calculates conjugate of a quaternion image.
26+
*
27+
* @param qimg quaternion image.
28+
* @param qcimg conjugate of qimg
29+
*/
30+
CV_EXPORTS_W void qconj(InputArray qimg, OutputArray qcimg);
31+
/**
32+
* @brief divides each element by its modulus.
33+
*
34+
* @param qimg quaternion image.
35+
* @param qnimg conjugate of qimg
36+
*/
37+
CV_EXPORTS_W void qunitary(InputArray qimg, OutputArray qnimg);
38+
/**
39+
* @brief Calculates the per-element quaternion product of two arrays
40+
*
41+
* @param src1 quaternion image.
42+
* @param src2 quaternion image.
43+
* @param dst product dst(I)=src1(I) . src2(I)
44+
*/
45+
CV_EXPORTS_W void qmultiply(InputArray src1, InputArray src2, OutputArray dst);
46+
/**
47+
* @brief Performs a forward or inverse Discrete quaternion Fourier transform of a 2D quaternion array.
48+
*
49+
* @param img quaternion image.
50+
* @param qimg quaternion image in dual space.
51+
* @param flags quaternion image in dual space. only DFT_INVERSE flags is supported
52+
* @param sideLeft true the hypercomplex exponential is to be multiplied on the left (false on the right ).
53+
*/
54+
CV_EXPORTS_W void qdft(InputArray img, OutputArray qimg, int flags, bool sideLeft);
55+
/**
56+
* @brief Compares a color template against overlapped color image regions.
57+
*
58+
* @param img Image where the search is running. It must be 3 channels image
59+
* @param templ Searched template. It must be not greater than the source image and have 3 channels
60+
* @param result Map of comparison results. It must be single-channel 64-bit floating-point
61+
*/
62+
CV_EXPORTS_W void colorMatchTemplate(InputArray img, InputArray templ, OutputArray result);
63+
64+
}
65+
}
66+
#endif
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#include <iostream>
2+
#include <fstream>
3+
#include <opencv2/core.hpp>
4+
#include <opencv2/core/utility.hpp>
5+
#include <opencv2/highgui.hpp>
6+
#include <opencv2/imgproc.hpp>
7+
#include <opencv2/ximgproc.hpp>
8+
#include <opencv2/ximgproc/color_match.hpp>
9+
10+
using namespace std;
11+
using namespace cv;
12+
13+
14+
15+
static void AddSlider(String sliderName, String windowName, int minSlider, int maxSlider, int valDefault, int *valSlider, void(*f)(int, void *), void *r)
16+
{
17+
createTrackbar(sliderName, windowName, valSlider, 1, f, r);
18+
setTrackbarMin(sliderName, windowName, minSlider);
19+
setTrackbarMax(sliderName, windowName, maxSlider);
20+
setTrackbarPos(sliderName, windowName, valDefault);
21+
}
22+
23+
struct SliderData {
24+
Mat img;
25+
int thresh;
26+
};
27+
28+
static void UpdateThreshImage(int , void *r)
29+
{
30+
SliderData *p = (SliderData*)r;
31+
Mat dst,labels,stats,centroids;
32+
33+
threshold(p->img, dst, p->thresh, 255, THRESH_BINARY);
34+
35+
connectedComponentsWithStats(dst, labels, stats, centroids, 8);
36+
if (centroids.rows < 10)
37+
{
38+
cout << "**********************************************************************************\n";
39+
for (int i = 0; i < centroids.rows; i++)
40+
{
41+
cout << dst.cols - centroids.at<double>(i, 0) << " ";
42+
cout << dst.rows - centroids.at<double>(i, 1) << "\n";
43+
}
44+
cout << "----------------------------------------------------------------------------------\n";
45+
}
46+
flip(dst, dst, -1);
47+
48+
imshow("Max Quaternion corr",dst);
49+
}
50+
51+
int main(int argc, char *argv[])
52+
{
53+
cv::CommandLineParser parser(argc, argv,
54+
"{help h | | match color image }{@colortemplate | | input color template image}{@colorimage | | input color image}");
55+
if (parser.has("help"))
56+
{
57+
parser.printMessage();
58+
return -1;
59+
}
60+
string templateName = parser.get<string>("@colortemplate");
61+
if (templateName.empty())
62+
{
63+
parser.printMessage();
64+
parser.printErrors();
65+
return -2;
66+
}
67+
string colorImageName = parser.get<string>("@colorimage");
68+
if (templateName.empty())
69+
{
70+
parser.printMessage();
71+
parser.printErrors();
72+
return -2;
73+
}
74+
Mat imgLogo = imread(templateName, IMREAD_COLOR);
75+
Mat imgColor = imread(colorImageName, IMREAD_COLOR);
76+
imshow("Image", imgColor);
77+
imshow("template", imgLogo);
78+
// OK NOW WHERE IS OPENCV LOGO ?
79+
Mat imgcorr;
80+
SliderData ps;
81+
ximgproc::colorMatchTemplate(imgColor, imgLogo, imgcorr);
82+
imshow("quaternion correlation real", imgcorr);
83+
normalize(imgcorr, imgcorr,1,0,NORM_MINMAX);
84+
imgcorr.convertTo(ps.img, CV_8U, 255);
85+
imshow("quaternion correlation", imgcorr);
86+
ps.thresh = 0;
87+
AddSlider("Level", "quaternion correlation", 0, 255, ps.thresh, &ps.thresh, UpdateThreshImage, &ps);
88+
int code = 0;
89+
while (code != 27)
90+
{
91+
code = waitKey(50);
92+
}
93+
94+
waitKey(0);
95+
return 0;
96+
}

modules/ximgproc/src/quaternion.cpp

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.#include "precomp.hpp"
4+
#include "precomp.hpp"
5+
#include "opencv2/imgproc.hpp"
6+
#include "opencv2/ximgproc/color_match.hpp"
7+
8+
using namespace std;
9+
10+
namespace cv { namespace ximgproc {
11+
12+
void createQuaternionImage(InputArray _img, OutputArray _qimg)
13+
{
14+
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
15+
CV_CheckType(depth, depth == CV_8U || depth == CV_32F || depth == CV_64F, "Depth must be CV_8U, CV_32F or CV_64F");
16+
CV_Assert(_img.dims() == 2 && cn == 3);
17+
vector<Mat> qplane(4);
18+
vector<Mat> plane;
19+
split(_img, plane);
20+
qplane[0] = Mat::zeros(_img.size(), CV_64FC1);
21+
for (int i = 0; i < cn; i++)
22+
plane[i].convertTo(qplane[3-i], CV_64F);
23+
merge(qplane, _qimg);
24+
}
25+
26+
void qconj(InputArray _img, OutputArray _qimg)
27+
{
28+
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
29+
CV_CheckType(depth, depth == CV_32F || depth == CV_64F, "Depth must be CV_32F or CV_64F");
30+
CV_Assert(_img.dims() == 2 && cn == 4);
31+
vector<Mat> qplane(4), plane;
32+
split(_img, plane);
33+
qplane[0] = plane[0];
34+
qplane[1] = -plane[1];
35+
qplane[2] = -plane[2];
36+
qplane[3] = -plane[3];
37+
merge(qplane, _qimg);
38+
}
39+
40+
void qunitary(InputArray _img, OutputArray _qimg)
41+
{
42+
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
43+
CV_Assert((depth == CV_64F) && _img.dims() == 2 && cn == 4);
44+
vector<Mat> qplane(4), plane;
45+
split(_img, plane);
46+
_img.copyTo(_qimg);
47+
Mat qimg = _qimg.getMat();
48+
qimg.forEach<Vec4d>([](Vec4d &p, const int * /*position*/) -> void {
49+
double d = p[0] * p[0] + p[1] * p[1] + p[2] * p[2] + p[3] * p[3];
50+
d = 1 / sqrt(d);
51+
p *= d;
52+
});
53+
/*
54+
qplane[0] = plane[0].clone();
55+
qplane[1] = plane[1].clone();
56+
qplane[2] = plane[2].clone();
57+
qplane[3] = plane[3].clone();
58+
double *ptr0 = qplane[0].ptr<double>(0, 0), *ptr1 = qplane[1].ptr<double>(0, 0);
59+
double *ptr2 = qplane[2].ptr<double>(0, 0), *ptr3 = qplane[3].ptr<double>(0, 0);
60+
int nb = plane[0].rows*plane[0].cols;
61+
for (int i = 0; i < nb; i++, ptr0++, ptr1++, ptr2++, ptr3++)
62+
{
63+
double d = *ptr0 * *ptr0 + *ptr1 * *ptr1 + *ptr2 * *ptr2 + *ptr3 * *ptr3;
64+
d = 1 / sqrt(d);
65+
*ptr0 *= d;
66+
*ptr1 *= d;
67+
*ptr2 *= d;
68+
*ptr3 *= d;
69+
}
70+
merge(qplane, _qimg);*/
71+
}
72+
73+
void qdft(InputArray _img, OutputArray _qimg, int flags, bool sideLeft)
74+
{
75+
CV_INSTRUMENT_REGION();
76+
77+
int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
78+
CV_Assert(depth == CV_64F && _img.dims() == 2 && cn == 4);
79+
float c;
80+
if (sideLeft)
81+
c = 1; // Left qdft
82+
else
83+
c = -1; // right qdft
84+
85+
vector<Mat> q;
86+
Mat img;
87+
img = _img.getMat();
88+
89+
CV_Assert(getOptimalDFTSize(img.rows) == img.rows && getOptimalDFTSize(img.cols) == img.cols);
90+
91+
split(img, q);
92+
Mat c1r;
93+
Mat c1i; // Imaginary part of c1 =x'
94+
Mat c2r; // Real part of c2 =y'
95+
Mat c2i; // Imaginary part of c2=z'
96+
c1r = q[0].clone();
97+
c1i = (q[1] + q[2] + q[3]) / sqrt(3);
98+
c2r = (q[2] - q[3]) / sqrt(2);
99+
c2i = c * (q[3] + q[2] - 2 * q[1]) / sqrt(6);
100+
vector<Mat> vc1 = { c1r,c1i }, vc2 = { c2r,c2i };
101+
Mat c1, c2, C1, C2;
102+
merge(vc1, c1);
103+
merge(vc2, c2);
104+
if (flags& DFT_INVERSE)
105+
{
106+
dft(c1, C1, DFT_COMPLEX_OUTPUT | DFT_INVERSE|DFT_SCALE);
107+
dft(c2, C2, DFT_COMPLEX_OUTPUT | DFT_INVERSE | DFT_SCALE);
108+
}
109+
else
110+
{
111+
dft(c1, C1, DFT_COMPLEX_OUTPUT);
112+
dft(c2, C2, DFT_COMPLEX_OUTPUT);
113+
}
114+
split(C1, vc1);
115+
split(C2, vc2);
116+
vector<Mat> qdft(4);
117+
qdft[0] = vc1[0].clone();
118+
qdft[1] = vc1[1] / sqrt(3) - c * 2 * vc2[1] / sqrt(6);
119+
qdft[2] = vc1[1] / sqrt(3) + vc2[0] / sqrt(2) + c * vc2[1] / sqrt(6);
120+
qdft[3] = vc1[1] / sqrt(3) - vc2[0] / sqrt(2) + c * vc2[1] / sqrt(6);
121+
Mat dst0;
122+
merge(qdft, dst0);
123+
dst0.copyTo(_qimg);
124+
}
125+
126+
127+
void qmultiply(InputArray src1, InputArray src2, OutputArray dst)
128+
{
129+
int type = src1.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
130+
CV_Assert(depth == CV_64F && src1.dims() == 2 && cn == 4);
131+
type = src2.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
132+
CV_Assert(depth == CV_64F && src2.dims() == 2 && cn == 4);
133+
vector<Mat> q3(4);
134+
if (src1.rows() == src2.rows() && src1.cols() == src2.cols())
135+
{
136+
vector<Mat> q1, q2;
137+
split(src1, q1);
138+
split(src2, q2);
139+
q3[0] = q1[0].mul(q2[0]) - q1[1].mul(q2[1]) - q1[2].mul(q2[2]) - q1[3].mul(q2[3]);
140+
q3[1] = q1[0].mul(q2[1]) + q1[1].mul(q2[0]) + q1[2].mul(q2[3]) - q1[3].mul(q2[2]);
141+
q3[2] = q1[0].mul(q2[2]) - q1[1].mul(q2[3]) + q1[2].mul(q2[0]) + q1[3].mul(q2[1]);
142+
q3[3] = q1[0].mul(q2[3]) + q1[1].mul(q2[2]) - q1[2].mul(q2[1]) + q1[3].mul(q2[0]);
143+
}
144+
else if (src1.rows() == 1 && src1.cols() == 1)
145+
{
146+
vector<Mat> q2;
147+
Vec4d q1 = src1.getMat().at<Vec4d>(0, 0);
148+
split(src2, q2);
149+
q3[0] = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
150+
q3[1] = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
151+
q3[2] = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
152+
q3[3] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
153+
}
154+
else if (src2.rows() == 1 && src2.cols() == 1)
155+
{
156+
vector<Mat> q1;
157+
split(src1, q1);
158+
Vec4d q2 = src2.getMat().at<Vec4d>(0, 0);
159+
q3[0] = q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3];
160+
q3[1] = q1[0] * q2[1] + q1[1] * q2[0] + q1[2] * q2[3] - q1[3] * q2[2];
161+
q3[2] = q1[0] * q2[2] - q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
162+
q3[3] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
163+
}
164+
else
165+
CV_Assert(src1.rows() == src2.rows() && src1.cols() == src2.cols());
166+
merge(q3, dst);
167+
168+
}
169+
170+
void colorMatchTemplate(InputArray _image, InputArray _templ, OutputArray _result)
171+
{
172+
CV_INSTRUMENT_REGION();
173+
Mat image = _image.getMat(), imageF;
174+
CV_Assert(image.channels() == 3);
175+
Mat colorTemplate = _templ.getMat(), colorTemplateF;
176+
CV_Assert(colorTemplate.channels() == 3);
177+
int rr = max(getOptimalDFTSize(image.rows), getOptimalDFTSize(colorTemplate.rows));
178+
int cc = max(getOptimalDFTSize(image.cols), getOptimalDFTSize(colorTemplate.cols));
179+
Mat logo(rr, cc, CV_64FC3, Scalar::all(0));
180+
Mat img = Mat(rr, cc, CV_64FC3, Scalar::all(0));
181+
colorTemplate.convertTo(colorTemplateF, CV_64F, 1 / 256.),
182+
colorTemplateF.copyTo(logo(Rect(0, 0, colorTemplate.cols, colorTemplate.rows)));
183+
image.convertTo(imageF, CV_64F, 1 / 256.);
184+
imageF.copyTo(img(Rect(0, 0, image.cols, image.rows)));
185+
Mat qimg, qlogo;
186+
Mat qimgFFT, qimgIFFT, qlogoFFT;
187+
// Create quaternion image
188+
createQuaternionImage(img, qimg);
189+
createQuaternionImage(logo, qlogo);
190+
// quaternion fourier transform
191+
qdft(qimg, qimgFFT, 0, true);
192+
qdft(qimg, qimgIFFT, DFT_INVERSE, true);
193+
qdft(qlogo, qlogoFFT, 0, false);
194+
double sqrtnn = sqrt(static_cast<int>(qimgFFT.rows*qimgFFT.cols));
195+
qimgFFT /= sqrtnn;
196+
qimgIFFT *= sqrtnn;
197+
qlogoFFT /= sqrtnn;
198+
Mat mu(1, 1, CV_64FC4, Scalar(0, 1, 1, 1) / sqrt(3.));
199+
Mat qtmp, qlogopara, qlogoortho;
200+
qmultiply(mu, qlogoFFT, qtmp);
201+
qmultiply(qtmp, mu, qtmp);
202+
subtract(qlogoFFT, qtmp, qlogopara);
203+
qlogopara = qlogopara / 2;
204+
subtract(qlogoFFT, qlogopara, qlogoortho);
205+
Mat qcross1, qcross2, cqf, cqfi;
206+
qconj(qimgFFT, cqf);
207+
qconj(qimgIFFT, cqfi);
208+
qmultiply(cqf, qlogopara, qcross1);
209+
qmultiply(cqfi, qlogoortho, qcross2);
210+
Mat pwsp = qcross1 + qcross2;
211+
Mat crossCorr, pwspUnitary;
212+
qunitary(pwsp, pwspUnitary);
213+
qdft(pwspUnitary, crossCorr, DFT_INVERSE, false);
214+
vector<Mat> p;
215+
split(crossCorr, p);
216+
Mat imgcorr = (p[0].mul(p[0]) + p[1].mul(p[1]) + p[2].mul(p[2]) + p[3].mul(p[3]));
217+
sqrt(imgcorr, _result);
218+
}
219+
}
220+
}

0 commit comments

Comments
 (0)