/
mykmeans.cpp
251 lines (200 loc) · 8.01 KB
/
mykmeans.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "mygraph.cpp"
#include <iostream>
#include <algorithm>
#include "opencv2/highgui/highgui.hpp"
struct User {
int chosenX;
int chosenY;
Mat clusteredImg; //for testing (doesnt get used for computations)
Mat skinImg; //img containing desired background/skin color
Mat destImg; //img to edit background & replace w/ skin color
Vec3b chosenColor;
bool colorPicked;
};
//finds the max number of vecs that are equal and returns it.
//if none are the same as eachother then returns null
Vec3b findMaxCommon(Vec3b v1, Vec3b v2, Vec3b v3, Vec3b v4) {
bool v1v2 = areEqual(v1, v2);
bool v1v3 = areEqual(v1, v3);
bool v1v4 = areEqual(v1, v2);
bool v2v3 = areEqual(v1, v3);
bool v2v4 = areEqual(v1, v2);
bool v3v4 = areEqual(v1, v3);
if (v1v2 && v1v3 && v1v4) { //all equal
return v1;
} else if ((v1v2 && v1v3) || (v1v2 && v1v4) || (v1v3 && v1v4)) { //3 are equal (all including v1)
return v1;
}
if (v2v3 && v2v4) {
return v2;
}
//just pairs
if (v1v2 || v1v3 || v1v4) {
return v1;
} else if (v2v3 || v2v4) {
return v2;
} else if (v3v4) {
return v3;
}
return v1; //no matching pairs: default is v1
}
// void CallBackFunction2(int event, int x, int y, int s, void* user) {
// if (event == EVENT_LBUTTONDOWN) {
// std::cout<<"mouse clicked - this position : (" << x << " , " << y << ") " << std::endl;
// ((struct User*)user)->chosenColor = (((struct User*)user)->destImg).at<Vec3b>(y,x);
// std::cout<<"color here : "<< ((struct User*)user)->chosenColor << std::endl;
// ((struct User*)user)->chosenX = x;
// ((struct User*)user)->chosenY = y;
// ((struct User*)user)->colorPicked = true;
// }
// return;
// }
void replaceBackground(Mat clusteredImg, Mat src, Vec3b backgroundColor, Vec3b newColor, String imgName) {
Mat new_image = clusteredImg;
//remove backgroundColor
int leftX = src.rows-1;
for( int y = 0; y < src.rows; y++ ) {
//starting from left side
for( int x = 0; x < src.cols; x++ ) {
if (areEqual(new_image.at<Vec3b>(y,x), backgroundColor)) {
new_image.at<Vec3b>(y,x)[0] = newColor[0];
new_image.at<Vec3b>(y,x)[1] = newColor[1];
new_image.at<Vec3b>(y,x)[2] = newColor[2];
} else {
leftX = x;
break;
}
}
//not background
//check from other side of row now
//starting from right side
int difference = 0;
for (int z = src.cols-1; z >= leftX; z-- ) {
if (areEqual(new_image.at<Vec3b>(y,z), backgroundColor)) {
new_image.at<Vec3b>(y,z)[0] = newColor[0];
new_image.at<Vec3b>(y,z)[1] = newColor[1];
new_image.at<Vec3b>(y,z)[2] = newColor[2];
} else {
//found other side of object
difference = z - leftX;
break;
}
}
//fill in between the difference w/ src image
// for (int i = leftX; i < leftX+difference; i++ ) {
// new_image.at<Vec3b>(y,i) = src.at<Vec3b>(y,i);
// }
}
//resize image
cv::Size newSize;
int new_width = std::min(600, src.size().width);
newSize.width = new_width;
newSize.height = new_width * src.size().height / src.size().width;
resize(new_image, new_image, newSize);
User* u = new User();
u->destImg = new_image;
namedWindow(imgName, 1);
// setMouseCallback(imgName, CallBackFunction2, u);
}
void myKmeans(User* u, String imgName) {
Vec3b newColor = u->chosenColor;
Mat src = u->destImg;
Mat samples(src.rows * src.cols, 3, CV_32F);
for( int y = 0; y < src.rows; y++ )
for( int x = 0; x < src.cols; x++ )
for( int z = 0; z < 3; z++)
samples.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y,x)[z];
int clusters = 5;
Mat labels;
int attempts = 5;
Mat centers;
kmeans(samples, clusters, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001), attempts, KMEANS_PP_CENTERS, centers );
Mat new_image( src.size(), src.type());
for( int y = 0; y < src.rows; y++ ) {
for( int x = 0; x < src.cols; x++ )
{
int cluster_idx = labels.at<int>(y + x*src.rows,0);
new_image.at<Vec3b>(y,x)[0] = centers.at<float>(cluster_idx, 0);
new_image.at<Vec3b>(y,x)[1] = centers.at<float>(cluster_idx, 1);
new_image.at<Vec3b>(y,x)[2] = centers.at<float>(cluster_idx, 2);
}
}
u->clusteredImg = new_image;
// namedWindow("clustered image", 3);
// setMouseCallback("clustered image", CallBackFunction3, u);
// imshow("clustered image", u->clusteredImg);
//determine background color(s) and remove
Vec3b colorTopLeft = new_image.at<Vec3b>(0,0);
Vec3b colorTopRight = new_image.at<Vec3b>(0, src.cols-1);
Vec3b colorBottomLeft = new_image.at<Vec3b>(src.rows-1, 0);
Vec3b colorBottomRight = new_image.at<Vec3b>(src.rows-1, src.cols-1);
Vec3b backgroundColor = findMaxCommon(colorTopLeft, colorTopRight, colorBottomLeft, colorBottomRight);
std::cout << "top:" << colorTopLeft << " " << colorTopRight << std::endl;
std::cout << "bottom:" << colorBottomLeft << " " << colorBottomRight << std::endl;
std::cout << "determined background-color: " << backgroundColor << std::endl;
//make BFS for clustered img
std::cout << "making graph now" << std::endl;
MyGraph* graph = new MyGraph(src, new_image, backgroundColor, u->chosenColor);
std::cout << "finished making graph " << std::endl;
//call BFS on background
Mat finalimg = graph->BFS_ReplaceBackground();
std::cout << "finished doing all BFS's " << std::endl;
//finally, resize image
cv::Size newSize;
int new_width = std::min(600, src.size().width);
newSize.width = new_width;
newSize.height = new_width * src.size().height / src.size().width;
resize(finalimg, finalimg, newSize);
u->destImg = finalimg;
namedWindow(imgName, 1);
// setMouseCallback(imgName, CallBackFunction2, u);
// std::cout<<"background color: " << backgroundColor<<std::endl;
cv::imwrite("../new_images2/finalimg-leather2.jpg", finalimg);
imshow(imgName, finalimg);
// old, linear replace background alg
//replaceBackground(new_image, src, backgroundColor, newColor, "new image");
}
// void CallBackFunction(int event, int x, int y, int s, void* user) {
// if (event == EVENT_LBUTTONDOWN) {
// std::cout<<"mouse clicked - position : (" << x << " , " << y << ") " << std::endl;
// ((struct User*)user)->chosenColor = (((struct User*)user)->skinImg).at<Vec3b>(y,x);
// ((struct User*)user)->chosenX = x;
// ((struct User*)user)->chosenY = y;
// ((struct User*)user)->colorPicked = true;
// myKmeans((struct User*)user, "new image");
// }
// return;
// }
int main( int argc, char** argv)
{
User* u = new User();
u->colorPicked = false;
Mat skinImg = imread (argv[1], 1);
u->skinImg = skinImg;
Mat destImg = imread( argv[2], 1 );
u->destImg = destImg;
u->chosenColor=(u->skinImg).at<Vec3b>(0,0);
u->colorPicked = true;
myKmeans(u, "new image");
//namedWindow("pick skincolor", 0);
//setMouseCallback("pick skincolor", CallBackFunction, u);
//imshow("pick skincolor", skinImg);
// maybe perform gaussian blur on the outline of the foreground?
// 1. find outline
// 2. gaussian blur
// 3. what about the pockets?
//
// issue with custom color picking: different lighting in personal photos - hard to determine skin tone
// have presets?
//FINDING LOOPS:
// fill in background w/ replacement color by doing BFS from corner that is the determined background color
// while doing BFS, put pixel coords that are not background color into a list. (non-background pixels list)
//
// then iterate through all un-replaced/non-background pixels, and for each of these, perform BFS to see if the clump of background color pixels
// are larger than, say, 81 square pixels (9x9pi). while doing this BFS, add each to a temporary list of coords.
// if clump is large enough, remove all coords in temp list from the list of non-background pixels. Fill non-background pixels with
// pixels from src image, and all clumps of bakground in the foreground w/ the chosen background color
waitKey( 0 );
}