├── LICENSE
├── README.md
├── adv_examples
└── L2
│ └── f3
│ └── all
│ └── 0.3 confidence
│ ├── Best example of 0 Distortion 103.01087188720703.png
│ ├── Best example of 1 Distortion 105.97237396240234.png
│ ├── Best example of 2 Distortion 94.40467834472656.png
│ ├── Best example of 3 Distortion 85.84400939941406.png
│ ├── Best example of 4 Distortion 101.89212036132812.png
│ ├── Best example of 5 Distortion 99.68110656738281.png
│ ├── Best example of 6 Distortion 75.24471282958984.png
│ ├── Best example of 7 Distortion 113.2362289428711.png
│ ├── Best example of 8 Distortion 84.97225189208984.png
│ ├── Best example of 9 Distortion 100.87386322021484.png
│ ├── Daedalus example batch.npz
│ └── Distortions of images 0 to 10.npy
├── data
└── coco_classes.txt
├── demo_plot.py
├── l0_yolov3.py
├── l2_ensemble.py
├── l2_retinanet.py
├── l2_yolov3.py
├── model
├── darknet53.py
└── yolo_model.py
└── resources
└── l2attack.jpg
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Daedalus-attack
2 | The code of our paper "Daedalus: Breaking Non-Maximum Suppression in Object Detection via Adversarial Examples".
3 |
4 | We propose an attack, in which we can tune the strength of the attack and specify the object category to attack, to break non-maximum suppression (NMS) in object detection. As the consequence, the detection model outputs extremely dense results as redundant detection boxes are not filtered by NMS.
5 |
6 | Some results are displayed here:
7 | 
8 | *Adversarial examples made by our L2 attack. The first row contains original images. The third row contains our low-confidence (0.3) adversarial examples. The fifth row contains our high-confidence (0.7) examples. The detection results from YOLO-v3 are in the rows below them. The confidence controls the density of the redundant detection boxes in the detection results.*
9 |
10 | **Launching real-world attacks via a Daedalus poster**
11 |
12 | We instantiated the Daedalus perturbation into a physical poster. You can watch the demo of the attack on YouTube:
13 | [](https://www.youtube.com/watch?v=U1LsTl8vufM)
14 | The code for generating posters against YOLO-v3 is in [this](https://github.com/NeuralSec/Daedalus-physical) repository (for academic purpose only).
15 |
16 | ---
17 |
18 | **Running the attack against YOLO-v3:**
19 |
20 | 1. Download [yolo.h5](https://1drv.ms/u/s!AqftEu9YAdEGidZ7vEm-4v4c2sV-Lw) and put it into '../model';
21 | 2. Put original images into '../Datasets/COCO/val2017/';
22 | 3. Run l2_yolov3.py.
23 |
24 | **Running the attack against RetinaNet:**
25 |
26 | 1. Install [keras-retinanet](https://github.com/fizyr/keras-retinanet);
27 | 2. Download [resnet50_coco_best_v2.1.0.h5](https://drive.google.com/file/d/1N6Xg5SOW8Ic4hpC8PoIRvggcstx0HcXw/view?usp=sharing) and put it into '../model';
28 | 3. Put original images into '../Datasets/COCO/val2017/';
29 | 4. Run l2_retinanet.py.
30 |
31 | **Running ensemble attack to craft robust adversarial examples:**
32 |
33 | Run l2_ensemble.py after completing the above setups for YOLO-v3 and RetinaNet attacks.
34 |
35 | All attacks can specify object categories to attack. Crafted adversarial examples will be stored as 416X416 sized .png files in '../adv_examples/...'. The examples can be tested on official darknet and retinanet.
36 |
37 | Cite this work:
38 |
39 | ```
40 | @article{wang2021daedalus,
41 | title={Daedalus: Breaking nonmaximum suppression in object detection via adversarial examples},
42 | author={Wang, Derui and Li, Chaoran and Wen, Sheng and Han, Qing-Long and Nepal, Surya and Zhang, Xiangyu and Xiang, Yang},
43 | journal={IEEE Transactions on Cybernetics},
44 | volume={52},
45 | number={8},
46 | pages={7427--7440},
47 | year={2021},
48 | publisher={IEEE}
49 | }
50 | ```
51 |
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 0 Distortion 103.01087188720703.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 0 Distortion 103.01087188720703.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 1 Distortion 105.97237396240234.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 1 Distortion 105.97237396240234.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 2 Distortion 94.40467834472656.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 2 Distortion 94.40467834472656.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 3 Distortion 85.84400939941406.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 3 Distortion 85.84400939941406.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 4 Distortion 101.89212036132812.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 4 Distortion 101.89212036132812.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 5 Distortion 99.68110656738281.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 5 Distortion 99.68110656738281.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 6 Distortion 75.24471282958984.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 6 Distortion 75.24471282958984.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 7 Distortion 113.2362289428711.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 7 Distortion 113.2362289428711.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 8 Distortion 84.97225189208984.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 8 Distortion 84.97225189208984.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Best example of 9 Distortion 100.87386322021484.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Best example of 9 Distortion 100.87386322021484.png
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Daedalus example batch.npz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Daedalus example batch.npz
--------------------------------------------------------------------------------
/adv_examples/L2/f3/all/0.3 confidence/Distortions of images 0 to 10.npy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/adv_examples/L2/f3/all/0.3 confidence/Distortions of images 0 to 10.npy
--------------------------------------------------------------------------------
/data/coco_classes.txt:
--------------------------------------------------------------------------------
1 | person
2 | bicycle
3 | car
4 | motorbike
5 | aeroplane
6 | bus
7 | train
8 | truck
9 | boat
10 | traffic light
11 | fire hydrant
12 | stop sign
13 | parking meter
14 | bench
15 | bird
16 | cat
17 | dog
18 | horse
19 | sheep
20 | cow
21 | elephant
22 | bear
23 | zebra
24 | giraffe
25 | backpack
26 | umbrella
27 | handbag
28 | tie
29 | suitcase
30 | frisbee
31 | skis
32 | snowboard
33 | sports ball
34 | kite
35 | baseball bat
36 | baseball glove
37 | skateboard
38 | surfboard
39 | tennis racket
40 | bottle
41 | wine glass
42 | cup
43 | fork
44 | knife
45 | spoon
46 | bowl
47 | banana
48 | apple
49 | sandwich
50 | orange
51 | broccoli
52 | carrot
53 | hot dog
54 | pizza
55 | donut
56 | cake
57 | chair
58 | sofa
59 | pottedplant
60 | bed
61 | diningtable
62 | toilet
63 | tvmonitor
64 | laptop
65 | mouse
66 | remote
67 | keyboard
68 | cell phone
69 | microwave
70 | oven
71 | toaster
72 | sink
73 | refrigerator
74 | book
75 | clock
76 | vase
77 | scissors
78 | teddy bear
79 | hair drier
80 | toothbrush
81 |
--------------------------------------------------------------------------------
/demo_plot.py:
--------------------------------------------------------------------------------
1 | import os
2 | import cv2
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | import matplotlib.gridspec as gridspec
6 |
7 | PATHS = ['adv_examples/plot/L2/f3/benign',
8 | 'adv_examples/plot/L2/f3/0.3',
9 | 'adv_examples/plot/L2/f3/0.7']
10 |
11 | RESULT_PATH = ['adv_examples/plot/L2/f3/benignresult',
12 | 'adv_examples/plot/L2/f3/0.3result',
13 | 'adv_examples/plot/L2/f3/0.7result']
14 |
15 |
16 | def plot(paths, result_paths):
17 | b_imgs = []
18 | b_r = []
19 | low_imgs = []
20 | l_r = []
21 | high_imgs =[]
22 | h_r = []
23 | for (root, dirs, files) in os.walk(paths[0]):
24 | if files:
25 | for f in files:
26 | path = os.path.join(root, f)
27 | originalimgs = cv2.imread(path) # RGB image
28 | originalimgs = cv2.cvtColor(originalimgs, cv2.COLOR_BGR2RGB)
29 | originalimgs = cv2.resize(originalimgs, (416, 416), interpolation=cv2.INTER_CUBIC)
30 | b_imgs.append(originalimgs)
31 | for (root, dirs, files) in os.walk(result_paths[0]):
32 | if files:
33 | for f in files:
34 | path = os.path.join(root, f)
35 | originalimgs = cv2.imread(path) # RGB image
36 | originalimgs = cv2.cvtColor(originalimgs, cv2.COLOR_BGR2RGB)
37 | originalimgs = cv2.resize(originalimgs, (416, 416), interpolation=cv2.INTER_CUBIC)
38 | b_r.append(originalimgs)
39 |
40 | for (root, dirs, files) in os.walk(paths[1]):
41 | if files:
42 | for f in files:
43 | path = os.path.join(root, f)
44 | lowconfimgs = cv2.imread(path) # RGB image
45 | lowconfimgs = cv2.cvtColor(lowconfimgs, cv2.COLOR_BGR2RGB)
46 | low_imgs.append(lowconfimgs)
47 | for (root, dirs, files) in os.walk(result_paths[1]):
48 | if files:
49 | for f in files:
50 | path = os.path.join(root, f)
51 | lowconfimgs = cv2.imread(path) # RGB image
52 | lowconfimgs = cv2.cvtColor(lowconfimgs, cv2.COLOR_BGR2RGB)
53 | l_r.append(lowconfimgs)
54 |
55 | for (root, dirs, files) in os.walk(paths[2]):
56 | if files:
57 | for f in files:
58 | path = os.path.join(root, f)
59 | highconfimgs = cv2.imread(path) # RGB image
60 | highconfimgs = cv2.cvtColor(highconfimgs, cv2.COLOR_BGR2RGB)
61 | high_imgs.append(highconfimgs)
62 | for (root, dirs, files) in os.walk(result_paths[2]):
63 | if files:
64 | for f in files:
65 | path = os.path.join(root, f)
66 | highconfimgs = cv2.imread(path) # RGB image
67 | highconfimgs = cv2.cvtColor(highconfimgs, cv2.COLOR_BGR2RGB)
68 | h_r.append(highconfimgs)
69 |
70 | b_imgs = np.array(b_imgs)
71 | b_r = np.array(b_r)
72 | low_imgs = np.array(low_imgs)
73 | l_r = np.array(l_r)
74 | high_imgs = np.array(high_imgs)
75 | h_r = np.array(h_r)
76 | print(b_imgs.shape, b_r.shape, low_imgs.shape, l_r.shape, high_imgs.shape, h_r.shape)
77 | results = np.stack((b_imgs, b_r, low_imgs, l_r, high_imgs, h_r))
78 |
79 | fig = plt.figure(figsize=(10, 10))
80 | gs = gridspec.GridSpec(6, 10, wspace=0.1, hspace=0.1)
81 |
82 | for i in range(6):
83 | for j in range(10):
84 | ax = fig.add_subplot(gs[i, j])
85 | ax.imshow(results[i, j], interpolation='none')
86 | ax.set_xticks([])
87 | ax.set_yticks([])
88 | gs.tight_layout(fig)
89 | plt.show()
90 |
91 | if __name__ == '__main__':
92 | plot(PATHS, RESULT_PATH)
93 |
--------------------------------------------------------------------------------
/l0_yolov3.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | # supress tensorflow logging other than errors
4 | # os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1'
5 | import sys
6 |
7 | from keras import backend as K
8 | import numpy as np
9 | import random as rd
10 | import tensorflow as tf
11 | from tensorflow.python import debug as tf_debug
12 | from keras.models import Model
13 | from keras import losses
14 |
15 | from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D
16 | from keras.layers import Conv2D, MaxPooling2D, Input
17 | from keras.layers import Dense, Dropout, Activation, Flatten
18 |
19 | from keras.datasets import cifar10
20 | from keras.models import load_model
21 | from keras.callbacks import EarlyStopping
22 |
23 | from model.yolo_model import YOLO
24 | import cv2
25 | import matplotlib.pyplot as plt
26 | import time
27 | from tensorflow.python import debug as tf_debug
28 | from skimage import io
29 |
30 | YOLO_OUT_SHAPE = (13, 13, 3, 85) # yolo output shape
31 | IMAGE_SHAPE = (1, 416, 416, 3) # input image shape
32 |
33 | MAX_ITERATIONS = 10000 # number of iterations to perform gradient descent
34 | ABORT_EARLY = True # abort gradient descent upon first valid solution
35 | LEARNING_RATE = 1e-2 # larger values converge faster to less accurate results
36 |
37 | lower_bound = 0
38 | INITIAL_CONST = 1e2
39 | LARGEST_CONST = 1e10
40 | REDUCE_CONST = True # try to lower c each iteration; faster to set to false
41 | CONST_FACTOR = 2.0 # f>1, rate at which we increase constant, smaller better
42 | EXAMPLE_NUM = 10
43 |
44 | # ============runing setting==================
45 | CONFIDENCE = 0.3
46 | MAX_SEARCH = 5
47 | START_FROM = 0
48 | CUDA_GPU = '2'
49 |
50 | os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID'
51 | os.environ['CUDA_VISIBLE_DEVICES'] = CUDA_GPU
52 |
53 | PATH = 'adv_examples/L0/f3_eval/test/{0} confidence'.format(CONFIDENCE)
54 |
55 | def get_classes(file):
56 | """Get classes name.
57 |
58 | # Argument:
59 | file: classes name for database.
60 |
61 | # Returns
62 | class_names: List, classes name.
63 |
64 | """
65 | with open(file) as f:
66 | class_names = f.readlines()
67 | class_names = [c.strip() for c in class_names]
68 |
69 | return class_names
70 |
71 | file = 'data/coco_classes.txt'
72 | all_classes = get_classes(file)
73 |
74 | def process_image(img):
75 | """
76 | Resize, reduce and expand image.
77 | # Argument:
78 | img: original image.
79 |
80 | # Returns
81 | image: ndarray(64, 64, 3), processed image.
82 | """
83 | image = cv2.resize(img, (416, 416), interpolation=cv2.INTER_CUBIC)
84 | image = np.array(image, dtype='float32')
85 | image /= 255.
86 | image = np.expand_dims(image, axis=0)
87 | return image
88 |
89 |
90 | def process_yolo_output(out, anchors, mask):
91 | """
92 | Tensor op: Process output features.
93 | # Arguments
94 | out - tensor (?, N, N, 3, 4 + 1 +80), output feature map of yolo.
95 | anchors - List, anchors for box.
96 | mask - List, mask for anchors.
97 |
98 | # Returns
99 | boxes - tensor (N, N, 3, 4), x,y,w,h for per box.
100 | box_confidence - tensor (N, N, 3, 1), confidence for per box.
101 | box_class_probs - tensor (N, N, 3, 80), class probs for per box.
102 | """
103 | grid_h, grid_w, num_boxes = map(int, out.shape[1: 4])
104 |
105 | anchors = [anchors[i] for i in mask]
106 | # Reshape to batch, height, width, num_anchors, box_params.
107 | anchors_tensor = tf.reshape(tf.constant(anchors, dtype=tf.float32, name='anchor_tensor'), [1, 1, len(anchors), 2])
108 | out = out[0]
109 | box_xy = tf.sigmoid(out[:, :, :, 0:2], name='box_xy')
110 | box_wh = tf.identity(tf.exp(out[:, :, :, 2:4]) * anchors_tensor, name='box_wh')
111 |
112 | box_confidence = tf.sigmoid(out[:, :, :, 4:5], name='objectness')
113 | box_class_probs = tf.sigmoid(out[:, :, :, 5:], name='class_probs')
114 |
115 | col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
116 | row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)
117 |
118 | col = col.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
119 | row = row.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
120 | grid = tf.constant(np.concatenate((col, row), axis=-1), dtype=tf.float32)
121 |
122 | box_xy += grid
123 | box_xy /= (grid_w, grid_h)
124 | box_wh /= (416, 416)
125 | box_xy -= (box_wh / 2.)
126 |
127 | # boxes -> (13, 13, 3, 4)
128 | boxes = tf.concat([box_xy, box_wh], axis=-1)
129 | # box_confidence -> (13, 13, 3, 1) 26 52
130 | # box_class_probs -> (13, 13, 3, 80)
131 | boxes = tf.reshape(boxes, [int(boxes.shape[0]) **
132 | 2, boxes.shape[2], boxes.shape[3]])
133 | box_confidence = tf.reshape(box_confidence,
134 | [int(box_confidence.shape[0]) ** 2,
135 | box_confidence.shape[-2],
136 | box_confidence.shape[-1]])
137 | box_class_probs = tf.reshape(box_class_probs,
138 | [int(box_class_probs.shape[0]) ** 2,
139 | box_class_probs.shape[-2],
140 | box_class_probs.shape[-1]],
141 | name='class_probs')
142 | return boxes, box_confidence, box_class_probs
143 |
144 |
145 | def process_output(raw_outs):
146 | """
147 | Tensor op: Extract b, c, and s from raw outputs.
148 | # Args:
149 | raw_outs - Yolo raw output tensor.
150 | #
151 | boxes - Tensors. (N1**2+N2**2+N3**2, 3, 4), classes: (N1**2+N2**2+N3**2, 3, 1), scores: (N1**2+N2**2+N3**2, 3, 80)
152 | """
153 | masks = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
154 | anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]]
155 | boxes, objecness, scores = [], [], []
156 |
157 | for out, mask in zip(raw_outs, masks):
158 | # out -> (1, 13, 13, 3, 85)
159 | # mask -> [6, 7, 8]
160 | # boxes(13X13, 3, 4), box_confidence(13X13, 3, 1),
161 | # box_class_probs(13X13, 3, 80) | 26 X 26 |
162 | b, c, s = process_yolo_output(out, anchors, mask)
163 | if boxes == []:
164 | boxes = b
165 | objecness = c
166 | scores = s
167 | else:
168 | boxes = tf.concat([boxes, b], 0, name='xywh')
169 | objecness = tf.concat([objecness, c], 0, name='objectness')
170 | scores = tf.concat([scores, s], 0, name='class_probs')
171 | return boxes, objecness, scores
172 |
173 |
174 | def pdist(xy):
175 | """
176 | Tensor op: Computes pairwise distance between each pair of points
177 | # Args:
178 | xy - [N,2] matrix representing N box position coordinates
179 | # Content:
180 | dists - [N,N] matrix of (squared) Euclidean distances
181 | # Return:
182 | expectation of the Euclidean distances
183 | """
184 | xy2 = tf.reduce_sum(xy * xy, 1, True)
185 | dists = xy2 - 2 * tf.matmul(xy, tf.transpose(xy)) + tf.transpose(xy2)
186 | return tf.reduce_sum(dists)
187 |
188 | def output_to_pdist(bx, by):
189 | """
190 | Tensor op: calculate expectation of box distance given yolo outpput bx & by.
191 | # Args:
192 | bx - YOLOv3 output batch x coordinates in shape (N, 3549, 3, 1)
193 | by - YOLOv3 output batch y coordinates in shape (N, 3549, 3, 1)
194 | """
195 | bxby = tf.concat([bx, by], axis=-1)
196 | bxby = tf.reshape(bxby, [-1, 2])
197 | return pdist(bxby)
198 |
199 | def pairwise_IoUs(bs1, bs2):
200 | """
201 | Tensor op: Calculate pairwise IoUs given two sets of boxes.
202 | # Arguments:
203 | bs1, bs2 - tensor of boxes in shape (?, 4)
204 | # Content:
205 | X11,y11------x12,y11 X21,y21------x22,y21
206 | | | | |
207 | | | | |
208 | x11,y12-------x12,y12 x21,y22-------x22,y22
209 | # Returns:
210 | iou - a tensor of the matrix containing pairwise IoUs, in shape (?, ?)
211 | """
212 | x11, y11, w1, h1 = tf.split(bs1, 4, axis=1) # (N, 1)
213 | x21, y21, w2, h2 = tf.split(bs2, 4, axis=1) # (N, 1)
214 | x12 = x11 + w1
215 | y12 = y11 + h1
216 | x22 = x21 + w2
217 | y22 = y21 + h2
218 | xA = tf.maximum(x11, tf.transpose(x21))
219 | yA = tf.maximum(y11, tf.transpose(y21))
220 | xB = tf.minimum(x12, tf.transpose(x22))
221 | yB = tf.minimum(y12, tf.transpose(y22))
222 | # prevent 0 area
223 | interArea = tf.maximum((xB - xA + 1), 0) * tf.maximum((yB - yA + 1), 0)
224 |
225 | boxAArea = (x12 - x11 + 1) * (y12 - y11 + 1)
226 | boxBArea = (x22 - x21 + 1) * (y22 - y21 + 1)
227 |
228 | iou = interArea / (boxAArea + tf.transpose(boxBArea) - interArea)
229 | return iou
230 |
231 |
232 | def expectation_of_IoUs(boxes):
233 | """
234 | Tensor op: Calculate the expectation given all pairwise IoUs.
235 | # Arguments
236 | boxes - boxes of objects. It takes (?, 4) shaped tensor;
237 | # Returns
238 | expt - expectation of IoUs of box pairs. Scalar tensor.
239 | """
240 | IoUs = pairwise_IoUs(boxes, boxes)
241 | expt = tf.reduce_mean(IoUs)
242 | return expt
243 |
244 |
245 | def expectation_of_IoUs_accross_classes(boxes, box_scores):
246 | """
247 | Tensor op: Calculate IoU expectation for IoU expectations from different class.
248 | Arguments:
249 | #boxes - (3549, 3, 4) tensor output from yolo net
250 | #box_scores - (N1**2+N2**2+N3**2, 3, 80) tensor
251 | Content:
252 | #box_classes - (N1**2+N2**2+N3**2, 3, 1) tensor
253 | Returns:
254 | #expt_over_all_classes - The IoU expectation of box pairs over all classes.
255 | """
256 | box_classes = tf.cast(tf.argmax(box_scores, axis=-1), tf.int32, name='box_classes')
257 | class_counts = tf.bincount(box_classes)
258 | dominating_cls = tf.argmax(class_counts)
259 | dominating_cls = tf.cast(dominating_cls, tf.int32)
260 | index = tf.equal(box_classes, dominating_cls)
261 | index = tf.cast(index, tf.int32)
262 | others, dominating_boxes = tf.dynamic_partition(boxes, index, num_partitions=2, name='dynamic_partition')
263 | expt_over_all_classes = expectation_of_IoUs(dominating_boxes)
264 |
265 | return expt_over_all_classes
266 |
267 |
268 | class YoloAttacker:
269 | """
270 | Daedalus adversarial example generator based on the Yolo v3 model.
271 | """
272 |
273 | def __init__(self, sess, model, learning_rate=LEARNING_RATE,
274 | max_iterations=MAX_ITERATIONS, abort_early=ABORT_EARLY,
275 | initial_const=INITIAL_CONST, largest_const=LARGEST_CONST,
276 | reduce_const=REDUCE_CONST, const_factor=CONST_FACTOR,
277 | independent_channels=False, lower_bound=lower_bound, max_search = MAX_SEARCH):
278 |
279 | self.model = model
280 | self.sess = sess
281 |
282 | self.LEARNING_RATE = learning_rate
283 | self.MAX_ITERATIONS = max_iterations
284 | self.ABORT_EARLY = abort_early
285 | self.INITIAL_CONST = initial_const
286 | self.LARGEST_CONST = largest_const
287 | self.REDUCE_CONST = reduce_const
288 | self.const_factor = const_factor
289 | self.independent_channels = independent_channels
290 |
291 | self.grad = self.gradient_descent(sess, model)
292 |
293 | self.confidence = CONFIDENCE
294 | self.lower_bound = lower_bound
295 | self.max_search = max_search
296 |
297 | self.search_iteration = 1
298 |
299 | def gradient_descent(self, sess, model):
300 |
301 | shape = IMAGE_SHAPE
302 |
303 | # the variable to optimize over
304 | modifier = tf.Variable(np.zeros(shape, dtype=np.float32))
305 |
306 | # the variables we're going to hold, use for efficiency
307 | canchange = tf.Variable(np.zeros(shape), dtype=np.float32)
308 | simg = tf.Variable(np.zeros(shape, dtype=np.float32))
309 | original = tf.Variable(np.zeros(shape, dtype=np.float32))
310 | timg = tf.Variable(np.zeros(shape, dtype=np.float32))
311 | const = tf.placeholder(tf.float32, [])
312 |
313 | # and the assignment to set the variables
314 | assign_modifier = tf.placeholder(np.float32, shape)
315 | assign_canchange = tf.placeholder(np.float32, shape)
316 | assign_simg = tf.placeholder(np.float32, shape)
317 | assign_original = tf.placeholder(np.float32, shape)
318 | assign_timg = tf.placeholder(np.float32, shape)
319 |
320 | # these are the variables to initialize when we run
321 | set_modifier = tf.assign(modifier, assign_modifier)
322 | setup = []
323 | setup.append(tf.assign(canchange, assign_canchange))
324 | setup.append(tf.assign(timg, assign_timg))
325 | setup.append(tf.assign(original, assign_original))
326 | setup.append(tf.assign(simg, assign_simg))
327 |
328 | newimg = ((tf.tanh(modifier + simg) + 1) / 2) * canchange + (1 - canchange) * original
329 |
330 | self.outs = self.model._yolo(newimg)
331 | # [(1, 13, 13, 3, 85), (1, 26, 26, 3, 85), (1, 52, 52, 3, 85)]
332 | # (3549, 3, 4), (3549, 3, 1), (3549, 3, 80) | 13 X 13 + 26 X 26 + 52 X 52
333 | boxes, objectness, classprobs = process_output(self.outs)
334 |
335 | Iou_expt = expectation_of_IoUs_accross_classes(boxes, classprobs)
336 | self.bx = boxes[..., 0:1]
337 | self.by = boxes[..., 1:2]
338 | self.bw = boxes[..., 2:3]
339 | self.bh = boxes[..., 3:4]
340 | self.obj_scores = objectness
341 | self.class_probs = classprobs
342 | self.box_scores = tf.multiply(self.obj_scores, tf.reduce_max(self.class_probs, axis=-1, keepdims=True))
343 |
344 | # # Optimisation metrics:
345 | self.l2dist = tf.reduce_sum(tf.square(newimg - (tf.tanh(timg) + 1) / 2), [1, 2, 3])
346 | self.image_sum = tf.reduce_sum(newimg)
347 |
348 | # Define DDoS losses: loss must be a tensor here!
349 | # Make the objectness of all detections to be 1.
350 | self.loss1_1_x = tf.reduce_mean(tf.square(self.box_scores - 1), [-3, -2, -1]) # X
351 |
352 | # Minimising the size of all bounding box.
353 | self.f1 = tf.reduce_mean(Iou_expt)
354 | self.f2 = tf.reduce_mean(tf.square(tf.multiply(self.bw, self.bh)), [-3, -2, -1]) # a
355 | self.f3 = self.f2 + 1/output_to_pdist(self.bx, self.by)
356 |
357 | # add two loss terms together
358 | self.loss_adv = self.loss1_1_x + self.f2
359 | loss1 = tf.reduce_mean(const * self.loss_adv)
360 | loss2 = tf.reduce_mean(self.l2dist)
361 | loss = loss1 + loss2
362 |
363 | outgrad = tf.gradients(loss, [modifier])[0]
364 |
365 | # setup the adam optimizer and keep track of variables we're creating
366 | start_vars = set(x.name for x in tf.global_variables())
367 | optimizer = tf.train.AdamOptimizer(self.LEARNING_RATE)
368 | train = optimizer.minimize(loss, var_list=[modifier])
369 |
370 | end_vars = tf.global_variables()
371 | new_vars = [x for x in end_vars if x.name not in start_vars]
372 | init = tf.variables_initializer(var_list=[modifier, canchange, simg,
373 | original, timg] + new_vars)
374 |
375 |
376 | def doit(oimgs, starts, valid, CONST):
377 | # convert to tanh-space
378 | imgs = np.arctanh((np.array(oimgs) * 2 - 1) * .999999)
379 | starts = np.arctanh((np.array(starts) * 2 - 1) * .999999)
380 |
381 | # initialize the variables
382 | sess.run(init)
383 | sess.run(setup, {assign_timg: imgs,
384 | assign_simg: starts,
385 | assign_original: oimgs,
386 | assign_canchange: valid})
387 |
388 | while self.search_iteration <= self.max_search:
389 | # try solving for each value of the constant
390 | print('=== try const ===', CONST, "|=== search_iteration ===", self.search_iteration)
391 | first_flag = True
392 | init_adv_losses = None
393 | for step in range(self.MAX_ITERATIONS):
394 | feed_dict = {const: CONST}
395 |
396 | # remember the old value
397 | oldmodifier = self.sess.run(modifier)
398 |
399 | # perform the update step
400 | _, works, l1= sess.run([train, loss1, self.loss_adv], feed_dict=feed_dict)
401 | if first_flag:
402 | init_adv_losses = l1
403 | first_flag = False
404 |
405 | def check_success(loss, init_loss):
406 | """
407 | Check if the initial loss value has been reduced by 'self.confidence' percent
408 | """
409 | return loss <= init_loss * (1 - self.confidence)
410 |
411 | if check_success(l1, init_adv_losses) and (self.ABORT_EARLY or step == CONST - 1):
412 | loss_shown, l2s, newimg_shown, l1 = sess.run([loss, loss2, newimg, self.loss_adv], feed_dict=feed_dict)
413 | l0_attack_pixel = np.sum(valid)
414 | # it worked previously, restore the old value and finish
415 | self.sess.run(set_modifier, {assign_modifier: oldmodifier})
416 | grads, scores, nimg = sess.run((outgrad, self.outs, newimg),
417 | feed_dict=feed_dict)
418 | l2s = np.array([l2s])
419 | return grads, scores, nimg, CONST, l2s
420 |
421 | self.lower_bound = max(self.lower_bound, CONST)
422 | if self.LARGEST_CONST < 1e9:
423 | CONST = (self.lower_bound + self.LARGEST_CONST) / 2
424 | else:
425 | CONST *= 10
426 | self.search_iteration += 1
427 |
428 | return doit
429 |
430 | def attack_single(self, img):
431 | """
432 | Run the attack on a single image and label
433 | """
434 |
435 | # the pixels we can change
436 | valid = np.ones((1, IMAGE_SHAPE[1], IMAGE_SHAPE[2], IMAGE_SHAPE[3]))
437 |
438 | # the previous image
439 | prev = np.copy(img).reshape((1, IMAGE_SHAPE[1], IMAGE_SHAPE[2],
440 | IMAGE_SHAPE[3]))
441 | last_solution = np.zeros((1,416,416,3))
442 | last_distortion = np.zeros((1,))
443 | last_const = np.zeros((1,))
444 | const = self.INITIAL_CONST
445 | self.search_iteration = 1
446 | while True:
447 | # try to solve given this valid map
448 | res = self.grad([np.copy(img)], np.copy(prev),
449 | valid, const)
450 | if res == None:
451 | # the attack failed, we return this as our final answer
452 | print("the attack failed, we return this as our final answer")
453 | return last_solution, last_distortion, last_const
454 |
455 | # the attack succeeded, now we pick new pixels to set to 0
456 | restarted = False
457 | gradientnorm, scores, nimg, const, l2s = res
458 |
459 | # save the results
460 | last_solution = prev = nimg
461 | last_distortion = l2s
462 | last_const = np.array([const])
463 |
464 | # adjust the value of const
465 | if self.REDUCE_CONST:
466 | self.search_iteration += 1
467 | self.LARGEST_CONST = min(self.LARGEST_CONST, const)
468 | if self.LARGEST_CONST < 1e9:
469 | const = (self.lower_bound + self.LARGEST_CONST) / 2
470 | print('*** calculate equal_count ***')
471 | equal_count = 416 ** 2 - np.sum(np.all(np.abs(img - nimg[0]) < .0001, axis=2))
472 | print("Forced equal:", np.sum(1 - valid),
473 | "Equal count:", equal_count)
474 | if np.sum(valid) == 0:
475 | # if no pixels changed, return
476 | return [img], l2s, last_const
477 |
478 | if self.independent_channels:
479 | # we are allowed to change each channel independently
480 | valid = valid.flatten()
481 | totalchange = abs(nimg[0] - img) * np.abs(gradientnorm[0])
482 | else:
483 | # we care only about which pixels change, not channels independently
484 | # compute total change as sum of change for each channel
485 | valid = valid.reshape((IMAGE_SHAPE[1] ** 2, IMAGE_SHAPE[3]))
486 | totalchange = abs(np.sum(nimg[0] - img, axis=2)) * np.sum(np.abs(gradientnorm[0]), axis=2)
487 | totalchange = totalchange.flatten()
488 |
489 | # set some of the pixels to 0 depending on their total change
490 | did = 0
491 | for e in np.argsort(totalchange):
492 | if np.all(valid[e]):
493 | did += 1
494 | valid[e] = 0
495 |
496 | if totalchange[e] > .01:
497 | # if this pixel changed a lot, skip
498 | break
499 | if did >= .3 * equal_count ** .5:
500 | # if we changed too many pixels, skip
501 | print('we changed too many pixels, skip')
502 | break
503 |
504 | valid = np.reshape(valid, (1, IMAGE_SHAPE[1], IMAGE_SHAPE[1], -1))
505 | # total nums of be masked based on l2 result
506 | print("Now forced equal:", np.sum(1 - valid))
507 |
508 |
509 | def attack(self, imgs):
510 | """
511 | Perform the L_0 attack on the given images.
512 | """
513 | r1 = []
514 | r2 = []
515 | for i, img in enumerate(imgs):
516 | print("Attack iteration", i)
517 | X_adv, dists, consts = self.attack_single(img)
518 |
519 | if not os.path.exists(PATH):
520 | os.makedirs(PATH)
521 | np.save(PATH + '/Distortions of image {0}.npy'.format(i), dists)
522 | for j in range(len(X_adv)):
523 | io.imsave(PATH + '/Best example of {1} CONST {0}.png'.format(consts, i+j), X_adv[j])
524 | print('====== save the result:', path+'/Best example of {1} CONST {0}.png'.format(consts, i+j), '======')
525 | r1.extend(X_adv)
526 | r2.extend(dists)
527 |
528 | return np.array(r1), np.array(r2)
529 |
530 | if __name__ == '__main__':
531 | sess = tf.InteractiveSession()
532 | init = tf.global_variables_initializer()
533 | sess.run(init)
534 |
535 | ORACLE = YOLO(0.6, 0.5) # The auguments do not matter.
536 |
537 | X_test = []
538 | i=0
539 | for (root, dirs, files) in os.walk('../COCO/val2017/'):
540 | if files:
541 | for f in files:
542 | # select 10 images
543 | if i >= EXAMPLE_NUM:
544 | break
545 | print(f)
546 | path = os.path.join(root, f)
547 | image = cv2.imread(path)
548 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # RGB
549 | image = process_image(image)
550 | X_test.append(image)
551 | i=i+1
552 | X_test = np.concatenate(X_test, axis=0)
553 | attacker = YoloAttacker(sess, ORACLE)
554 | attacker.attack(X_test)
--------------------------------------------------------------------------------
/l2_ensemble.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | # supress tensorflow logging other than errors
4 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
5 | sys.path.insert(0, '..')
6 |
7 | from keras import backend as K
8 | import numpy as np
9 | import random as rd
10 | import tensorflow as tf
11 | from tensorflow.python import debug as tf_debug
12 | from keras.models import Model
13 | from keras import losses
14 |
15 | from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D
16 | from keras.layers import Conv2D, MaxPooling2D, Input
17 | from keras.layers import Dense, Dropout, Activation, Flatten
18 |
19 | from keras.models import load_model
20 | from keras.callbacks import EarlyStopping
21 |
22 | from keras_retinanet import models as retina_models
23 | from YOLOv3.model.yolo_model import YOLO
24 | import cv2
25 | import matplotlib.pyplot as plt
26 | from skimage import io
27 | import time
28 |
29 | # Parameter settings:
30 | GPU_ID = 0 # which gpu to used
31 | ATTACK_MODE = 'all' # select attack mode from 'all', 'most', 'least' and 'single';
32 | ATTACK_CLASS = None # select the class to attack in 'single' mode
33 | CONFIDENCE = 0.3 # the confidence of attack
34 | EXAMPLE_NUM = 10 # total number of adversarial example to generate.
35 | BATCH_SIZE = 1 # number of adversarial example generated in each batch
36 |
37 | BINARY_SEARCH_STEPS = 5 # number of times to adjust the constsant with binary search
38 | INITIAL_consts = 1e1 # the initial constsant c to pick as a first guess
39 | CLASS_NUM = 80 # 80 for COCO dataset
40 | MAX_ITERATIONS = 10000 # number of iterations to perform gradient descent
41 | ABORT_EARLY = True # if we stop improving, abort gradient descent early
42 | LEARNING_RATE = 1e-2 # larger values converge faster to less accurate results
43 | IMAGE_SHAPE = (416, 416, 3) # input image shape
44 | SAVE_PATH = 'adv_examples/L2/f3/{0}/'.format(ATTACK_MODE)
45 | # select GPU to use
46 | os.environ["CUDA_VISIBLE_DEVICES"] = '{0}'.format(GPU_ID)
47 |
48 | def load_yolov3():
49 | return YOLO(0.5, 0.5)
50 |
51 | def load_yolo():
52 | return YOLO(0.5, 0.5)
53 |
54 | def load_retinanet():
55 | model_path = os.path.join('model', 'resnet50_coco_best_v2.1.0.h5')
56 | # load retinanet model
57 | oracle = retina_models.load_model(model_path, backbone_name='resnet50')
58 | oracle.layers.pop()
59 | oracle.outputs = [oracle.layers[-2].output, oracle.layers[-1].output] #remove nms from original model
60 | oracle.layers[-1].outbound_nodes = []
61 | oracle.summary()
62 | return oracle
63 |
64 | def process_image(img):
65 | """
66 | Resize, reduce and expand image.
67 | # Argument:
68 | img: original image.
69 |
70 | # Returns
71 | image: ndarray(64, 64, 3), processed image.
72 | """
73 | image = cv2.resize(img, (416, 416),
74 | interpolation=cv2.INTER_CUBIC)
75 | image = np.array(image, dtype='float32')
76 | image /= 255.
77 | image = np.expand_dims(image, axis=0)
78 | return image
79 |
80 |
81 | def process_yolo_output(out, anchors, mask):
82 | """
83 | Tensor op: Process output features.
84 | # Arguments
85 | out - tensor (N, S, S, 3, 4+1+80), output feature map of yolo.
86 | anchors - List, anchors for box.
87 | mask - List, mask for anchors.
88 | # Returns
89 | boxes - tensor (N, S, S, 3, 4), x,y,w,h for per box.
90 | box_confidence - tensor (N, S, S, 3, 1), confidence for per box.
91 | box_class_probs - tensor (N, S, S, 3, 80), class probs for per box.
92 | """
93 | batchsize, grid_h, grid_w, num_boxes = map(int, out.shape[0:4])
94 |
95 | box_confidence = tf.sigmoid(out[..., 4:5], name='objectness') # (N, S, S, 3, 1)
96 | box_class_probs = tf.sigmoid(out[..., 5:], name='class_probs') # (N, S, S, 3, 80)
97 |
98 | anchors = np.array([anchors[i] for i in mask]) # Dimension of the used three anchor boxes [[x,x], [x,x], [x,x]].
99 | # duplicate to shape (batch, height, width, num_anchors, box_params).
100 | anchors = np.repeat(anchors[np.newaxis, :, :], grid_w, axis=0) # (S, 3, 2)
101 | anchors = np.repeat(anchors[np.newaxis, :, :, :], grid_h, axis=0) # (S, S, 3, 2)
102 | anchors = np.repeat(anchors[np.newaxis, :, :, :, :], batchsize, axis=0) # (N, S, S, 3, 2)
103 | anchors_tensors = tf.constant(anchors, dtype=tf.float32, name='anchor_tensors')
104 |
105 | box_xy = tf.sigmoid(out[..., 0:2], name='box_xy') # (N, S, S, 3, 2)
106 | box_wh = tf.identity(tf.exp(out[..., 2:4]) * anchors_tensors, name='box_wh') # (N, S, S, 3, 2)
107 |
108 | col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
109 | row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)
110 |
111 | col = col.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
112 | row = row.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
113 | grid = np.concatenate((col, row), axis=-1) #(13, 13, 3, 2)
114 | grid_batch = np.repeat(grid[np.newaxis, :, :, :, :], batchsize, axis=0)
115 | box_xy += grid_batch
116 | box_xy /= (grid_w, grid_h)
117 | box_wh /= (416, 416)
118 | box_xy -= (box_wh / 2.)
119 |
120 | # boxes -> (N, S, S, 3, 4)
121 | boxes = tf.concat([box_xy, box_wh], axis=-1)
122 | boxes = tf.reshape(boxes, [batchsize, -1, boxes.shape[-2], boxes.shape[-1]], name='boxes') #(N, S*S, 3, 4)
123 | # box_confidence -> (N, S, S, 3, 1) or 26 or 52
124 | # box_class_probs -> (N, S, S, 3, 80)
125 | box_confidence = tf.reshape(box_confidence, [batchsize,
126 | -1,
127 | box_confidence.shape[-2],
128 | box_confidence.shape[-1]], name='box_confidence')
129 | box_class_probs = tf.reshape(box_class_probs, [batchsize,
130 | -1,
131 | box_class_probs.shape[-2],
132 | box_class_probs.shape[-1]], name='class_probs')
133 | return boxes, box_confidence, box_class_probs
134 |
135 |
136 | def process_output(raw_outs):
137 | """
138 | Tensor op: Extract b, c, and s from raw outputs.
139 | # Args:
140 | raw_outs - Yolo raw output tensor list [(N, 13, 13, 3, 85), (N, 26, 26, 3, 85), (N, 26, 26, 3, 85)].
141 | # Returns:
142 | boxes - Tensors. (N, 3549, 3, 4), classes: (N, 3549, 3, 1), scores: (N, 3549, 3, 80)
143 | """
144 | masks = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
145 | anchors = [[10, 13], [16, 30], [33, 23],
146 | [30, 61], [62, 45], [59, 119],
147 | [116, 90], [156, 198], [373, 326]]
148 | boxes, objecness, scores = [], [], []
149 |
150 | for out, mask in zip(raw_outs, masks):
151 | # out -> (N, 13, 13, 3, 85)
152 | # mask -> one of the masks
153 | # boxes (N, 13X13, 3, 4), box_confidence (N, 13X13, 3, 1)
154 | # box_class_probs (13X13, 3, 80) | 26 X 26 |
155 | b, c, s = process_yolo_output(out, anchors, mask)
156 | if boxes == []:
157 | boxes = b
158 | objecness = c
159 | scores = s
160 | else:
161 | boxes = tf.concat([boxes, b], 1, name='xywh')
162 | objecness = tf.concat([objecness, c], 1, name='objectness')
163 | scores = tf.concat([scores, s], 1, name='class_probs')
164 | return boxes, objecness, scores
165 |
166 | class Daedalus:
167 | """
168 | Daedalus adversarial example generator based on the Yolo v3 model.
169 | """
170 | def __init__(self, sess, models, target_class=ATTACK_CLASS, attack_mode=ATTACK_MODE, img_shape=IMAGE_SHAPE,
171 | batch_size=BATCH_SIZE, confidence=CONFIDENCE, learning_rate=LEARNING_RATE, binary_search_steps=BINARY_SEARCH_STEPS,
172 | max_iterations=MAX_ITERATIONS, abort_early=ABORT_EARLY, initial_consts=INITIAL_consts, boxmin=0, boxmax=1):
173 |
174 | # self.sess = tf_debug.LocalCLIDebugWrapperSession(sess)
175 | self.sess = sess
176 | self.LEARNING_RATE = learning_rate
177 | self.MAX_ITERATIONS = max_iterations
178 | self.BINARY_SEARCH_STEPS = binary_search_steps
179 | self.ABORT_EARLY = abort_early
180 | self.initial_consts = initial_consts
181 | self.batch_size = batch_size
182 | self.repeat = binary_search_steps >= 6
183 | self.yolo3 = models[0]
184 | self.yolo = models[1]
185 | self.retinanet = models[2]
186 | self.confidence = confidence
187 | self.img_dimension = img_shape[0]
188 | self.target_class = target_class
189 | self.attack_mode = attack_mode
190 |
191 | def select_class(target_class, boxes, objectness, box_scores, mode='all'):
192 | box_classes = tf.cast(tf.argmax(box_scores, axis=-1), tf.int32, name='box_classes')
193 | class_counts = tf.bincount(box_classes)
194 | print(class_counts)
195 | if mode == 'all':
196 | selected_boxes = tf.reshape(boxes, [BATCH_SIZE, -1, 4])
197 | selected_scores = tf.reshape(box_scores, [BATCH_SIZE, -1, CLASS_NUM])
198 | if objectness == None:
199 | return selected_boxes, None, selected_scores
200 | selected_objectness = tf.reshape(objectness, [BATCH_SIZE, -1, 1])
201 | return selected_boxes, selected_objectness, selected_scores
202 | elif mode == 'most':
203 | selected_cls = tf.argmax(class_counts)
204 | elif mode == 'least':
205 | class_counts = tf.where(tf.equal(class_counts,0), int(1e6)*tf.ones_like(class_counts, dtype=tf.int32), class_counts)
206 | selected_cls = tf.argmin(class_counts)
207 | elif mode == 'single':
208 | file = 'data/coco_classes.txt'
209 | with open(file) as f:
210 | class_names = f.readlines()
211 | class_names = [c.strip() for c in class_names]
212 | selected_cls = class_names.index(target_class)
213 | selected_cls = tf.cast(selected_cls, tf.int32)
214 | index = tf.equal(box_classes, selected_cls)
215 | index = tf.cast(index, tf.int32)
216 | _, selected_boxes = tf.dynamic_partition(boxes, index, num_partitions=2, name='dynamic_partition')
217 | _, selected_scores = tf.dynamic_partition(box_scores, index, num_partitions=2, name='dynamic_partition')
218 | selected_boxes = tf.reshape(selected_boxes, [BATCH_SIZE, -1, 4])
219 | selected_scores = tf.reshape(selected_scores, [BATCH_SIZE, -1, CLASS_NUM])
220 | if objectness == None:
221 | return selected_boxes, None, selected_scores
222 | _, selected_objectness = tf.dynamic_partition(objectness, index, num_partitions=2, name='dynamic_partition')
223 | selected_objectness = tf.reshape(selected_objectness, [BATCH_SIZE, -1, 1])
224 | return selected_boxes, selected_objectness, selected_scores
225 |
226 | def yolov3_cg(images):
227 | # Get prediction from the model:
228 | outs = self.yolo3._yolo(images)
229 | # [(N, 13, 13, 3, 85), (N, 26, 26, 3, 85), (N, 52, 52, 3, 85)] to (N, 3549, 3, 4), (N, 3549, 3, 1), (N, 3549, 3, 80)
230 | boxes, objectness, classprobs = process_output(outs)
231 | boxes, objectness, classprobs = select_class(self.target_class, boxes, objectness, classprobs, mode=self.attack_mode)
232 | print(boxes, objectness, classprobs)
233 | self.yolo3bx = boxes[..., 0:1]
234 | self.yolo3by = boxes[..., 1:2]
235 | self.yolo3bw = boxes[..., 2:3]
236 | self.yolo3bh = boxes[..., 3:4]
237 | self.yolo3obj_scores = objectness
238 | self.yolo3class_probs = classprobs
239 | self.yolo3box_scores = tf.multiply(self.yolo3obj_scores, tf.reduce_max(self.yolo3class_probs, axis=-1, keepdims=True))
240 | return
241 |
242 | def retina_cg(images):
243 | caffe_imgs = images * 255.
244 | caffe_imgs = caffe_imgs[..., ::-1]
245 | caffe_offsets = np.concatenate([103.939*np.ones((batch_size, 416, 416, 1)),
246 | 116.779*np.ones((batch_size, 416, 416, 1)),
247 | 123.68*np.ones((batch_size, 416, 416, 1))], axis=-1)
248 | caffe_imgs = caffe_imgs - caffe_offsets
249 | boxes, classprobs = self.retinanet(images)
250 | boxes, _, classprobs = select_class(self.target_class, boxes, None, classprobs, mode=self.attack_mode)
251 | print(boxes, classprobs)
252 | self.retinax1 = boxes[..., 0:1]/self.img_dimension
253 | self.retinay1 = boxes[..., 1:2]/self.img_dimension
254 | self.retinax2 = boxes[..., 2:3]/self.img_dimension
255 | self.retinay2 = boxes[..., 3:4]/self.img_dimension
256 | self.retinabw = tf.math.abs(self.retinax2 - self.retinax1)
257 | self.retinabh = tf.math.abs(self.retinay1 - self.retinay2)
258 | self.retinaclass_probs = classprobs
259 | self.retinabox_scores = tf.reduce_max(self.retinaclass_probs, axis=-1, keepdims=True)
260 | return
261 |
262 | # the perturbation we're going to optimize:
263 | perturbations = tf.Variable(np.zeros((batch_size,
264 | img_shape[0],
265 | img_shape[1],
266 | img_shape[2])), dtype=tf.float32, name='perturbations')
267 | # tf variables to sending data to tf:
268 | self.timgs = tf.Variable(np.zeros((batch_size,
269 | img_shape[0],
270 | img_shape[1],
271 | img_shape[2])), dtype=tf.float32, name='self.timgs')
272 | self.consts = tf.Variable(np.zeros(batch_size), dtype=tf.float32, name='self.consts')
273 |
274 | # and here's what we use to assign them:
275 | self.assign_timgs = tf.placeholder(tf.float32, (batch_size,
276 | img_shape[0],
277 | img_shape[1],
278 | img_shape[2]))
279 | self.assign_consts = tf.placeholder(tf.float32, [batch_size])
280 |
281 | # Tensor operation: the resulting image, tanh'd to keep bounded from
282 | # boxmin to boxmax:
283 | self.boxmul = (boxmax - boxmin) / 2.
284 | self.boxplus = (boxmin + boxmax) / 2.
285 | self.newimgs = tf.tanh(perturbations + self.timgs) * self.boxmul + self.boxplus
286 | yolov3_cg(self.newimgs)
287 | retina_cg(self.newimgs)
288 |
289 | # Optimisation metrics:
290 | self.l2dist = tf.reduce_sum(tf.square(self.newimgs - (tf.tanh(self.timgs) * self.boxmul + self.boxplus)), [1, 2, 3])
291 |
292 | # Define DDoS losses: loss must be a tensor here!
293 | # Make the box confidence of all detections to be 1.
294 | self.loss1_1_x = tf.reduce_mean(tf.square(self.yolo3box_scores-1), [-2,-1]) + tf.reduce_mean(tf.square(self.retinabox_scores-1), [-2,-1])
295 |
296 | # Minimising the size of all bounding box.
297 | self.f3 = tf.reduce_mean(tf.square(tf.multiply(self.yolo3bw, self.yolo3bh)), [-2, -1]) + 1e3*tf.reduce_mean(tf.square(tf.multiply(self.retinabw, self.retinabh)), [-2, -1])
298 |
299 | # add two loss terms together
300 | self.loss_adv = self.loss1_1_x + self.f3
301 | self.loss1 = tf.reduce_mean(self.consts * self.loss_adv)
302 | self.loss2 = tf.reduce_mean(self.l2dist)
303 | self.loss = self.loss1 + self.loss2
304 |
305 | # Setup the adam optimizer and keep track of variables we're creating
306 | start_vars = set(x.name for x in tf.global_variables())
307 | optimizer = tf.train.AdamOptimizer(self.LEARNING_RATE)
308 | self.train = optimizer.minimize(self.loss, var_list=[perturbations])
309 | end_vars = tf.global_variables()
310 | new_vars = [x for x in end_vars if x.name not in start_vars]
311 |
312 | # these are the variables to initialize when we run
313 | self.setup = []
314 | self.setup.append(self.timgs.assign(self.assign_timgs))
315 | self.setup.append(self.consts.assign(self.assign_consts))
316 | self.init = tf.variables_initializer(var_list=[perturbations] + new_vars)
317 |
318 | def attack_batch(self, imgs):
319 | """
320 | Run the attack on a batch of images and labels.
321 | """
322 |
323 | def check_success(loss, init_loss):
324 | """
325 | Check if the initial loss value has been reduced by 'self.confidence' percent
326 | """
327 | return loss <= init_loss * (1 - self.confidence)
328 |
329 | batch_size = self.batch_size
330 |
331 | # convert images to arctanh-space
332 | imgs = np.arctanh((imgs - self.boxplus) / self.boxmul * 0.999999)
333 |
334 | # set the lower and upper bounds of the constsant.
335 | lower_bound = np.zeros(batch_size)
336 | consts = np.ones(batch_size) * self.initial_consts
337 | upper_bound = np.ones(batch_size) * 1e10
338 |
339 | # store the best l2, score, and image attack
340 | o_bestl2 = [1e10] * batch_size
341 | o_bestloss = [1e10] * batch_size
342 | o_bestattack = [np.zeros(imgs[0].shape)] * batch_size
343 |
344 | for outer_step in range(self.BINARY_SEARCH_STEPS):
345 | # completely reset adam's internal state.
346 | self.sess.run(self.init)
347 |
348 | # take in the current data batch.
349 | batch = imgs[:batch_size]
350 |
351 | # cache the current best l2 and score.
352 | bestl2 = [1e10] * batch_size
353 | # bestconfidence = [-1]*batch_size
354 | bestloss = [1e10] * batch_size
355 |
356 | # The last iteration (if we run many steps) repeat the search once.
357 | if self.repeat and outer_step == self.BINARY_SEARCH_STEPS - 1:
358 | consts = upper_bound
359 |
360 | # set the variables so that we don't have to send them over again.
361 | self.sess.run(self.setup, {self.assign_timgs: batch,
362 | self.assign_consts: consts})
363 |
364 | obj_grads = tf.gradients(self.loss1_1_x, self.newimgs)
365 | print('objectness gradients:', sess.run(obj_grads))
366 | loss_grads = tf.gradients(self.f3, self.newimgs)
367 | print('loss gradients:', sess.run(loss_grads))
368 |
369 | # start gradient descent attack
370 | print('adjust c to:', sess.run(self.consts))
371 | init_loss = sess.run(self.loss)
372 | init_adv_losses = sess.run(self.loss_adv)
373 | prev = init_loss * 1.1
374 | for iteration in range(self.MAX_ITERATIONS):
375 | # perform the attack on a single example
376 | _, l, l2s, l1s, nimgs, c = self.sess.run([self.train, self.loss, self.l2dist, self.loss_adv, self.newimgs, self.consts])
377 | # print out the losses every 10%
378 | if iteration % (self.MAX_ITERATIONS // 10) == 0:
379 | print('===iteration:', iteration, '===')
380 | print('attacked box number:', sess.run(self.yolo3bw).shape, sess.run(self.retinabw).shape)
381 | print('loss values of box confidence and dimension:', sess.run([self.loss1_1_x, self.f3]))
382 | print('adversarial losses:', l1s)
383 | print('distortions:', l2s)
384 |
385 | # check if we should abort search if we're getting nowhere.
386 | if self.ABORT_EARLY and iteration % (self.MAX_ITERATIONS // 10) == 0:
387 | if l > prev * .9999:
388 | break
389 | prev = l
390 |
391 | # update the best result found so far
392 | for e, (l1, l2, ii) in enumerate(zip(l1s, l2s, nimgs)):
393 | if l2 < bestl2[e] and check_success(l1, init_adv_losses[e]):
394 | bestl2[e] = l2
395 | bestloss[e] = l1
396 | if l2 < o_bestl2[e] and check_success(l1, init_adv_losses[e]):
397 | o_bestl2[e] = l2
398 | o_bestloss[e] = l1
399 | o_bestattack[e] = ii
400 |
401 | # adjust the constsant as needed
402 | for e in range(batch_size):
403 | if check_success(l1s[e], init_adv_losses[e]):
404 | # success, divide consts by two
405 | upper_bound[e] = min(upper_bound[e], consts[e])
406 | if upper_bound[e] < 1e9:
407 | consts[e] = (lower_bound[e] + upper_bound[e]) / 2
408 | else:
409 | # failure, either multiply by 10 if no solution found yet
410 | # or do binary search with the known upper bound
411 | lower_bound[e] = max(lower_bound[e], consts[e])
412 | if upper_bound[e] < 1e9:
413 | consts[e] = (lower_bound[e] + upper_bound[e]) / 2
414 | else:
415 | consts[e] *= 10
416 | # return the best solution found
417 | o_bestl2 = np.array(o_bestl2)
418 | return o_bestattack, o_bestl2
419 |
420 |
421 | def attack(self, imgs):
422 | """
423 | Perform the L_2 attack on the given images for the given targets.
424 | If self.targeted is true, then the targets represents the target labels.
425 | If self.targeted is false, then targets are the original class labels.
426 | """
427 | r = []
428 | ds = []
429 | print('go up to', len(imgs))
430 | for i in range(0, len(imgs), self.batch_size):
431 | print('tick', i)
432 | X_adv, dists = self.attack_batch(imgs[i:i + self.batch_size])
433 | path = SAVE_PATH+'ensemble/{0} confidence'.format(self.confidence)
434 | if not os.path.exists(path):
435 | os.makedirs(path)
436 | np.save(path+'/Distortions of images {0} to {1}.npy'.format(i, i+self.batch_size), dists)
437 | for j in range(len(X_adv)):
438 | io.imsave(path+'/Best example of {1} Distortion {2}.png'.format(self.confidence, i+j, dists[j]), X_adv[j])
439 | r.extend(X_adv)
440 | ds.extend(dists)
441 | return np.array(r), np.array(ds)
442 |
443 |
444 | if __name__ == '__main__':
445 | sess = tf.InteractiveSession()
446 | init = tf.global_variables_initializer()
447 | sess.run(init)
448 | ORACLEs = [load_yolov3(), load_yolo(), load_retinanet()] # The auguments do not matter.
449 | attacker = Daedalus(sess, ORACLEs)
450 |
451 | X_test = []
452 | for (root, dirs, files) in os.walk('../Datasets/COCO/val2017/'):
453 | if files:
454 | for f in files:
455 | print(f)
456 | path = os.path.join(root, f)
457 | image = cv2.imread(path)
458 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # RGB
459 | image = process_image(image)
460 | #image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
461 | X_test.append(image)
462 | EXAMPLE_NUM -= 1
463 | if EXAMPLE_NUM == 0:
464 | break
465 | X_test = np.concatenate(X_test, axis=0)
466 |
467 | start = time.time()
468 | X_adv, distortions = attacker.attack(X_test)
469 | end = time.time()
470 | print('time: {0:.2f}s'.format((end - start)*0.2))
471 | f = open('f2 runtime.txt', 'a')
472 | f.write('time: {0:.2f}s\n'.format((end - start)*0.2))
473 | f.close()
474 | np.savez(SAVE_PATH+'ensemble/{} confidence/Daedalus example batch.npz'.format(CONFIDENCE), X_adv=X_adv, distortions=distortions)
475 | writer = tf.summary.FileWriter("log", sess.graph)
476 | writer.close()
477 |
--------------------------------------------------------------------------------
/l2_retinanet.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | # supress tensorflow logging other than errors
4 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
5 | sys.path.insert(0, '..')
6 |
7 | from keras import backend as K
8 | import numpy as np
9 | import random as rd
10 | import tensorflow as tf
11 | from tensorflow.python import debug as tf_debug
12 | from keras.models import Model
13 | from keras import losses
14 |
15 | from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D
16 | from keras.layers import Conv2D, MaxPooling2D, Input
17 | from keras.layers import Dense, Dropout, Activation, Flatten
18 |
19 | from keras.datasets import cifar10
20 | from keras.models import load_model
21 | from keras.callbacks import EarlyStopping
22 |
23 | from keras_retinanet import models
24 | import cv2
25 | import matplotlib.pyplot as plt
26 | from skimage import io
27 | import time
28 |
29 | # Parameter settings:
30 | GPU_ID = 0 # which gpu to used
31 | ATTACK_MODE = 'all' # select attack mode from 'all', 'most', 'least' and 'single';
32 | ATTACK_CLASS = None # select the class to attack in 'single' mode
33 | CONFIDENCE = 0.3 # the confidence of attack
34 | EXAMPLE_NUM = 10 # total number of adversarial example to generate.
35 | BATCH_SIZE = 1 # number of adversarial example generated in each batch
36 |
37 | BINARY_SEARCH_STEPS = 5 # number of times to adjust the constsant with binary search
38 | INITIAL_consts = 50 # the initial constsant c to pick as a first guess
39 | CLASS_NUM = 80 # 80 for COCO dataset
40 | MAX_ITERATIONS = 10000 # number of iterations to perform gradient descent
41 | ABORT_EARLY = True # if we stop improving, abort gradient descent early
42 | LEARNING_RATE = 1e-2 # larger values converge faster to less accurate results
43 | IMAGE_SHAPE = (416, 416, 3) # input image shape
44 | SAVE_PATH = 'adv_examples/L2/f3/{0}/'.format(ATTACK_MODE)
45 | # select GPU to use
46 | os.environ["CUDA_VISIBLE_DEVICES"] = '{0}'.format(GPU_ID)
47 |
48 |
49 | def process_image(img):
50 | """
51 | Resize, reduce and expand image.
52 | # Argument:
53 | img: original image.
54 |
55 | # Returns
56 | image: ndarray(64, 64, 3), processed image.
57 | """
58 | image = cv2.resize(img, (416, 416),
59 | interpolation=cv2.INTER_CUBIC)
60 | image = np.array(image, dtype='float32')
61 | image /= 255.
62 | image = np.expand_dims(image, axis=0)
63 | return image
64 |
65 | class Daedalus:
66 | """
67 | Daedalus adversarial example generator based on the Yolo v3 model.
68 | """
69 | def __init__(self, sess, model, target_class=ATTACK_CLASS, attack_mode=ATTACK_MODE, img_shape=IMAGE_SHAPE,
70 | batch_size=BATCH_SIZE, confidence=CONFIDENCE, learning_rate=LEARNING_RATE, binary_search_steps=BINARY_SEARCH_STEPS,
71 | max_iterations=MAX_ITERATIONS, abort_early=ABORT_EARLY, initial_consts=INITIAL_consts, boxmin=0, boxmax=1):
72 |
73 | # self.sess = tf_debug.LocalCLIDebugWrapperSession(sess)
74 | self.sess = sess
75 | self.LEARNING_RATE = learning_rate
76 | self.MAX_ITERATIONS = max_iterations
77 | self.BINARY_SEARCH_STEPS = binary_search_steps
78 | self.ABORT_EARLY = abort_early
79 | self.initial_consts = initial_consts
80 | self.batch_size = batch_size
81 | self.repeat = binary_search_steps >= 6
82 | self.detection_model = model
83 | self.confidence = confidence
84 | self.img_dimension = img_shape[0]
85 | self.target_class = target_class
86 | self.attack_mode = attack_mode
87 |
88 | def select_class(target_class, boxes, objectness, box_scores, mode='all'):
89 | box_classes = tf.cast(tf.argmax(box_scores, axis=-1), tf.int32, name='box_classes')
90 | class_counts = tf.bincount(box_classes)
91 | print(class_counts)
92 | if mode == 'all':
93 | selected_boxes = tf.reshape(boxes, [BATCH_SIZE, -1, 4])
94 | selected_scores = tf.reshape(box_scores, [BATCH_SIZE, -1, CLASS_NUM])
95 | if objectness == None:
96 | return selected_boxes, None, selected_scores
97 | selected_objectness = tf.reshape(objectness, [BATCH_SIZE, -1, 1])
98 | return selected_boxes, selected_objectness, selected_scores
99 | elif mode == 'most':
100 | selected_cls = tf.argmax(class_counts)
101 | elif mode == 'least':
102 | class_counts = tf.where(tf.equal(class_counts,0), int(1e6)*tf.ones_like(class_counts, dtype=tf.int32), class_counts)
103 | selected_cls = tf.argmin(class_counts)
104 | elif mode == 'single':
105 | file = 'data/coco_classes.txt'
106 | with open(file) as f:
107 | class_names = f.readlines()
108 | class_names = [c.strip() for c in class_names]
109 | selected_cls = class_names.index(target_class)
110 | selected_cls = tf.cast(selected_cls, tf.int32)
111 | index = tf.equal(box_classes, selected_cls)
112 | index = tf.cast(index, tf.int32)
113 | _, selected_boxes = tf.dynamic_partition(boxes, index, num_partitions=2, name='dynamic_partition')
114 | _, selected_scores = tf.dynamic_partition(box_scores, index, num_partitions=2, name='dynamic_partition')
115 | selected_boxes = tf.reshape(selected_boxes, [BATCH_SIZE, -1, 4])
116 | selected_scores = tf.reshape(selected_scores, [BATCH_SIZE, -1, CLASS_NUM])
117 | if objectness == None:
118 | return selected_boxes, None, selected_scores
119 | _, selected_objectness = tf.dynamic_partition(objectness, index, num_partitions=2, name='dynamic_partition')
120 | selected_objectness = tf.reshape(selected_objectness, [BATCH_SIZE, -1, 1])
121 | return selected_boxes, selected_objectness, selected_scores
122 |
123 | # the perturbation we're going to optimize:
124 | perturbations = tf.Variable(np.zeros((batch_size,
125 | img_shape[0],
126 | img_shape[1],
127 | img_shape[2])), dtype=tf.float32, name='perturbations')
128 | # tf variables to sending data to tf:
129 | self.timgs = tf.Variable(np.zeros((batch_size,
130 | img_shape[0],
131 | img_shape[1],
132 | img_shape[2])), dtype=tf.float32, name='self.timgs')
133 | self.consts = tf.Variable(np.zeros(batch_size), dtype=tf.float32, name='self.consts')
134 |
135 | # and here's what we use to assign them:
136 | self.assign_timgs = tf.placeholder(tf.float32, (batch_size,
137 | img_shape[0],
138 | img_shape[1],
139 | img_shape[2]))
140 | self.assign_consts = tf.placeholder(tf.float32, [batch_size])
141 |
142 | # Tensor operation: the resulting image, tanh'd to keep bounded from
143 | # boxmin to boxmax:
144 | self.boxmul = (boxmax - boxmin) / 2.
145 | self.boxplus = (boxmin + boxmax) / 2.
146 | self.newimgs = tf.tanh(perturbations + self.timgs) * self.boxmul + self.boxplus
147 |
148 | caffe_imgs = self.newimgs * 255.
149 | caffe_imgs = caffe_imgs[..., ::-1]
150 | caffe_offsets = np.concatenate([103.939*np.ones((batch_size, 416, 416, 1)),
151 | 116.779*np.ones((batch_size, 416, 416, 1)),
152 | 123.68*np.ones((batch_size, 416, 416, 1))], axis=-1)
153 | caffe_imgs = caffe_imgs - caffe_offsets
154 |
155 | # Get prediction from the model:
156 | boxes, classprobs = self.detection_model(caffe_imgs)
157 | boxes, _, classprobs = select_class(self.target_class, boxes, None, classprobs, mode=self.attack_mode)
158 | print(boxes, classprobs)
159 | self.x1 = boxes[..., 0:1]/self.img_dimension
160 | self.y1 = boxes[..., 1:2]/self.img_dimension
161 | self.x2 = boxes[..., 2:3]/self.img_dimension
162 | self.y2 = boxes[..., 3:4]/self.img_dimension
163 | self.bw = tf.math.abs(self.x2 - self.x1)
164 | self.bh = tf.math.abs(self.y1 - self.y2)
165 | self.class_probs = classprobs
166 | self.box_scores = tf.reduce_max(self.class_probs, axis=-1, keepdims=True)
167 |
168 | # Optimisation metrics:
169 | self.l2dist = tf.reduce_sum(tf.square(self.newimgs - (tf.tanh(self.timgs) * self.boxmul + self.boxplus)), [1, 2, 3])
170 |
171 | # Define DDoS losses: loss must be a tensor here!
172 | # Make the box confidence of all detections to be 1.
173 | self.loss1_1_x = tf.reduce_mean(tf.square(self.box_scores - 1), [-2, -1])
174 |
175 | # Minimising the size of all bounding box.
176 | self.f3 = 1e1 * tf.reduce_mean(tf.square(tf.multiply(self.bw, self.bh)), [-2, -1])
177 |
178 | # add two loss terms together
179 | self.loss_adv = self.loss1_1_x + self.f3
180 | self.loss1 = tf.reduce_mean(self.consts * self.loss_adv)
181 | self.loss2 = tf.reduce_mean(self.l2dist)
182 | self.loss = self.loss1 + self.loss2
183 |
184 | # Setup the adam optimizer and keep track of variables we're creating
185 | start_vars = set(x.name for x in tf.global_variables())
186 | optimizer = tf.train.AdamOptimizer(self.LEARNING_RATE)
187 | self.train = optimizer.minimize(self.loss, var_list=[perturbations])
188 | end_vars = tf.global_variables()
189 | new_vars = [x for x in end_vars if x.name not in start_vars]
190 |
191 | # these are the variables to initialize when we run
192 | self.setup = []
193 | self.setup.append(self.timgs.assign(self.assign_timgs))
194 | self.setup.append(self.consts.assign(self.assign_consts))
195 | self.init = tf.variables_initializer(var_list=[perturbations] + new_vars)
196 |
197 | def attack_batch(self, imgs):
198 | """
199 | Run the attack on a batch of images and labels.
200 | """
201 |
202 | def check_success(loss, init_loss):
203 | """
204 | Check if the initial loss value has been reduced by 'self.confidence' percent
205 | """
206 | return loss <= init_loss * (1 - self.confidence)
207 |
208 | batch_size = self.batch_size
209 |
210 | # convert images to arctanh-space
211 | imgs = np.arctanh((imgs - self.boxplus) / self.boxmul * 0.999999)
212 |
213 | # set the lower and upper bounds of the constsant.
214 | lower_bound = np.zeros(batch_size)
215 | consts = np.ones(batch_size) * self.initial_consts
216 | upper_bound = np.ones(batch_size) * 1e10
217 |
218 | # store the best l2, score, and image attack
219 | o_bestl2 = [1e10] * batch_size
220 | o_bestloss = [1e10] * batch_size
221 | o_bestattack = [np.zeros(imgs[0].shape)] * batch_size
222 |
223 | for outer_step in range(self.BINARY_SEARCH_STEPS):
224 | # completely reset adam's internal state.
225 | self.sess.run(self.init)
226 |
227 | # take in the current data batch.
228 | batch = imgs[:batch_size]
229 |
230 | # cache the current best l2 and score.
231 | bestl2 = [1e10] * batch_size
232 | # bestconfidence = [-1]*batch_size
233 | bestloss = [1e10] * batch_size
234 |
235 | # The last iteration (if we run many steps) repeat the search once.
236 | if self.repeat and outer_step == self.BINARY_SEARCH_STEPS - 1:
237 | consts = upper_bound
238 |
239 | # set the variables so that we don't have to send them over again.
240 | self.sess.run(self.setup, {self.assign_timgs: batch,
241 | self.assign_consts: consts})
242 |
243 | # start gradient descent attack
244 | print('adjust c to:', sess.run(self.consts))
245 | init_loss = sess.run(self.loss)
246 | init_adv_losses = sess.run(self.loss_adv)
247 | prev = init_loss * 1.1
248 | for iteration in range(self.MAX_ITERATIONS):
249 | # perform the attack on a single example
250 | _, l, l2s, l1s, nimgs, c = self.sess.run([self.train, self.loss, self.l2dist, self.loss_adv, self.newimgs, self.consts])
251 | # print out the losses every 10%
252 | if iteration % (self.MAX_ITERATIONS // 10) == 0:
253 | print('===iteration:', iteration, '===')
254 | print('attacked box number:', sess.run(self.bw).shape)
255 | print('loss values of box confidence and dimension:', sess.run([self.loss1_1_x, self.f3]))
256 | print('adversarial losses:', l1s)
257 | print('distortions:', l2s)
258 | path = SAVE_PATH+'retinanet/{0} confidence'.format(self.confidence)
259 | if not os.path.exists(path):
260 | os.makedirs(path)
261 | #[io.imsave(path+'/debug_img_{0}Iteration_{1}.png'.format(i, iteration), nimgs[i]) for i in range(nimgs.shape[0])]
262 |
263 | # check if we should abort search if we're getting nowhere.
264 | if self.ABORT_EARLY and iteration % (self.MAX_ITERATIONS // 10) == 0:
265 | if l > prev * .9999:
266 | break
267 | prev = l
268 |
269 | # update the best result found so far
270 | for e, (l1, l2, ii) in enumerate(zip(l1s, l2s, nimgs)):
271 | if l2 < bestl2[e] and check_success(l1, init_adv_losses[e]):
272 | bestl2[e] = l2
273 | bestloss[e] = l1
274 | if l2 < o_bestl2[e] and check_success(l1, init_adv_losses[e]):
275 | o_bestl2[e] = l2
276 | o_bestloss[e] = l1
277 | o_bestattack[e] = ii
278 |
279 | # adjust the constsant as needed
280 | for e in range(batch_size):
281 | if check_success(l1s[e], init_adv_losses[e]):
282 | # success, divide consts by two
283 | upper_bound[e] = min(upper_bound[e], consts[e])
284 | if upper_bound[e] < 1e9:
285 | consts[e] = (lower_bound[e] + upper_bound[e]) / 2
286 | else:
287 | # failure, either multiply by 10 if no solution found yet
288 | # or do binary search with the known upper bound
289 | lower_bound[e] = max(lower_bound[e], consts[e])
290 | if upper_bound[e] < 1e9:
291 | consts[e] = (lower_bound[e] + upper_bound[e]) / 2
292 | else:
293 | consts[e] *= 10
294 | # return the best solution found
295 | o_bestl2 = np.array(o_bestl2)
296 | return o_bestattack, o_bestl2
297 |
298 |
299 | def attack(self, imgs):
300 | """
301 | Perform the L_2 attack on the given images for the given targets.
302 | If self.targeted is true, then the targets represents the target labels.
303 | If self.targeted is false, then targets are the original class labels.
304 | """
305 | r = []
306 | ds = []
307 | print('go up to', len(imgs))
308 | for i in range(0, len(imgs), self.batch_size):
309 | print('tick', i)
310 | X_adv, dists = self.attack_batch(imgs[i:i + self.batch_size])
311 | path = SAVE_PATH+'retinanet/{0} confidence'.format(self.confidence)
312 | if not os.path.exists(path):
313 | os.makedirs(path)
314 | np.save(path+'/Distortions of images {0} to {1}.npy'.format(i, i+self.batch_size), dists)
315 | for j in range(len(X_adv)):
316 | io.imsave(path+'/Best example of {1} Distortion {2}.png'.format(self.confidence, i+j, dists[j]), X_adv[j])
317 | r.extend(X_adv)
318 | ds.extend(dists)
319 | return np.array(r), np.array(ds)
320 |
321 |
322 | if __name__ == '__main__':
323 | sess = tf.InteractiveSession()
324 | init = tf.global_variables_initializer()
325 | sess.run(init)
326 | # models can be downloaded here: https://github.com/fizyr/keras-retinanet/releases
327 | model_path = os.path.join('model', 'resnet50_coco_best_v2.1.0.h5')
328 | # load retinanet model
329 | ORACLE = models.load_model(model_path, backbone_name='resnet50')
330 | ORACLE.layers.pop()
331 | ORACLE.outputs = [ORACLE.layers[-2].output, ORACLE.layers[-1].output] #remove nms from original model
332 | ORACLE.layers[-1].outbound_nodes = []
333 | ORACLE.summary()
334 | X_test = []
335 | for (root, dirs, files) in os.walk('../Datasets/COCO/val2017'):
336 | if files:
337 | for f in files:
338 | print(f)
339 | path = os.path.join(root, f)
340 | image = cv2.imread(path)
341 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # RGB
342 | image = process_image(image)
343 | X_test.append(image)
344 | EXAMPLE_NUM -= 1
345 | if EXAMPLE_NUM == 0:
346 | break
347 | X_test = np.concatenate(X_test, axis=0)
348 | attacker = Daedalus(sess, ORACLE)
349 | X_adv, distortions = attacker.attack(X_test)
350 | np.savez(SAVE_PATH+'retinanet/{0} confidence/Daedalus example batch.npz'.format(CONFIDENCE), X_adv=X_adv, distortions=distortions)
351 | writer = tf.summary.FileWriter("log", sess.graph)
352 | writer.close()
353 |
--------------------------------------------------------------------------------
/l2_yolov3.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | # supress tensorflow logging other than errors
4 | #os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
5 |
6 | from keras import backend as K
7 | import numpy as np
8 | import random as rd
9 | import tensorflow as tf
10 | from tensorflow.python import debug as tf_debug
11 | from keras.models import Model
12 | from keras import losses
13 |
14 | from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D
15 | from keras.layers import Conv2D, MaxPooling2D, Input
16 | from keras.layers import Dense, Dropout, Activation, Flatten
17 |
18 | from keras.models import load_model
19 | from keras.callbacks import EarlyStopping
20 |
21 | from model.yolo_model import YOLO
22 | import cv2
23 | import matplotlib.pyplot as plt
24 | from skimage import io
25 | import time
26 |
27 | # Parameter settings:
28 | GPU_ID = 0 # which gpu to used
29 | ATTACK_MODE = 'all' # select attack mode from 'all', 'most', 'least' and 'single';
30 | ATTACK_CLASS = 'person' # select the class to attack in 'single' mode
31 | CONFIDENCE = 0.3 # the confidence of attack
32 | EXAMPLE_NUM = 10 # total number of adversarial example to generate.
33 | BATCH_SIZE = 1 # number of adversarial example generated in each batch
34 |
35 | BINARY_SEARCH_STEPS = 5 # number of times to adjust the constsant with binary search
36 | INITIAL_consts = 2 # the initial constsant c to pick as a first guess
37 | CLASS_NUM = 80 # 80 for COCO dataset
38 | MAX_ITERATIONS = 10000 # number of iterations to perform gradient descent
39 | ABORT_EARLY = True # if we stop improving, abort gradient descent early
40 | LEARNING_RATE = 1e-2 # larger values converge faster to less accurate results
41 | IMAGE_SHAPE = (416, 416, 3) # input image shape
42 | SAVE_PATH = 'adv_examples/L2/f3/{0}/'.format(ATTACK_MODE)
43 | # select GPU to use
44 | os.environ["CUDA_VISIBLE_DEVICES"] = '{0}'.format(GPU_ID)
45 |
46 |
47 | def process_image(img):
48 | """
49 | Resize, reduce and expand image.
50 | # Argument:
51 | img: original image.
52 |
53 | # Returns
54 | image: ndarray(64, 64, 3), processed image.
55 | """
56 | image = cv2.resize(img, (416, 416),
57 | interpolation=cv2.INTER_CUBIC)
58 | image = np.array(image, dtype='float32')
59 | image /= 255.
60 | image = np.expand_dims(image, axis=0)
61 | return image
62 |
63 |
64 | def process_yolo_output(out, anchors, mask):
65 | """
66 | Tensor op: Process output features.
67 | # Arguments
68 | out - tensor (N, S, S, 3, 4+1+80), output feature map of yolo.
69 | anchors - List, anchors for box.
70 | mask - List, mask for anchors.
71 | # Returns
72 | boxes - tensor (N, S, S, 3, 4), x,y,w,h for per box.
73 | box_confidence - tensor (N, S, S, 3, 1), confidence for per box.
74 | box_class_probs - tensor (N, S, S, 3, 80), class probs for per box.
75 | """
76 | batchsize, grid_h, grid_w, num_boxes = map(int, out.shape[0:4])
77 |
78 | box_confidence = tf.sigmoid(out[..., 4:5], name='objectness') # (N, S, S, 3, 1)
79 | box_class_probs = tf.sigmoid(out[..., 5:], name='class_probs') # (N, S, S, 3, 80)
80 |
81 | anchors = np.array([anchors[i] for i in mask]) # Dimension of the used three anchor boxes [[x,x], [x,x], [x,x]].
82 | # duplicate to shape (batch, height, width, num_anchors, box_params).
83 | anchors = np.repeat(anchors[np.newaxis, :, :], grid_w, axis=0) # (S, 3, 2)
84 | anchors = np.repeat(anchors[np.newaxis, :, :, :], grid_h, axis=0) # (S, S, 3, 2)
85 | anchors = np.repeat(anchors[np.newaxis, :, :, :, :], batchsize, axis=0) # (N, S, S, 3, 2)
86 | anchors_tensors = tf.constant(anchors, dtype=tf.float32, name='anchor_tensors')
87 |
88 | box_xy = tf.sigmoid(out[..., 0:2], name='box_xy') # (N, S, S, 3, 2)
89 | box_wh = tf.identity(tf.exp(out[..., 2:4]) * anchors_tensors, name='box_wh') # (N, S, S, 3, 2)
90 |
91 | col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
92 | row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)
93 |
94 | col = col.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
95 | row = row.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
96 | grid = np.concatenate((col, row), axis=-1) #(13, 13, 3, 2)
97 | grid_batch = np.repeat(grid[np.newaxis, :, :, :, :], batchsize, axis=0)
98 | box_xy += grid_batch
99 | box_xy /= (grid_w, grid_h)
100 | box_wh /= (416, 416)
101 | box_xy -= (box_wh / 2.)
102 |
103 | # boxes -> (N, S, S, 3, 4)
104 | boxes = tf.concat([box_xy, box_wh], axis=-1)
105 | boxes = tf.reshape(boxes, [batchsize, -1, boxes.shape[-2], boxes.shape[-1]], name='boxes') #(N, S*S, 3, 4)
106 | # box_confidence -> (N, S, S, 3, 1) or 26 or 52
107 | # box_class_probs -> (N, S, S, 3, 80)
108 | box_confidence = tf.reshape(box_confidence, [batchsize,
109 | -1,
110 | box_confidence.shape[-2],
111 | box_confidence.shape[-1]], name='box_confidence')
112 | box_class_probs = tf.reshape(box_class_probs, [batchsize,
113 | -1,
114 | box_class_probs.shape[-2],
115 | box_class_probs.shape[-1]], name='class_probs')
116 | return boxes, box_confidence, box_class_probs
117 |
118 |
119 | def process_output(raw_outs):
120 | """
121 | Tensor op: Extract b, c, and s from raw outputs.
122 | # Args:
123 | raw_outs - Yolo raw output tensor list [(N, 13, 13, 3, 85), (N, 26, 26, 3, 85), (N, 26, 26, 3, 85)].
124 | # Returns:
125 | boxes - Tensors. (N, 3549, 3, 4), classes: (N, 3549, 3, 1), scores: (N, 3549, 3, 80)
126 | """
127 | masks = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
128 | anchors = [[10, 13], [16, 30], [33, 23],
129 | [30, 61], [62, 45], [59, 119],
130 | [116, 90], [156, 198], [373, 326]]
131 | boxes, objecness, scores = [], [], []
132 |
133 | for out, mask in zip(raw_outs, masks):
134 | # out -> (N, 13, 13, 3, 85)
135 | # mask -> one of the masks
136 | # boxes (N, 13X13, 3, 4), box_confidence (N, 13X13, 3, 1)
137 | # box_class_probs (13X13, 3, 80) | 26 X 26 |
138 | b, c, s = process_yolo_output(out, anchors, mask)
139 | if boxes == []:
140 | boxes = b
141 | objecness = c
142 | scores = s
143 | else:
144 | boxes = tf.concat([boxes, b], 1, name='xywh')
145 | objecness = tf.concat([objecness, c], 1, name='objectness')
146 | scores = tf.concat([scores, s], 1, name='class_probs')
147 | return boxes, objecness, scores
148 |
149 |
150 | def pdist(xy):
151 | """
152 | Tensor op: Computes pairwise distance between each pair of points
153 | # Args:
154 | xy - [N,2] matrix representing N box position coordinates
155 | # Content:
156 | dists - [N,N] matrix of (squared) Euclidean distances
157 | # Return:
158 | expectation of the Euclidean distances
159 | """
160 | xy2 = tf.reduce_sum(xy * xy, 1, True)
161 | dists = xy2 - 2 * tf.matmul(xy, tf.transpose(xy)) + tf.transpose(xy2)
162 | return tf.reduce_mean(dists)
163 |
164 | def output_to_pdist(bx, by):
165 | """
166 | Tensor op: calculate expectation of box distance given yolo outpput bx & by.
167 | # Args:
168 | bx - YOLOv3 output batch x coordinates in shape (N, 3549, 3, 1)
169 | by - YOLOv3 output batch y coordinates in shape (N, 3549, 3, 1)
170 | """
171 | bxby = tf.concat([bx, by], axis=-1)
172 | bxby = tf.reshape(bxby, [-1, 2])
173 | return pdist(bxby)
174 |
175 | def pairwise_IoUs(bs1, bs2):
176 | """
177 | Tensor op: Calculate pairwise IoUs given two sets of boxes.
178 | # Arguments:
179 | bs1, bs2 - tensor of boxes in shape (?, 4)
180 | # Content:
181 | X11,y11------x12,y11 X21,y21------x22,y21
182 | | | | |
183 | | | | |
184 | x11,y12-------x12,y12 x21,y22-------x22,y22
185 | # Returns:
186 | iou - a tensor of the matrix containing pairwise IoUs, in shape (?, ?)
187 | """
188 | x11, y11, w1, h1 = tf.split(bs1, 4, axis=1) # (N, 1)
189 | x21, y21, w2, h2 = tf.split(bs2, 4, axis=1) # (N, 1)
190 | x12 = x11 + w1
191 | y12 = y11 + h1
192 | x22 = x21 + w2
193 | y22 = y21 + h2
194 | xA = tf.maximum(x11, tf.transpose(x21))
195 | yA = tf.maximum(y11, tf.transpose(y21))
196 | xB = tf.minimum(x12, tf.transpose(x22))
197 | yB = tf.minimum(y12, tf.transpose(y22))
198 | # prevent 0 area
199 | interArea = tf.maximum((xB - xA + 1), 0) * tf.maximum((yB - yA + 1), 0)
200 |
201 | boxAArea = (x12 - x11 + 1) * (y12 - y11 + 1)
202 | boxBArea = (x22 - x21 + 1) * (y22 - y21 + 1)
203 |
204 | iou = interArea / (boxAArea + tf.transpose(boxBArea) - interArea)
205 | print('iou', iou)
206 | return iou
207 |
208 |
209 | def expectation_of_IoUs(boxes):
210 | """
211 | Tensor op: Calculate the expectation given all pairwise IoUs.
212 | # Arguments
213 | boxes - boxes of objects. It takes (?, 4) shaped tensor;
214 | # Returns
215 | expt - expectation of IoUs of box pairs. Scalar tensor.
216 | """
217 | IoUs = pairwise_IoUs(boxes, boxes)
218 | expt = tf.reduce_mean(IoUs)
219 | return expt
220 |
221 | def expectation_of_IoUs_accross_classes(boxes, box_scores):
222 | """
223 | Tensor op: Calculate IoU expectation for IoU expectations from different class.
224 | Arguments:
225 | #boxes - (3549, 3, 4) tensor output from yolo net
226 | #box_scores - (N1**2+N2**2+N3**2, 3, 80) tensor
227 | Content:
228 | #box_classes - (N1**2+N2**2+N3**2, 3, 1) tensor
229 | Returns:
230 | #expt_over_all_classes - The IoU expectation of box pairs over all classes.
231 | """
232 | box_classes = tf.cast(tf.argmax(box_scores, axis=-1), tf.int32, name='box_classes')
233 | class_counts = tf.bincount(box_classes)
234 | dominating_cls = tf.argmax(class_counts)
235 | dominating_cls = tf.cast(dominating_cls, tf.int32)
236 | index = tf.equal(box_classes, dominating_cls)
237 | index = tf.cast(index, tf.int32)
238 | others, dominating_boxes = tf.dynamic_partition(boxes, index, num_partitions=2, name='dynamic_partition')
239 | expt_over_all_classes = expectation_of_IoUs(dominating_boxes)
240 | return expt_over_all_classes
241 |
242 | class Daedalus:
243 | """
244 | Daedalus adversarial example generator based on the Yolo v3 model.
245 | """
246 | def __init__(self, sess, model, target_class=ATTACK_CLASS, attack_mode=ATTACK_MODE, img_shape=IMAGE_SHAPE,
247 | batch_size=BATCH_SIZE, confidence=CONFIDENCE, learning_rate=LEARNING_RATE, binary_search_steps=BINARY_SEARCH_STEPS,
248 | max_iterations=MAX_ITERATIONS, abort_early=ABORT_EARLY, initial_consts=INITIAL_consts, boxmin=0, boxmax=1):
249 |
250 | # self.sess = tf_debug.LocalCLIDebugWrapperSession(sess)
251 | self.sess = sess
252 | self.LEARNING_RATE = learning_rate
253 | self.MAX_ITERATIONS = max_iterations
254 | self.BINARY_SEARCH_STEPS = binary_search_steps
255 | self.ABORT_EARLY = abort_early
256 | self.initial_consts = initial_consts
257 | self.batch_size = batch_size
258 | self.repeat = binary_search_steps >= 6
259 | self.yolo_model = model
260 | self.confidence = confidence
261 | self.target_class = target_class
262 | self.attack_mode = attack_mode
263 |
264 | def select_class(target_class, boxes, objectness, box_scores, mode='all'):
265 | box_classes = tf.cast(tf.argmax(box_scores, axis=-1), tf.int32, name='box_classes')
266 | class_counts = tf.bincount(box_classes)
267 | print(class_counts)
268 | if mode == 'all':
269 | selected_boxes = tf.reshape(boxes, [BATCH_SIZE, -1, 4])
270 | selected_scores = tf.reshape(box_scores, [BATCH_SIZE, -1, CLASS_NUM])
271 | if objectness == None:
272 | return selected_boxes, None, selected_scores
273 | selected_objectness = tf.reshape(objectness, [BATCH_SIZE, -1, 1])
274 | return selected_boxes, selected_objectness, selected_scores
275 | elif mode == 'most':
276 | selected_cls = tf.argmax(class_counts)
277 | elif mode == 'least':
278 | class_counts = tf.where(tf.equal(class_counts,0), int(1e6)*tf.ones_like(class_counts, dtype=tf.int32), class_counts)
279 | selected_cls = tf.argmin(class_counts)
280 | elif mode == 'single':
281 | file = 'data/coco_classes.txt'
282 | with open(file) as f:
283 | class_names = f.readlines()
284 | class_names = [c.strip() for c in class_names]
285 | selected_cls = class_names.index(target_class)
286 | selected_cls = tf.cast(selected_cls, tf.int32)
287 | index = tf.equal(box_classes, selected_cls)
288 | index = tf.cast(index, tf.int32)
289 | _, selected_boxes = tf.dynamic_partition(boxes, index, num_partitions=2, name='dynamic_partition')
290 | _, selected_scores = tf.dynamic_partition(box_scores, index, num_partitions=2, name='dynamic_partition')
291 | selected_boxes = tf.reshape(selected_boxes, [BATCH_SIZE, -1, 4])
292 | selected_scores = tf.reshape(selected_scores, [BATCH_SIZE, -1, CLASS_NUM])
293 | if objectness == None:
294 | return selected_boxes, None, selected_scores
295 | _, selected_objectness = tf.dynamic_partition(objectness, index, num_partitions=2, name='dynamic_partition')
296 | selected_objectness = tf.reshape(selected_objectness, [BATCH_SIZE, -1, 1])
297 | return selected_boxes, selected_objectness, selected_scores
298 |
299 | # the perturbation we're going to optimize:
300 | perturbations = tf.Variable(np.zeros((batch_size,
301 | img_shape[0],
302 | img_shape[1],
303 | img_shape[2])), dtype=tf.float32, name='perturbations')
304 | # tf variables to sending data to tf:
305 | self.timgs = tf.Variable(np.zeros((batch_size,
306 | img_shape[0],
307 | img_shape[1],
308 | img_shape[2])), dtype=tf.float32, name='self.timgs')
309 | self.consts = tf.Variable(np.zeros(batch_size), dtype=tf.float32, name='self.consts')
310 |
311 | # and here's what we use to assign them:
312 | self.assign_timgs = tf.placeholder(tf.float32, (batch_size,
313 | img_shape[0],
314 | img_shape[1],
315 | img_shape[2]))
316 | self.assign_consts = tf.placeholder(tf.float32, [batch_size])
317 |
318 | # Tensor operation: the resulting image, tanh'd to keep bounded from
319 | # boxmin to boxmax:
320 | self.boxmul = (boxmax - boxmin) / 2.
321 | self.boxplus = (boxmin + boxmax) / 2.
322 | self.newimgs = tf.tanh(perturbations + self.timgs) * self.boxmul + self.boxplus
323 |
324 | # Get prediction from the model:
325 | outs = self.yolo_model._yolo(self.newimgs)
326 | # [(N, 13, 13, 3, 85), (N, 26, 26, 3, 85), (N, 52, 52, 3, 85)]
327 | print(outs)
328 | # (N, 3549, 3, 4), (N, 3549, 3, 1), (N, 3549, 3, 80)
329 | boxes, objectness, classprobs = process_output(outs)
330 | boxes, objectness, classprobs = select_class(self.target_class, boxes, objectness, classprobs, mode=self.attack_mode)
331 | print(boxes, objectness, classprobs)
332 | self.bx = boxes[..., 0:1]
333 | self.by = boxes[..., 1:2]
334 | self.bw = boxes[..., 2:3]
335 | self.bh = boxes[..., 3:4]
336 | self.obj_scores = objectness
337 | self.class_probs = classprobs
338 | self.box_scores = tf.multiply(self.obj_scores, tf.reduce_max(self.class_probs, axis=-1, keepdims=True))
339 |
340 | # Optimisation metrics:
341 | self.l2dist = tf.reduce_sum(tf.square(self.newimgs - (tf.tanh(self.timgs) * self.boxmul + self.boxplus)), [1, 2, 3])
342 |
343 | # Define DDoS losses: loss must be a tensor here!
344 | # Make the objectness of all detections to be 1.
345 | self.loss1_1_x = tf.reduce_mean(tf.square(self.box_scores - 1), [-2, -1])
346 |
347 | # Minimising the size of all bounding box.
348 | #self.f1 = tf.reduce_mean(IoU_expts)
349 | self.f3 = tf.reduce_mean(tf.square(tf.multiply(self.bw, self.bh)), [-2, -1])
350 | #self.f2 = self.f3 + 1/output_to_pdist(self.bx, self.by)
351 |
352 | # add two loss terms together
353 | self.loss_adv = self.loss1_1_x + self.f3
354 | self.loss1 = tf.reduce_mean(self.consts * self.loss_adv)
355 | self.loss2 = tf.reduce_mean(self.l2dist)
356 | self.loss = self.loss1 + self.loss2
357 |
358 | # Setup the adam optimizer and keep track of variables we're creating
359 | start_vars = set(x.name for x in tf.global_variables())
360 | optimizer = tf.train.AdamOptimizer(self.LEARNING_RATE)
361 | self.train = optimizer.minimize(self.loss, var_list=[perturbations])
362 | end_vars = tf.global_variables()
363 | new_vars = [x for x in end_vars if x.name not in start_vars]
364 |
365 | # these are the variables to initialize when we run
366 | self.setup = []
367 | self.setup.append(self.timgs.assign(self.assign_timgs))
368 | self.setup.append(self.consts.assign(self.assign_consts))
369 | self.init = tf.variables_initializer(var_list=[perturbations] + new_vars)
370 |
371 | def attack_batch(self, imgs):
372 | """
373 | Run the attack on a batch of images and labels.
374 | """
375 |
376 | def check_success(loss, init_loss):
377 | """
378 | Check if the initial loss value has been reduced by 'self.confidence' percent
379 | """
380 | return loss <= init_loss * (1 - self.confidence)
381 |
382 | batch_size = self.batch_size
383 |
384 | # convert images to arctanh-space
385 | imgs = np.arctanh((imgs - self.boxplus) / self.boxmul * 0.999999)
386 |
387 | # set the lower and upper bounds of the constsant.
388 | lower_bound = np.zeros(batch_size)
389 | consts = np.ones(batch_size) * self.initial_consts
390 | upper_bound = np.ones(batch_size) * 1e10
391 |
392 | # store the best l2, score, and image attack
393 | o_bestl2 = [1e10] * batch_size
394 | o_bestloss = [1e10] * batch_size
395 | o_bestattack = [np.zeros(imgs[0].shape)] * batch_size
396 |
397 | for outer_step in range(self.BINARY_SEARCH_STEPS):
398 | # completely reset adam's internal state.
399 | self.sess.run(self.init)
400 |
401 | # take in the current data batch.
402 | batch = imgs[:batch_size]
403 |
404 | # cache the current best l2 and score.
405 | bestl2 = [1e10] * batch_size
406 | # bestconfidence = [-1]*batch_size
407 | bestloss = [1e10] * batch_size
408 |
409 | # The last iteration (if we run many steps) repeat the search once.
410 | if self.repeat and outer_step == self.BINARY_SEARCH_STEPS - 1:
411 | consts = upper_bound
412 |
413 | # set the variables so that we don't have to send them over again.
414 | self.sess.run(self.setup, {self.assign_timgs: batch,
415 | self.assign_consts: consts})
416 |
417 | # start gradient descent attack
418 | print('adjust c to:', sess.run(self.consts))
419 | init_loss = sess.run(self.loss)
420 | init_adv_losses = sess.run(self.loss_adv)
421 | prev = init_loss * 1.1
422 | for iteration in range(self.MAX_ITERATIONS):
423 | # perform the attack on a single example
424 | _, l, l2s, l1s, nimgs, c = self.sess.run([self.train, self.loss, self.l2dist, self.loss_adv, self.newimgs, self.consts])
425 | # print out the losses every 10%
426 | if iteration % (self.MAX_ITERATIONS // 10) == 0:
427 | print('===iteration:', iteration, '===')
428 | print('attacked box number:', sess.run(self.bw).shape)
429 | print('loss values of box confidence and dimension:', sess.run([self.loss1_1_x, self.f3]))
430 | print('adversarial losses:', l1s)
431 | print('distortions:', l2s)
432 |
433 | # check if we should abort search if we're getting nowhere.
434 | if self.ABORT_EARLY and iteration % (self.MAX_ITERATIONS // 10) == 0:
435 | if l > prev * .9999:
436 | break
437 | prev = l
438 |
439 | # update the best result found so far
440 | for e, (l1, l2, ii) in enumerate(zip(l1s, l2s, nimgs)):
441 | if l2 < bestl2[e] and check_success(l1, init_adv_losses[e]):
442 | bestl2[e] = l2
443 | bestloss[e] = l1
444 | if l2 < o_bestl2[e] and check_success(l1, init_adv_losses[e]):
445 | o_bestl2[e] = l2
446 | o_bestloss[e] = l1
447 | o_bestattack[e] = ii
448 |
449 | # adjust the constsant as needed
450 | for e in range(batch_size):
451 | if check_success(l1s[e], init_adv_losses[e]):
452 | # success, divide consts by two
453 | upper_bound[e] = min(upper_bound[e], consts[e])
454 | if upper_bound[e] < 1e9:
455 | consts[e] = (lower_bound[e] + upper_bound[e]) / 2
456 | else:
457 | # failure, either multiply by 10 if no solution found yet
458 | # or do binary search with the known upper bound
459 | lower_bound[e] = max(lower_bound[e], consts[e])
460 | if upper_bound[e] < 1e9:
461 | consts[e] = (lower_bound[e] + upper_bound[e]) / 2
462 | else:
463 | consts[e] *= 10
464 | # return the best solution found
465 | o_bestl2 = np.array(o_bestl2)
466 | return o_bestattack, o_bestl2
467 |
468 |
469 | def attack(self, imgs):
470 | """
471 | Perform the L_2 attack on the given images for the given targets.
472 | If self.targeted is true, then the targets represents the target labels.
473 | If self.targeted is false, then targets are the original class labels.
474 | """
475 | r = []
476 | ds = []
477 | print('go up to', len(imgs))
478 | for i in range(0, len(imgs), self.batch_size):
479 | print('tick', i)
480 | X_adv, dists = self.attack_batch(imgs[i:i + self.batch_size])
481 | path = SAVE_PATH+'{0} confidence'.format(self.confidence)
482 | if not os.path.exists(path):
483 | os.makedirs(path)
484 | np.save(path+'/Distortions of images {0} to {1}.npy'.format(i, i+self.batch_size), dists)
485 | for j in range(len(X_adv)):
486 | io.imsave(path+'/Best example of {1} Distortion {2}.png'.format(self.confidence, i+j, dists[j]), X_adv[j])
487 | r.extend(X_adv)
488 | ds.extend(dists)
489 | return np.array(r), np.array(ds)
490 |
491 |
492 | if __name__ == '__main__':
493 | sess = tf.InteractiveSession()
494 | init = tf.global_variables_initializer()
495 | sess.run(init)
496 | ORACLE = YOLO(0.6, 0.5) # The auguments do not matter.
497 | X_test = []
498 | for (root, dirs, files) in os.walk('../Datasets/COCO/val2017/'):
499 | if files:
500 | for f in files:
501 | print(f)
502 | path = os.path.join(root, f)
503 | image = cv2.imread(path)
504 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # RGB
505 | image = process_image(image)
506 | #image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
507 | X_test.append(image)
508 | EXAMPLE_NUM -= 1
509 | if EXAMPLE_NUM == 0:
510 | break
511 | X_test = np.concatenate(X_test, axis=0)
512 | attacker = Daedalus(sess, ORACLE)
513 | X_adv, distortions = attacker.attack(X_test)
514 | np.savez(SAVE_PATH+'{0} confidence/Daedalus example batch.npz'.format(CONFIDENCE), X_adv=X_adv, distortions=distortions)
515 | writer = tf.summary.FileWriter("log", sess.graph)
516 | writer.close()
--------------------------------------------------------------------------------
/model/darknet53.py:
--------------------------------------------------------------------------------
1 | """Darknet-53 for yolo v3.
2 | """
3 | from keras.models import Model
4 | from keras.layers import Input, Conv2D, GlobalAveragePooling2D, Dense
5 | from keras.layers import add, Activation, BatchNormalization
6 | from keras.layers.advanced_activations import LeakyReLU
7 | from keras.regularizers import l2
8 |
9 |
10 | def conv2d_unit(x, filters, kernels, strides=1):
11 | """Convolution Unit
12 | This function defines a 2D convolution operation with BN and LeakyReLU.
13 |
14 | # Arguments
15 | x: Tensor, input tensor of conv layer.
16 | filters: Integer, the dimensionality of the output space.
17 | kernels: An integer or tuple/list of 2 integers, specifying the
18 | width and height of the 2D convolution window.
19 | strides: An integer or tuple/list of 2 integers,
20 | specifying the strides of the convolution along the width and
21 | height. Can be a single integer to specify the same value for
22 | all spatial dimensions.
23 |
24 | # Returns
25 | Output tensor.
26 | """
27 | x = Conv2D(filters, kernels,
28 | padding='same',
29 | strides=strides,
30 | activation='linear',
31 | kernel_regularizer=l2(5e-4))(x)
32 | x = BatchNormalization()(x)
33 | x = LeakyReLU(alpha=0.1)(x)
34 |
35 | return x
36 |
37 |
38 | def residual_block(inputs, filters):
39 | """Residual Block
40 | This function defines a 2D convolution operation with BN and LeakyReLU.
41 |
42 | # Arguments
43 | x: Tensor, input tensor of residual block.
44 | kernels: An integer or tuple/list of 2 integers, specifying the
45 | width and height of the 2D convolution window.
46 |
47 | # Returns
48 | Output tensor.
49 | """
50 | x = conv2d_unit(inputs, filters, (1, 1))
51 | x = conv2d_unit(x, 2 * filters, (3, 3))
52 | x = add([inputs, x])
53 | x = Activation('linear')(x)
54 |
55 | return x
56 |
57 |
58 | def stack_residual_block(inputs, filters, n):
59 | """Stacked residual Block
60 | """
61 | x = residual_block(inputs, filters)
62 |
63 | for i in range(n - 1):
64 | x = residual_block(x, filters)
65 |
66 | return x
67 |
68 |
69 | def darknet_base(inputs):
70 | """Darknet-53 base model.
71 | """
72 |
73 | x = conv2d_unit(inputs, 32, (3, 3))
74 |
75 | x = conv2d_unit(x, 64, (3, 3), strides=2)
76 | x = stack_residual_block(x, 32, n=1)
77 |
78 | x = conv2d_unit(x, 128, (3, 3), strides=2)
79 | x = stack_residual_block(x, 64, n=2)
80 |
81 | x = conv2d_unit(x, 256, (3, 3), strides=2)
82 | x = stack_residual_block(x, 128, n=8)
83 |
84 | x = conv2d_unit(x, 512, (3, 3), strides=2)
85 | x = stack_residual_block(x, 256, n=8)
86 |
87 | x = conv2d_unit(x, 1024, (3, 3), strides=2)
88 | x = stack_residual_block(x, 512, n=4)
89 |
90 | return x
91 |
92 |
93 | def darknet():
94 | """Darknet-53 classifier.
95 | """
96 | inputs = Input(shape=(416, 416, 3))
97 | x = darknet_base(inputs)
98 |
99 | x = GlobalAveragePooling2D()(x)
100 | x = Dense(1000, activation='softmax')(x)
101 |
102 | model = Model(inputs, x)
103 |
104 | return model
105 |
106 |
107 | if __name__ == '__main__':
108 | model = darknet()
109 | print(model.summary())
110 |
--------------------------------------------------------------------------------
/model/yolo_model.py:
--------------------------------------------------------------------------------
1 | """YOLO v3 output
2 | """
3 | import numpy as np
4 | import keras.backend as K
5 | from keras.models import load_model
6 |
7 | class YOLO:
8 | def __init__(self, obj_threshold, nms_threshold):
9 | """Init.
10 |
11 | # Arguments
12 | obj_threshold: Integer, threshold for object.
13 | nms_threshold: Integer, threshold for box.
14 | """
15 | self._t1 = obj_threshold
16 | self._t2 = nms_threshold
17 | self._yolo = load_model('../YOLOv3/data/yolo.h5')
18 |
19 | def _process_feats(self, out, anchors, mask):
20 | """process output features.
21 |
22 | # Arguments
23 | out: Tensor (N, N, 3, 4 + 1 +80), output feature map of yolo.
24 | anchors: List, anchors for box.
25 | mask: List, mask for anchors.
26 |
27 | # Returns
28 | boxes: ndarray (N, N, 3, 4), x,y,w,h for per box.
29 | box_confidence: ndarray (N, N, 3, 1), confidence for per box.
30 | box_class_probs: ndarray (N, N, 3, 80), class probs for per box.
31 | """
32 | grid_h, grid_w, num_boxes = map(int, out.shape[1: 4])
33 |
34 | anchors = [anchors[i] for i in mask]
35 | # Reshape to batch, height, width, num_anchors, box_params.
36 | anchors_tensor = K.reshape(K.variable(anchors),
37 | [1, 1, len(anchors), 2])
38 | out = out[0]
39 | box_xy = K.get_value(K.sigmoid(out[..., :2]))
40 | box_wh = K.get_value(K.exp(out[..., 2:4]) * anchors_tensor)
41 | box_confidence = K.get_value(K.sigmoid(out[..., 4]))
42 | box_confidence = np.expand_dims(box_confidence, axis=-1)
43 | box_class_probs = K.get_value(K.sigmoid(out[..., 5:]))
44 |
45 | col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
46 | row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)
47 |
48 | col = col.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
49 | row = row.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
50 | grid = np.concatenate((col, row), axis=-1)
51 |
52 | box_xy += grid
53 | box_xy /= (grid_w, grid_h)
54 | box_wh /= (416, 416)
55 | box_xy -= (box_wh / 2.)
56 | boxes = np.concatenate((box_xy, box_wh), axis=-1)
57 | #print('box confidences are: {0}',format(box_confidence))
58 | return boxes, box_confidence, box_class_probs
59 |
60 | def _filter_boxes(self, boxes, box_confidences, box_class_probs):
61 | """Filter boxes with object threshold.
62 |
63 | # Arguments
64 | boxes: ndarray, boxes of objects.
65 | box_confidences: ndarray, confidences of objects.
66 | box_class_probs: ndarray, class_probs of objects.
67 |
68 | # Returns
69 | boxes: ndarray, filtered boxes.
70 | classes: ndarray, classes for boxes.
71 | scores: ndarray, scores for boxes.
72 | """
73 | box_scores = box_confidences * box_class_probs
74 | box_classes = np.argmax(box_scores, axis=-1)
75 | box_class_scores = np.max(box_scores, axis=-1)
76 | pos = np.where(box_class_scores >= self._t1)
77 |
78 | boxes = boxes[pos]
79 | classes = box_classes[pos]
80 | scores = box_class_scores[pos]
81 |
82 | return boxes, classes, scores
83 |
84 | def _soft_nms(self, boxes, scores):
85 | """Suppress non-maximal boxes.
86 |
87 | # Arguments
88 | boxes: ndarray, boxes of objects.
89 | scores: ndarray, scores of objects.
90 |
91 | # Returns
92 | keep: ndarray, index of effective boxes.
93 | """
94 | x = boxes[:, 0]
95 | y = boxes[:, 1]
96 | w = boxes[:, 2]
97 | h = boxes[:, 3]
98 |
99 | areas = w * h
100 | order = scores.argsort()[::-1]
101 | keep = []
102 | while order.size > 0:
103 | i = order[0]
104 | keep.append(i)
105 | order = order[1:]
106 |
107 | xx1 = np.maximum(x[i], x[order])
108 | yy1 = np.maximum(y[i], y[order])
109 | xx2 = np.minimum(x[i] + w[i], x[order] + w[order])
110 | yy2 = np.minimum(y[i] + h[i], y[order] + h[order])
111 |
112 | w1 = np.maximum(0.0, xx2 - xx1 + 1)
113 | h1 = np.maximum(0.0, yy2 - yy1 + 1)
114 | inter = w1 * h1
115 |
116 | ovr = inter / (areas[i] + areas[order] - inter)
117 |
118 | scores[order] *= np.exp((ovr**2)/(-0.5)) # change scores based on IoU
119 | scores = scores[order]
120 | order = scores.argsort()[::-1] # re-order
121 |
122 | keep = np.array(keep)
123 | return keep
124 |
125 | def _nms_boxes(self, boxes, scores):
126 | """Suppress non-maximal boxes.
127 |
128 | # Arguments
129 | boxes: ndarray, boxes of objects.
130 | scores: ndarray, scores of objects.
131 |
132 | # Returns
133 | keep: ndarray, index of effective boxes.
134 | """
135 | x = boxes[:, 0]
136 | y = boxes[:, 1]
137 | w = boxes[:, 2]
138 | h = boxes[:, 3]
139 |
140 | areas = w * h
141 | order = scores.argsort()[::-1]
142 |
143 | keep = []
144 | while order.size > 0:
145 | i = order[0]
146 | keep.append(i)
147 |
148 | xx1 = np.maximum(x[i], x[order[1:]])
149 | yy1 = np.maximum(y[i], y[order[1:]])
150 | xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
151 | yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])
152 |
153 | w1 = np.maximum(0.0, xx2 - xx1 + 1)
154 | h1 = np.maximum(0.0, yy2 - yy1 + 1)
155 | inter = w1 * h1
156 |
157 | ovr = inter / (areas[i] + areas[order[1:]] - inter)
158 | inds = np.where(ovr <= self._t2)[0]
159 | order = order[inds + 1]
160 |
161 | keep = np.array(keep)
162 |
163 | return keep
164 |
165 | def _yolo_out(self, outs, shape):
166 | """Process output of yolo base net.
167 |
168 | # Argument:
169 | outs: output of yolo base net.
170 | shape: shape of original image.
171 |
172 | # Returns:
173 | boxes: ndarray, boxes of objects.
174 | classes: ndarray, classes of objects.
175 | scores: ndarray, scores of objects.
176 | """
177 | masks = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
178 | anchors = [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
179 | [59, 119], [116, 90], [156, 198], [373, 326]]
180 |
181 | boxes, classes, scores = [], [], []
182 |
183 | for out, mask in zip(outs, masks):
184 | b, c, s = self._process_feats(out, anchors, mask)
185 | b, c, s = self._filter_boxes(b, c, s)
186 | boxes.append(b)
187 | classes.append(c)
188 | scores.append(s)
189 |
190 | boxes = np.concatenate(boxes)
191 | classes = np.concatenate(classes)
192 | scores = np.concatenate(scores)
193 |
194 |
195 |
196 | # Scale boxes back to original image shape.
197 | width, height = shape[1], shape[0]
198 | image_dims = [width, height, width, height]
199 | boxes = boxes * image_dims
200 |
201 | nboxes, nclasses, nscores = [], [], []
202 | for c in set(classes):
203 | inds = np.where(classes == c)
204 | b = boxes[inds]
205 | c = classes[inds]
206 | s = scores[inds]
207 |
208 | keep = self._nms_boxes(b, s) #use NMS
209 | #keep = self._soft_nms(b, s) #use soft-NMS
210 |
211 | nboxes.append(b[keep])
212 | nclasses.append(c[keep])
213 | nscores.append(s[keep])
214 |
215 | if not nclasses and not nscores:
216 | return None, None, None
217 |
218 | boxes = np.concatenate(nboxes)
219 | classes = np.concatenate(nclasses)
220 | scores = np.concatenate(nscores)
221 |
222 | return boxes, classes, scores
223 |
224 | def predict(self, image, shape):
225 | """Detect the objects with yolo.
226 |
227 | # Arguments
228 | image: ndarray, processed input image.
229 | shape: shape of original image.
230 |
231 | # Returns
232 | boxes: ndarray, boxes of objects.
233 | classes: ndarray, classes of objects.
234 | scores: ndarray, scores of objects.
235 | """
236 |
237 | raw_outs = self._yolo.predict(image)
238 | boxes, classes, scores = self._yolo_out(raw_outs, shape)
239 |
240 | return boxes, classes, scores, raw_outs
241 |
--------------------------------------------------------------------------------
/resources/l2attack.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NeuralSec/Daedalus-attack/3f9cb38f6389cb7cd4895a2f9679aa5ce3c81e70/resources/l2attack.jpg
--------------------------------------------------------------------------------