Skip to content

Commit 6a36066

Browse files
committed
First commit
1 parent 3054618 commit 6a36066

File tree

5 files changed

+448
-0
lines changed

5 files changed

+448
-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: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
#ifdef __cplusplus
8+
9+
#include <opencv2/core.hpp>
10+
11+
namespace cv {
12+
namespace ximgproc {
13+
14+
//! @addtogroup ximgproc_filters
15+
//! @{
16+
17+
/**
18+
* @brief creates a quaternion image.
19+
*
20+
* @param img Source 8-bit, 32-bit or 64-bit image, with 3-channel image.
21+
* @param qimg result CV_64FC4 a quaternion image( 4 chanels zero channel and B,G,R).
22+
*/
23+
CV_EXPORTS_W void createQuaternionImage(InputArray img, OutputArray qimg);
24+
25+
/**
26+
* @brief calculates conjugate of a quaternion image.
27+
*
28+
* @param qimg quaternion image.
29+
* @param qcimg conjugate of qimg
30+
*/
31+
CV_EXPORTS_W void qconj(InputArray qimg, OutputArray qcimg);
32+
/**
33+
* @brief divides each element by its modulus.
34+
*
35+
* @param qimg quaternion image.
36+
* @param qnimg conjugate of qimg
37+
*/
38+
CV_EXPORTS_W void qunitary(InputArray qimg, OutputArray qnimg);
39+
/**
40+
* @brief Calculates the per-element quaternion product of two arrays
41+
*
42+
* @param src1 quaternion image.
43+
* @param src2 quaternion image.
44+
* @param dst product dst(I)=src1(I) . src2(I)
45+
*/
46+
CV_EXPORTS_W void qmultiply(InputArray src1, InputArray src2, OutputArray dst);
47+
/**
48+
* @brief Performs a forward or inverse Discrete quaternion Fourier transform of a 2D quaternion array.
49+
*
50+
* @param img quaternion image.
51+
* @param qimg quaternion image in dual space.
52+
* @param flags quaternion image in dual space. only DFT_INVERSE flags is supported
53+
* @param sideLeft true the hypercomplex exponential is to be multiplied on the left (false on the right ).
54+
*/
55+
CV_EXPORTS_W void qdft(InputArray img, OutputArray qimg, int flags, bool sideLeft);
56+
/**
57+
* @brief Compares a color template against overlapped color image regions.
58+
*
59+
* @param img Image where the search is running. It must be 3 channels image
60+
* @param templ Searched template. It must be not greater than the source image and have 3 channels
61+
* @param result Map of comparison results. It must be single-channel 64-bit floating-point
62+
*/
63+
CV_EXPORTS_W void colorMatchTemplate(InputArray img, InputArray templ, OutputArray result);
64+
65+
}
66+
}
67+
#endif
68+
#endif
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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+
vector<Point> pRef;
27+
};
28+
29+
static void UpdateThreshImage(int , void *r)
30+
{
31+
SliderData *p = (SliderData*)r;
32+
Mat dst,labels,stats,centroids;
33+
34+
threshold(p->img, dst, p->thresh, 255, THRESH_BINARY);
35+
36+
connectedComponentsWithStats(dst, labels, stats, centroids, 8);
37+
if (centroids.rows < 10)
38+
{
39+
cout << "**********************************************************************************\n";
40+
for (int i = 0; i < static_cast<int>(p->pRef.size()); i++)
41+
{
42+
cout << p->pRef[i] << "\n";
43+
}
44+
cout << "---\n";
45+
for (int i = 0; i < centroids.rows; i++)
46+
{
47+
cout << dst.cols - centroids.at<double>(i, 0) << " ";
48+
cout << dst.rows - centroids.at<double>(i, 1) << "\n";
49+
}
50+
cout << "----------------------------------------------------------------------------------\n";
51+
}
52+
flip(dst, dst, -1);
53+
54+
imshow("Max Quaternion corr",dst);
55+
}
56+
57+
int main(int , char *[])
58+
{
59+
Mat imgLogo = imread("g:/lib/opencv/samples/data/opencv-logo.png", IMREAD_COLOR);
60+
Mat imgColor = imread("g:/lib/opencv/samples/data/lena.jpg", IMREAD_COLOR);
61+
// DUPLICATE OPENCV LOGO CHANGING COLOR
62+
Mat img,colorTemplate;
63+
imgLogo(Rect(0, 0, imgLogo.cols, 580)).copyTo(img);
64+
resize(img, colorTemplate, Size(), 0.05, 0.05);
65+
vector<Mat> colorMask(4);
66+
inRange(colorTemplate, Vec3b(255, 0, 0), Vec3b(255, 0, 0), colorMask[0]);
67+
inRange(colorTemplate, Vec3b(0, 255, 0), Vec3b(0,255, 0), colorMask[1]);
68+
inRange(colorTemplate, Vec3b( 0, 0,255), Vec3b( 0, 0,255), colorMask[2]);
69+
colorMask[3] = Mat(colorTemplate.size(), CV_8UC3, Scalar(255));
70+
SliderData ps;
71+
RNG r;
72+
for (int i = 0; i < 16; i++)
73+
{
74+
Point p(i / 4 * 130 + 10+r.uniform(-10,10), (i % 4) * 130 + 10+r.uniform(-10, 10));
75+
Mat newLogo= colorTemplate.clone();
76+
if (i % 6 != 5)
77+
{
78+
newLogo.setTo(Scalar(r.uniform(0, 256), r.uniform(0, 256), r.uniform(0, 256)), colorMask[i % 4]);
79+
newLogo.setTo(Scalar(r.uniform(0, 256), r.uniform(0, 256), r.uniform(0, 256)), colorMask[(i + 1) % 4]);
80+
}
81+
else
82+
ps.pRef.push_back(p);
83+
newLogo.copyTo(imgColor(Rect(p.x, p.y, newLogo.cols, newLogo.rows)));
84+
85+
}
86+
imshow("Image", imgColor);
87+
imshow("opencv_logo", colorTemplate);
88+
imwrite("g:/image.png", imgColor);
89+
Mat colorRef(imgColor.size(), imgColor.type(),Scalar::all(0));
90+
colorTemplate.copyTo(colorRef(Rect(0, 0, colorTemplate.cols, colorTemplate.rows)));
91+
imwrite("g:/opencv_logo.png", colorRef);
92+
if (img.empty())
93+
{
94+
cout << "Cannot load image file\n";
95+
return 0;
96+
}
97+
// OK NOW WHERE IS OPENCV LOGO ?
98+
Mat imgcorr;
99+
ximgproc::colorMatchTemplate(imgColor, colorTemplate, imgcorr);
100+
imshow("quaternion correlation real", imgcorr);
101+
normalize(imgcorr, imgcorr,1,0,NORM_MINMAX);
102+
imgcorr.convertTo(ps.img, CV_8U, 255);
103+
imshow("quaternion correlation", imgcorr);
104+
AddSlider("Level", "quaternion correlation", 0, 255, ps.thresh, &ps.thresh, UpdateThreshImage, &ps);
105+
int code = 0;
106+
while (code != 27)
107+
{
108+
code = waitKey(50);
109+
}
110+
111+
waitKey(0);
112+
return 0;
113+
}

modules/ximgproc/src/quaternion.cpp

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

0 commit comments

Comments
 (0)