相似图像搜索(算法) [原创]
使用方法:先执行dir *.jpg *.bmp /b /s > imgcmp.lst,然后替换掉原来的imgcmp.lst双击执行imgcmp.exe.
哈哈windows目录下面相同或者相似的文件还是蛮多的嘛.
阈值是相关度判断是否是相似图片的阈值,块大小是图片的分块像素大小.
可执行文件 下载:
下面是截图欣赏:
1
RBT
<
Pair
<
String,String
>
>
img_map;
2 Int g_limit = 2 ;
3 Int g_block = 32 ;
4
5 RGBQUAD pixel_comp(CxImage * pImageSrc, CxImage * pImageDst,Int & nResult,Byte limit = 20 )
6 {
7 Int src_row = pImageSrc -> GetHeight();
8 Int src_col = pImageSrc -> GetWidth();
9 Byte * src_buffer = pImageSrc -> GetBits();
10 Int dst_row = pImageDst -> GetHeight();
11 Int dst_col = pImageDst -> GetWidth();
12 Byte * dst_buffer = pImageDst -> GetBits();
13 RGBQUAD ret = { - 1 , - 1 , - 1 , - 1 }, max_ret = { 0 };
14 if (src_row != dst_row || src_col != dst_col)
15 {
16 nResult = (dst_row + 2 ) * (dst_col + 1 ) - (src_row + 2 ) * (src_col + 1 );
17 return ret;
18 }
19
20 Double r = 0 ,g = 0 ,b = 0 ,s = 0 ;
21 Double abs_r = 0 ,abs_g = 0 ,abs_b = 0 ,abs_s = 0 ;
22 Char delta_r = 0 , delta_g = 0 , delta_b = 0 ;
23 Int Block_Row = g_block;
24 Int Block_Col = g_block;
25 #define Abs(x) ((x)>=0?(x):-(x))
26 for (Int i = 0 ; i < (src_row + Block_Row - 1 ) / Block_Row; i ++ )
27 {
28 for (Int j = 0 ; j < (src_col + Block_Col - 1 ) / Block_Col; j ++ )
29 {
30 Double l_r = 0 ,l_g = 0 ,l_b = 0 ,l_s = 0 ;
31 Double l_abs_r = 0 ,l_abs_g = 0 ,l_abs_b = 0 ,l_abs_s = 0 ;
32 for (Int k = 0 ; k < Block_Row; k ++ )
33 {
34 for (Int l = 0 ; l < Block_Col; l ++ )
35 {
36 register Int row = i * Block_Row + k;
37 register Int idx = j * Block_Col + l;
38 if (row >= src_row || idx >= src_col) continue ;
39 idx += row * src_col;
40 delta_b = dst_buffer[idx * 3 ] - src_buffer[idx * 3 ];
41 delta_g = dst_buffer[idx * 3 + 1 ] - src_buffer[idx * 3 + 1 ];
42 delta_r = dst_buffer[idx * 3 + 2 ] - src_buffer[idx * 3 + 2 ];
43
44 l_b += (delta_b);
45 l_g += (delta_g);
46 l_r += (delta_r);
47
48 l_abs_b += Abs(delta_b);
49 l_abs_g += Abs(delta_g);
50 l_abs_r += Abs(delta_r);
51 }
52 }
53 if ( (max_ret.rgbBlue = (Byte)l_abs_b / Block_Row / Block_Col) > limit ||
54 (max_ret.rgbGreen = (Byte)l_abs_g / Block_Row / Block_Col) > limit ||
55 (max_ret.rgbRed = (Byte)l_abs_r / Block_Row / Block_Col) > limit )
56 {
57 return max_ret;
58 }
59
60 b += l_b;
61 g += l_g;
62 r += l_r;
63
64 abs_b += l_abs_b;
65 abs_g += l_abs_g;
66 abs_r += l_abs_r;
67 }
68 }
69 #undef Abs
70 max_ret.rgbRed = (Byte)(abs_r / src_row / src_col);
71 max_ret.rgbGreen = (Byte)(abs_g / src_row / src_col);
72 max_ret.rgbBlue = (Byte)(abs_b / src_row / src_col);
73 nResult = (Int)((r + g + b) / src_row / src_col);
74 return max_ret;
75 }
76
77 template < typename T >
78 T jabs(T t)
79 {
80 if ((t | ((T)( - 1 ) >> 1 )) == (T)( - 1 ))
81 return 0 - t;
82 else
83 return t;
84 }
85
86 Int symbol(Int p)
87 {
88 return p >= 0 ? p: - p;
89 }
90
91 CStr next_sub(CStr pchSrc, Char match = ' \\ ' )
92 {
93 int len = strlen(pchSrc);
94 for ( int i = len - 1 ; i >= 0 ; i -- )
95 {
96 if (pchSrc[i] == match)
97 return pchSrc + i + 1 ;
98 }
99 return 0 ;
100 }
101
102 Int img_comp(CStr pchFileSrc,CxImage * img_src,CStr pchFileDst,CxImage * img_dst,Byte limit = 2 )
103 {
104 CStr tmp_str = Nul;
105 if (strcmp(pchFileSrc,pchFileDst) == 0 ) return 0 ;
106 if ( ! img_src -> IsValid())
107 {
108 printf( " parse image (%s) failure!\n " ,pchFileSrc);
109 throw " parse image failure! " ;
110 }
111 if ( ! img_dst -> IsValid())
112 {
113 printf( " parse image (%s) failure!\n " ,pchFileDst);
114 throw " parse image failure! " ;
115 }
116 Int nResult;
117 RGBQUAD t = pixel_comp(img_src,img_dst,nResult);
118
119 if ( * (Int * ) & t == - 1 ) // 长宽不同,则按名字排序
120 return (Int)nResult;
121 else if (jabs(t.rgbBlue) < limit && jabs(t.rgbGreen) < limit && jabs(t.rgbRed) < limit && jabs(t.rgbReserved) < limit)
122 {
123 if (strcmp(pchFileDst,pchFileSrc) != 0 )
124 {
125 img_map.Insert(Pair < String,String > (String(pchFileSrc),String(pchFileDst)));
126 img_map.Insert(Pair < String,String > (String(pchFileDst),String(pchFileSrc)));
127 }
128 return 0 ;
129 }
130 else
131 return (Int)nResult;
132 }
133
134 class ImageCmp
135 {
136 public :
137 Bool operator < (ImageCmp & img)
138 {
139 try {
140 return img_comp(_img_src.ConvertToCStr(),_img,img._img_src.ConvertToCStr(),img._img,g_limit) < 0 ;
141 } catch (){ return False;}
142 }
143 CStr GetSrc() { return _img_src.ConvertToCStr(); }
144 ImageCmp(CStr pchImgSrc) : _img_src(pchImgSrc)
145 {
146 _img = New CxImage(pchImgSrc,CxImage::GetTypeIdFromName(next_sub(pchImgSrc, ' . ' )));
147 }
148 ImageCmp( const String & sImgSrc) : _img_src(sImgSrc)
149 {
150 CStr tmp_str = Nul;
151 _img = New CxImage(sImgSrc.ConvertToCStr(),CxImage::GetTypeIdFromName(next_sub(sImgSrc.ConvertToCStr(), ' . ' )));
152 }
153 ImageCmp() {_img = New CxImage();}
154 AutoPtr < CxImage > _img;
155 protected :
156 String _img_src;
157 };
158
159 int main()
160 {
161 printf( " 阈值: " );
162 scanf( " %d " , & g_limit);
163 printf( " 块大小: " );
164 scanf( " %d " , & g_block);
165 CStr name_cfg = " imgcmp.lst " ;
166 Char buffer[MAX_PATH + 1 ] = { 0 };
167 Vector < ImageCmp > vec_file;
168 FILE * file_cfg = fopen(name_cfg, " r " );
169 printf( " loading. " );
170 if (file_cfg)
171 {
172 String last;
173 while ( ! feof(file_cfg))
174 {
175 String tmp;
176 fgets(buffer,MAX_PATH,file_cfg);
177 tmp = buffer;
178 tmp.Trim();
179 if ( ! tmp.IsEmpty() && tmp != last)
180 {
181 vec_file.PushBack(tmp);
182 printf( " . " );
183 }
184 last = tmp;
185 }
186 fclose(file_cfg);
187 file_cfg = Nul;
188 }
189 printf( " \nParsing\n " );
190
191 fast_sort < ImageCmp,Vector < ImageCmp > > (vec_file, 0 ,vec_file.GetSize() - 1 );
192 printf( " results:\n " );
193
194 FILE * html = fopen( " output.html " , " w " );
195 fprintf(html, " <HTML><HEAD><TITLE>图片相似度搜索</TITLE></HEAD><BODY>\n " );
196 Bool has_line_draw = False;
197 String last_name;
198 for (UInt i = 0 ; i < vec_file.GetSize(); i ++ )
199 {
200 RBT < Pair < String,String > > ::Iterator iter = img_map.Search(vec_file[i].GetSrc());
201 if (iter)
202 {
203 if ( ! last_name.IsEmpty())
204 {
205 Bool found = False;
206 RBT < String > src_map,dst_map;
207 typedef RBT < String > ::Iterator map_iter;
208 for (RBT < Pair < String,String > > ::Iterator iter = img_map.Search(last_name);iter;iter = img_map.Search(( * iter).GetVal()))
209 {
210 map_iter it = src_map.Search(( * iter).GetKey());
211 if (it)
212 {
213 break ;
214 }
215 src_map.Insert(( * iter).GetKey());
216 }
217 for (RBT < Pair < String,String > > ::Iterator iter = img_map.Search(vec_file[i].GetSrc());iter;iter = img_map.Search(( * iter).GetVal()))
218 {
219 if (src_map.Search(( * iter).GetKey()))
220 {
221 found = True;
222 break ;
223 }
224 map_iter it = dst_map.Search(( * iter).GetKey());
225 if (it)
226 {
227 break ;
228 }
229 dst_map.Insert(( * iter).GetKey());
230 }
231 if ( ! found)
232 {
233 fprintf(html, " <hr>\n " );
234 }
235 }
236 printf( " %s Same with %s \n " ,vec_file[i].GetSrc(),( * iter).GetVal());
237 fprintf(html, " <a href='file:///%s'>%s</a><br>\n " ,vec_file[i].GetSrc(),vec_file[i].GetSrc());
238 has_line_draw = False;
239 last_name = vec_file[i].GetSrc();
240 }
241 }
242 fprintf(html, " </body></html> " );
243 fclose(html);
244 ShellExecute(Nul, " open " , " output.html " ,Nul,Nul,SW_SHOW);
245 return 0 ;
246 }
247
2 Int g_limit = 2 ;
3 Int g_block = 32 ;
4
5 RGBQUAD pixel_comp(CxImage * pImageSrc, CxImage * pImageDst,Int & nResult,Byte limit = 20 )
6 {
7 Int src_row = pImageSrc -> GetHeight();
8 Int src_col = pImageSrc -> GetWidth();
9 Byte * src_buffer = pImageSrc -> GetBits();
10 Int dst_row = pImageDst -> GetHeight();
11 Int dst_col = pImageDst -> GetWidth();
12 Byte * dst_buffer = pImageDst -> GetBits();
13 RGBQUAD ret = { - 1 , - 1 , - 1 , - 1 }, max_ret = { 0 };
14 if (src_row != dst_row || src_col != dst_col)
15 {
16 nResult = (dst_row + 2 ) * (dst_col + 1 ) - (src_row + 2 ) * (src_col + 1 );
17 return ret;
18 }
19
20 Double r = 0 ,g = 0 ,b = 0 ,s = 0 ;
21 Double abs_r = 0 ,abs_g = 0 ,abs_b = 0 ,abs_s = 0 ;
22 Char delta_r = 0 , delta_g = 0 , delta_b = 0 ;
23 Int Block_Row = g_block;
24 Int Block_Col = g_block;
25 #define Abs(x) ((x)>=0?(x):-(x))
26 for (Int i = 0 ; i < (src_row + Block_Row - 1 ) / Block_Row; i ++ )
27 {
28 for (Int j = 0 ; j < (src_col + Block_Col - 1 ) / Block_Col; j ++ )
29 {
30 Double l_r = 0 ,l_g = 0 ,l_b = 0 ,l_s = 0 ;
31 Double l_abs_r = 0 ,l_abs_g = 0 ,l_abs_b = 0 ,l_abs_s = 0 ;
32 for (Int k = 0 ; k < Block_Row; k ++ )
33 {
34 for (Int l = 0 ; l < Block_Col; l ++ )
35 {
36 register Int row = i * Block_Row + k;
37 register Int idx = j * Block_Col + l;
38 if (row >= src_row || idx >= src_col) continue ;
39 idx += row * src_col;
40 delta_b = dst_buffer[idx * 3 ] - src_buffer[idx * 3 ];
41 delta_g = dst_buffer[idx * 3 + 1 ] - src_buffer[idx * 3 + 1 ];
42 delta_r = dst_buffer[idx * 3 + 2 ] - src_buffer[idx * 3 + 2 ];
43
44 l_b += (delta_b);
45 l_g += (delta_g);
46 l_r += (delta_r);
47
48 l_abs_b += Abs(delta_b);
49 l_abs_g += Abs(delta_g);
50 l_abs_r += Abs(delta_r);
51 }
52 }
53 if ( (max_ret.rgbBlue = (Byte)l_abs_b / Block_Row / Block_Col) > limit ||
54 (max_ret.rgbGreen = (Byte)l_abs_g / Block_Row / Block_Col) > limit ||
55 (max_ret.rgbRed = (Byte)l_abs_r / Block_Row / Block_Col) > limit )
56 {
57 return max_ret;
58 }
59
60 b += l_b;
61 g += l_g;
62 r += l_r;
63
64 abs_b += l_abs_b;
65 abs_g += l_abs_g;
66 abs_r += l_abs_r;
67 }
68 }
69 #undef Abs
70 max_ret.rgbRed = (Byte)(abs_r / src_row / src_col);
71 max_ret.rgbGreen = (Byte)(abs_g / src_row / src_col);
72 max_ret.rgbBlue = (Byte)(abs_b / src_row / src_col);
73 nResult = (Int)((r + g + b) / src_row / src_col);
74 return max_ret;
75 }
76
77 template < typename T >
78 T jabs(T t)
79 {
80 if ((t | ((T)( - 1 ) >> 1 )) == (T)( - 1 ))
81 return 0 - t;
82 else
83 return t;
84 }
85
86 Int symbol(Int p)
87 {
88 return p >= 0 ? p: - p;
89 }
90
91 CStr next_sub(CStr pchSrc, Char match = ' \\ ' )
92 {
93 int len = strlen(pchSrc);
94 for ( int i = len - 1 ; i >= 0 ; i -- )
95 {
96 if (pchSrc[i] == match)
97 return pchSrc + i + 1 ;
98 }
99 return 0 ;
100 }
101
102 Int img_comp(CStr pchFileSrc,CxImage * img_src,CStr pchFileDst,CxImage * img_dst,Byte limit = 2 )
103 {
104 CStr tmp_str = Nul;
105 if (strcmp(pchFileSrc,pchFileDst) == 0 ) return 0 ;
106 if ( ! img_src -> IsValid())
107 {
108 printf( " parse image (%s) failure!\n " ,pchFileSrc);
109 throw " parse image failure! " ;
110 }
111 if ( ! img_dst -> IsValid())
112 {
113 printf( " parse image (%s) failure!\n " ,pchFileDst);
114 throw " parse image failure! " ;
115 }
116 Int nResult;
117 RGBQUAD t = pixel_comp(img_src,img_dst,nResult);
118
119 if ( * (Int * ) & t == - 1 ) // 长宽不同,则按名字排序
120 return (Int)nResult;
121 else if (jabs(t.rgbBlue) < limit && jabs(t.rgbGreen) < limit && jabs(t.rgbRed) < limit && jabs(t.rgbReserved) < limit)
122 {
123 if (strcmp(pchFileDst,pchFileSrc) != 0 )
124 {
125 img_map.Insert(Pair < String,String > (String(pchFileSrc),String(pchFileDst)));
126 img_map.Insert(Pair < String,String > (String(pchFileDst),String(pchFileSrc)));
127 }
128 return 0 ;
129 }
130 else
131 return (Int)nResult;
132 }
133
134 class ImageCmp
135 {
136 public :
137 Bool operator < (ImageCmp & img)
138 {
139 try {
140 return img_comp(_img_src.ConvertToCStr(),_img,img._img_src.ConvertToCStr(),img._img,g_limit) < 0 ;
141 } catch (){ return False;}
142 }
143 CStr GetSrc() { return _img_src.ConvertToCStr(); }
144 ImageCmp(CStr pchImgSrc) : _img_src(pchImgSrc)
145 {
146 _img = New CxImage(pchImgSrc,CxImage::GetTypeIdFromName(next_sub(pchImgSrc, ' . ' )));
147 }
148 ImageCmp( const String & sImgSrc) : _img_src(sImgSrc)
149 {
150 CStr tmp_str = Nul;
151 _img = New CxImage(sImgSrc.ConvertToCStr(),CxImage::GetTypeIdFromName(next_sub(sImgSrc.ConvertToCStr(), ' . ' )));
152 }
153 ImageCmp() {_img = New CxImage();}
154 AutoPtr < CxImage > _img;
155 protected :
156 String _img_src;
157 };
158
159 int main()
160 {
161 printf( " 阈值: " );
162 scanf( " %d " , & g_limit);
163 printf( " 块大小: " );
164 scanf( " %d " , & g_block);
165 CStr name_cfg = " imgcmp.lst " ;
166 Char buffer[MAX_PATH + 1 ] = { 0 };
167 Vector < ImageCmp > vec_file;
168 FILE * file_cfg = fopen(name_cfg, " r " );
169 printf( " loading. " );
170 if (file_cfg)
171 {
172 String last;
173 while ( ! feof(file_cfg))
174 {
175 String tmp;
176 fgets(buffer,MAX_PATH,file_cfg);
177 tmp = buffer;
178 tmp.Trim();
179 if ( ! tmp.IsEmpty() && tmp != last)
180 {
181 vec_file.PushBack(tmp);
182 printf( " . " );
183 }
184 last = tmp;
185 }
186 fclose(file_cfg);
187 file_cfg = Nul;
188 }
189 printf( " \nParsing\n " );
190
191 fast_sort < ImageCmp,Vector < ImageCmp > > (vec_file, 0 ,vec_file.GetSize() - 1 );
192 printf( " results:\n " );
193
194 FILE * html = fopen( " output.html " , " w " );
195 fprintf(html, " <HTML><HEAD><TITLE>图片相似度搜索</TITLE></HEAD><BODY>\n " );
196 Bool has_line_draw = False;
197 String last_name;
198 for (UInt i = 0 ; i < vec_file.GetSize(); i ++ )
199 {
200 RBT < Pair < String,String > > ::Iterator iter = img_map.Search(vec_file[i].GetSrc());
201 if (iter)
202 {
203 if ( ! last_name.IsEmpty())
204 {
205 Bool found = False;
206 RBT < String > src_map,dst_map;
207 typedef RBT < String > ::Iterator map_iter;
208 for (RBT < Pair < String,String > > ::Iterator iter = img_map.Search(last_name);iter;iter = img_map.Search(( * iter).GetVal()))
209 {
210 map_iter it = src_map.Search(( * iter).GetKey());
211 if (it)
212 {
213 break ;
214 }
215 src_map.Insert(( * iter).GetKey());
216 }
217 for (RBT < Pair < String,String > > ::Iterator iter = img_map.Search(vec_file[i].GetSrc());iter;iter = img_map.Search(( * iter).GetVal()))
218 {
219 if (src_map.Search(( * iter).GetKey()))
220 {
221 found = True;
222 break ;
223 }
224 map_iter it = dst_map.Search(( * iter).GetKey());
225 if (it)
226 {
227 break ;
228 }
229 dst_map.Insert(( * iter).GetKey());
230 }
231 if ( ! found)
232 {
233 fprintf(html, " <hr>\n " );
234 }
235 }
236 printf( " %s Same with %s \n " ,vec_file[i].GetSrc(),( * iter).GetVal());
237 fprintf(html, " <a href='file:///%s'>%s</a><br>\n " ,vec_file[i].GetSrc(),vec_file[i].GetSrc());
238 has_line_draw = False;
239 last_name = vec_file[i].GetSrc();
240 }
241 }
242 fprintf(html, " </body></html> " );
243 fclose(html);
244 ShellExecute(Nul, " open " , " output.html " ,Nul,Nul,SW_SHOW);
245 return 0 ;
246 }
247