ryanj commited on
Commit
9223e5e
β€’
1 Parent(s): 038d848

Initial Commit

Browse files
annotations/abercrombie_women.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2489e4154b2d035c7c0ddee66100e642da754c03e005e292a301618abe70cf06
3
+ size 67188
annotations/aritzia.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a9501466b2d4327aa29a2fdfcdb2de712821323dba16cbb7c200d5107ecf5814
3
+ size 90000
annotations/banana_annotations.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:61b679f59f989e012530ab5c3c2bf5246507b34c9aa4844ffe9e93d552e3fb71
3
+ size 49980
annotations/dynamite.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2d9babf9f122ebae53223e2ac8edb6c2666a304df614239876bab91c3b8e93fe
3
+ size 64847
annotations/hnm_annotations.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e5eb93941f94524318a41537e90c2a2df1ffdf094c19cb3f9b06f3e5bf7af18b
3
+ size 444989
annotations/street_annotations.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:573e43757b4348138156518f25dffed3260659382be2aa5c5ff47db56b7c18a4
3
+ size 80297
annotations/uniqlo_annotations.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:64a78ebb310af49d972e9b579b42fac8b8042ea5c06669610a8a542435cf649d
3
+ size 38733
app.py ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ subprocess.run(['wget', '-N', '-q', 'https://ampl.com/dl/open/ipopt/ipopt-linux64.zip'], stdout=subprocess.DEVNULL)
3
+ subprocess.run(['unzip', '-o', '-q', 'ipopt-linux64'], stdout=subprocess.DEVNULL)
4
+
5
+ from pyomo.environ import *
6
+ import pickle
7
+
8
+ import pickle
9
+ dict_files = {
10
+ 'banana republic': 'annotations/banana_annotations.pickle',
11
+ 'h&m': 'annotations/hnm_annotations.pickle',
12
+ 'uniqlo': 'annotations/uniqlo_annotations.pickle',
13
+ 'street': 'annotations/street_annotations.pickle',
14
+ 'abercrombie women': 'annotations/abercrombie_women.pickle',
15
+ 'aritzia': 'annotations/aritzia.pickle',
16
+ 'dynamite': 'annotations/dynamite.pickle',
17
+ }
18
+
19
+ style_gender = {
20
+ 'banana republic': 'male',
21
+ 'h&m': 'male',
22
+ 'uniqlo': 'male',
23
+ 'street': 'male',
24
+ 'abercrombie women': 'female',
25
+ 'aritzia': 'female',
26
+ 'dynamite': 'female',
27
+ }
28
+
29
+ gender_article_keys = {
30
+ 'male': ['shirt', 'pants_or_shorts', 'hoodie_or_jacket', 'hat', 'shoes'],
31
+ 'female': ['top', 'bottom', 'dress', 'hoodie_or_jacket', 'hat', 'shoes']
32
+ }
33
+
34
+ style_annotations_dict = {}
35
+
36
+ def get_annotations(dict_file):
37
+ with open(dict_file, 'rb') as f:
38
+ annotations = pickle.load(f)
39
+
40
+ for a in annotations:
41
+ if not( ( a.get('shirt', '') and a.get('pants_or_shorts', '') ) or
42
+ ( a.get('top', '') and a.get('bottom', '') ) or
43
+ a.get('dress', '')
44
+ ):
45
+ print( dict_file, '\n', a )
46
+
47
+ filtered_annotations = [ a for a in annotations if ( (a.get('shirt', '') and a.get('pants_or_shorts', '')) or (
48
+ a.get('gender', '') == 'female' ) ) ]
49
+ return filtered_annotations
50
+
51
+ style_annotations_dict = {
52
+ k: get_annotations(file_path) for k, file_path in dict_files.items()
53
+ }
54
+
55
+ import collections
56
+ from collections import defaultdict
57
+
58
+ optional = [False, False, True, True, False]
59
+ article_keys = ['shirt', 'pants_or_shorts', 'hoodie_or_jacket', 'hat', 'shoes']
60
+
61
+ def annotation_to_tuple(annotation):
62
+ t = tuple( annotation[key] for key in article_keys )
63
+ return t
64
+
65
+ def remove_owned_articles(annotations_tuple, clothes_owned):
66
+ return tuple( article if article not in clothes_owned else ''
67
+ for article in annotations_tuple )
68
+
69
+ def annotations_value(annotation_tuple_count, clothes_owned):
70
+ new_scores = {}
71
+ for annotation_tuple, count in annotation_tuple_count.items():
72
+ num_missing = 0
73
+ for clothing in annotation_tuple:
74
+
75
+ if not (clothing == '' or clothing in clothes_owned):
76
+ num_missing += 1
77
+ if num_missing > 0:
78
+ new_scores[annotation_tuple] = count/num_missing
79
+ return new_scores
80
+
81
+ def is_outfit_subset(smaller_outfit, larger_outfit):
82
+ if smaller_outfit == larger_outfit:
83
+ return False
84
+ for i in range(len(larger_outfit)): # assume both r tuples of fixed size
85
+ if smaller_outfit[i] != '' and smaller_outfit[i] != larger_outfit[i]:
86
+ return False
87
+ return True
88
+
89
+ def most_outfits_helper(annotations, capacity=5, clothes_owned=[]):
90
+ annotations = [ annotation_to_tuple(e) for e in annotations ]
91
+ outfit_count = dict(collections.Counter(annotations))
92
+
93
+ outfits = list(outfit_count.keys())
94
+ for small_outfit in outfit_count:
95
+ for larger_outfit in outfit_count:
96
+ if is_outfit_subset(small_outfit, larger_outfit):
97
+ outfit_count[small_outfit] += outfit_count[larger_outfit]
98
+
99
+ clothes_owned += ['']
100
+ best_outfits = most_outfits(outfit_count, capacity, set(clothes_owned))
101
+ best_outfits.remove('')
102
+ return best_outfits
103
+
104
+ # consider every image in the dataset
105
+ # each image has an outfit and casts a vote for that outfit
106
+ # a outfit is a tuple of pants_or_short + shirt + jacket_or_hoodie?
107
+ # images with shoes or hats have an additional vote for the shoes and hats
108
+ # greedily pick the clothes with the highest votes
109
+ # then remove those clothes from all outfits in that dataset and recount
110
+ # can be solved as an integer programming problem
111
+ def most_outfits(annotation_tuple_count, capacity, clothes_owned):
112
+ if capacity == 0 :
113
+ return clothes_owned
114
+
115
+ # merge counts based on new keys
116
+ updated_annotations = defaultdict(int)
117
+ for tup, count in annotation_tuple_count.items():
118
+ updated_annotations[ remove_owned_articles(tup, clothes_owned) ] += count
119
+ annotation_tuple_count = updated_annotations
120
+
121
+ outfits_scores = annotations_value(annotation_tuple_count, clothes_owned)
122
+ outfits_scores = sorted(outfits_scores.items(), key=lambda x:-x[1])
123
+ num_new = 1
124
+ for outfit, score in outfits_scores:
125
+ clothes_proposed = clothes_owned.union(set(outfit))
126
+ num_new = len(clothes_proposed) - len(clothes_owned)
127
+ if num_new <= capacity:
128
+ clothes_owned = clothes_proposed
129
+ break
130
+
131
+ return most_outfits( annotation_tuple_count, capacity-num_new, clothes_owned )
132
+
133
+ from pyomo.environ import *
134
+ from functools import reduce
135
+
136
+ def cover_style_ip(annotations, capacity=10, clothes_owned=[], mask=None, solver='ipopt'):
137
+ """Use integer program to find the set of clothes that makes as many outfits as possible.
138
+ annotations: List[ Dict ], contains maps from clothing categories to string types
139
+ capacity: int, number of new clothes to find
140
+ clothes_owned: List[ Str ], iterable of strings of clothing types
141
+ mask: Optional( List[ Str ] ), optional iterable of categories of clothes to consider.
142
+ Uses all clothing types by default.
143
+ solver: Str, the nonlinear optimization solver to use for max bool sat
144
+ """
145
+ unique_clothes = set()
146
+ interested_clothing_types = article_keys if mask is None else mask
147
+ for a in annotations:
148
+ for key in interested_clothing_types:
149
+ unique_clothes.add( a[key] )
150
+ if '' in unique_clothes:
151
+ unique_clothes.remove('')
152
+
153
+ model = ConcreteModel()
154
+
155
+ # 1. Create the variables we want to optimize
156
+ clothing_literals_dict = {clothing: Var(within=Binary) for clothing in unique_clothes}
157
+
158
+ # set literals to true for clothes already owned
159
+ for clothing in clothes_owned:
160
+ clothing_literals_dict[clothing] = 1
161
+
162
+ # capacity changes from number of new clothes to total clothes
163
+ capacity += len(clothes_owned)
164
+
165
+ capacity_constraint = Constraint(expr=sum(list(clothing_literals_dict.values())) <= capacity)
166
+
167
+ outfit_clauses = []
168
+ for a in annotations:
169
+ # get the clothing literal if it exists, otherwise it's masked and say it's absent
170
+ outfit_literals = [ clothing_literals_dict.get(a[key], 0) for key in article_keys if a[key] != '' ]
171
+ outfit_clauses += [ reduce(lambda x,y: x*y, outfit_literals) ]
172
+
173
+ # 3. Maximize the objective function
174
+ objective = Objective( expr=sum( outfit_clauses ), sense=maximize)
175
+
176
+ for name, literal in clothing_literals_dict.items():
177
+ setattr(model, name, literal)
178
+ model.capacity_constraint = capacity_constraint
179
+ model.objective = objective
180
+
181
+ SolverFactory(solver).solve(model)
182
+
183
+ basis_clothes = [ name for name, literal in clothing_literals_dict.items() if not isinstance(literal, int) and round(literal()) ]
184
+ return basis_clothes
185
+
186
+ def annotation_str(annotation):
187
+ output = ""
188
+ output += annotation['hat']
189
+ output += ' + ' + annotation['shirt'] if output and annotation['shirt'] else annotation['shirt']
190
+ output += ' + ' + annotation['hoodie_or_jacket'] if output and annotation['hoodie_or_jacket'] else annotation['hoodie_or_jacket']
191
+ output += ' + ' + annotation['pants_or_shorts'] if output and annotation['pants_or_shorts'] else annotation['pants_or_shorts']
192
+ output += ' + ' + annotation['shoes'] if output and annotation['shoes'] else annotation['shoes']
193
+ # output += ' ' + annotation['url'] if annotation.get('url') else ''
194
+ return output
195
+
196
+ def annotation_str(annotation):
197
+ gender = annotation.get('gender', 'male')
198
+ output = ""
199
+ for k in gender_article_keys[gender]:
200
+ output += ' + ' + annotation[k] if output and annotation.get(k) else annotation.get(k, '')
201
+ return output
202
+
203
+
204
+ def annotation_to_key(annotation):
205
+ gender = annotation.get('gender', 'male')
206
+ output = ""
207
+ if gender == 'male':
208
+ output += ' + ' + annotation['shirt'] if output and annotation['shirt'] else annotation['shirt']
209
+ output += ' + ' + annotation['hoodie_or_jacket'] if output and annotation['hoodie_or_jacket'] else annotation['hoodie_or_jacket']
210
+ output += ' + ' + annotation['pants_or_shorts'] if output and annotation['pants_or_shorts'] else annotation['pants_or_shorts']
211
+ else:
212
+ useful_keys = ['dress', 'top', 'bottom', 'hoodie_or_jacket']
213
+ for k in useful_keys:
214
+ output += ' + ' + annotation[k] if output and annotation.get(k) else annotation.get(k, '')
215
+ return output
216
+
217
+
218
+ def get_num_outfits(annotations, articles):
219
+ outfits = set()
220
+ for e in annotations:
221
+ if (sum([(e[key] == '' or e[key] in articles ) for key in article_keys]) == len(article_keys)
222
+ # and e['shirt'] and e['pants_or_shorts']
223
+ ):
224
+ str_form = annotation_to_key(e) # ignore +- hat, shoes otherwise use annotation_str
225
+ outfits.add(str_form)
226
+ return sorted(list(outfits))
227
+
228
+ def get_outfit_urls(annotations, outfits):
229
+ outfit_urls = defaultdict(list)
230
+
231
+ for e in annotations:
232
+ str_form = annotation_to_key(e)
233
+ if e.get('url') and str_form in outfits:
234
+ outfit_urls[str_form] += [ e['url'] ]
235
+ return dict(outfit_urls)
236
+
237
+ def cover_style(annotations, capacity=10, clothes_owned=[]):
238
+ clothes = list(cover_style_ip(annotations, capacity=capacity, clothes_owned=clothes_owned))
239
+ reachable_outfits = get_num_outfits(annotations, set(clothes+clothes_owned) )
240
+
241
+ if len(clothes_owned) == 0: # return basis outfits from scratch
242
+ return clothes, get_outfit_urls(annotations, reachable_outfits)
243
+
244
+ elif capacity == 0: # return reach of current clothes
245
+ return clothes, get_outfit_urls(annotations, reachable_outfits)
246
+
247
+ else: # capacity > 0 and len(clothes_owned) > 0, show new clothes and outfits
248
+ new_clothes = [ c for c in clothes if c not in clothes_owned ]
249
+ reachable_outfits_base = get_num_outfits(annotations, clothes_owned)
250
+ new_outfits = [ o for o in reachable_outfits if o not in reachable_outfits_base ]
251
+ return new_clothes, get_outfit_urls(annotations, new_outfits)
252
+
253
+
254
+ def str_to_list(input):
255
+ tokens = [ token.strip() for token in input.split(',') ]
256
+ return [ t for t in tokens if t != '' ]
257
+
258
+ CLOTHES_HEADER = f'## Most Effective Clothes'
259
+ OUTFITS_HEADER = f'## Possible πŸ‘•πŸ˜Ž Outfits'
260
+
261
+ def cover_style_helper(styles, capacity=10, clothes_owned=''):
262
+
263
+ if len(styles) == 0:
264
+ return f'{CLOTHES_HEADER}\nPlease pick at least one style from the left.' , OUTFITS_HEADER
265
+
266
+ clothes_owned = str_to_list(clothes_owned)
267
+ annotations = []
268
+ for style in styles:
269
+ annotations += style_annotations_dict[style][:500]
270
+
271
+ if len(styles) == 1: # hack for h&m having wayyyyy more examples than other brands
272
+ annotations = style_annotations_dict[style]
273
+
274
+ clothes, outfit_urls = cover_style(annotations, capacity, clothes_owned)
275
+ clothes_str = f'{CLOTHES_HEADER}\n'
276
+ for c in clothes:
277
+ clothes_str += f'* {c}\n'
278
+
279
+ outfits_str = f'{OUTFITS_HEADER} ({len(outfit_urls)})\n'
280
+
281
+ for outfit, urls in outfit_urls.items():
282
+ outfits_str += f'1. {str(outfit)}: '
283
+ for i, u in enumerate(urls):
284
+ outfits_str += f'<a href="{u}" target="_blank">[{i+1}]</a>' # f'[\[{i}\]]({u})'
285
+ outfits_str += '\n'
286
+ return clothes_str, outfits_str
287
+
288
+ article_keys = gender_article_keys['male']
289
+ print(cover_style_helper( ['banana republic'] ))
290
+
291
+ def cover_style_helper_wrapper(markdown, styles, capacity=10, clothes_owned=''):
292
+ if len(styles) > 0:
293
+ global article_keys
294
+ gender = style_gender[styles[0]]
295
+ article_keys = gender_article_keys[gender]
296
+ return cover_style_helper(styles, capacity, clothes_owned)
297
+
298
+ def filter_test(annotation, filter_set):
299
+ for f in filter_set:
300
+ num_occur = sum([1 for key in article_keys+['caption'] if f in annotation[key]])
301
+ if num_occur == 0:
302
+ return False
303
+ return True
304
+
305
+ import gradio as gr
306
+
307
+ def change_gallery(style_choice, start_from=0, text_filter=''):
308
+ global article_keys
309
+ gender = style_gender[style_choice]
310
+ article_keys = gender_article_keys[gender]
311
+
312
+ chosen_annotations = style_annotations_dict[style_choice]
313
+ if text_filter:
314
+ text_filter = set([t.strip() for t in text_filter.split(',')])
315
+ chosen_annotations = [ a for a in chosen_annotations if filter_test(a, text_filter) ]
316
+ start_from = int(start_from/100*len(chosen_annotations))
317
+ # print(len(chosen_annotations), [a['url'] for a in chosen_annotations[start_from:start_from+20]])
318
+ return [a['url'] for a in chosen_annotations[start_from:start_from+20]]
319
+
320
+ def update_styles(gender):
321
+ global article_keys
322
+ article_keys = gender_article_keys[gender]
323
+
324
+ default_values = ["banana republic"] if gender == "male" else ["aritzia"]
325
+ return gr.CheckboxGroup.update(choices=[k for k in style_annotations_dict.keys() if style_gender[k] == gender], value=default_values, label='Styles')
326
+
327
+
328
+
329
+
330
+
331
+ article_keys = gender_article_keys['male']
332
+ INTRO_MARKDOWN = """**Good clothes are the ones that you can use to make many outfits.**
333
+ Finding stuff that works with your style and wardrobe is hard.
334
+ This program helps you find the clothes that make as many fashionable outfits as possible,
335
+ given your current wardrobe, style, and budget.
336
+
337
+ """
338
+
339
+ with gr.Blocks() as demo:
340
+ with gr.Tabs(selected=0):
341
+ with gr.TabItem('Find the Literal Optimal Clothes', id=0):
342
+ with gr.Box():
343
+ default_clothes = """black dress pants,
344
+ blue dress shirt,
345
+ blue jacket,
346
+ blue jeans,
347
+ blue shirt,
348
+ brown boots,
349
+ gray sweater,
350
+ white dress shirt,
351
+ white sneakers"""
352
+
353
+ with gr.Row():
354
+ with gr.Column():
355
+ gr.Markdown(INTRO_MARKDOWN)
356
+ with gr.Row():
357
+ with gr.Group():
358
+ gender = gr.Radio(["male", "female"], value="male", label='gender')
359
+ styles = gr.CheckboxGroup([k for k in style_annotations_dict.keys() if style_gender[k] == gender.value], value=["banana republic"], label='Styles')
360
+ gender.change(update_styles, inputs=[gender], outputs=[styles])
361
+ with gr.Group():
362
+ capacity = gr.Number(5, precision=0, label='Number of clothes to recommend')
363
+ clothes_owned = gr.Textbox(
364
+ label="Clothes Owned",
365
+ lines=3,
366
+ value=default_clothes, #json.dumps(default_clothes),
367
+ )
368
+ with gr.Row():
369
+ btn = gr.Button("Recommend Clothes")
370
+
371
+ with gr.Row():
372
+ clothes_markdown, outfits_markdown = cover_style_helper(styles.value, capacity.value, clothes_owned.value)
373
+ clothes_recommended = gr.Markdown(clothes_markdown)
374
+ possible_outfits = gr.Markdown(outfits_markdown)
375
+
376
+ btn.click(cover_style_helper, inputs=[styles, capacity, clothes_owned], outputs=[clothes_recommended, possible_outfits])
377
+
378
+ gr.Markdown("## 3 Different Example Uses")
379
+ example_label = gr.Textbox('', label='Explanation', visible=False)
380
+ gr.Examples(examples=[ ['Find the central clothes that make a style.', ['street'], 10, ''],
381
+ ['Find new outfits hidden in your wardrobe.', ['h&m'], 0, 'white t shirt, grey t shirt, black pants, black shorts, black t shirt, blue jeans'],
382
+ ['Find the clothes that best fit your existing wardrobe.', ['banana republic', 'street'], 5, 'white t shirt, grey t shirt, black pants, black shorts, black t shirt, blue jeans']],
383
+ inputs=[example_label, styles, capacity, clothes_owned],
384
+ outputs=[clothes_recommended, possible_outfits],
385
+ fn=cover_style_helper_wrapper,
386
+ cache_examples=False)
387
+
388
+ EXPLORE_MARKDOWN = """# Explore Styles
389
+ Hint: Click an image to enable arrow key browsing.
390
+ You put in clothes you own to find outfit ideas!
391
+ """
392
+ with gr.TabItem('Explore Styles', id=1):
393
+ with gr.Box():
394
+ gr.Markdown(EXPLORE_MARKDOWN)
395
+ with gr.Row():
396
+ with gr.Column():
397
+ bad_urls = ['dynamite', 'abercrombie women']
398
+ styles = gr.Radio(list(style_annotations_dict.keys()), value="uniqlo", label='Styles')
399
+ start_from = gr.Slider(0, 100, label='Start from', value=0, step=10)
400
+ with gr.Group():
401
+ text_filter = gr.Textbox(value='white t shirt, jeans', label='Outfit includes')
402
+ filter_button = gr.Button('Apply Text Filter')
403
+ with gr.Column():
404
+ outfits_gallery = gr.Gallery(label='Outfit Examples', value=change_gallery(styles.value, start_from.value, text_filter.value))
405
+
406
+ styles.change(fn=change_gallery, inputs=[styles, start_from, text_filter], outputs=outfits_gallery)
407
+ start_from.change(fn=change_gallery, inputs=[styles, start_from, text_filter], outputs=outfits_gallery)
408
+ filter_button.click(fn=change_gallery, inputs=[styles, start_from, text_filter], outputs=outfits_gallery)
409
+
410
+ with gr.TabItem('How it works', id=2):
411
+ ABOUT_MARKDOWN = """
412
+ # Why
413
+ I don't really enjoy shopping, it takes a while a look at all the clothes,
414
+ and I'm not sure if I should buy what I picked out.
415
+ This program can tell me which clothes are the best for my wardrobe and style,
416
+ so I don't have to worry about buying the wrong stuff.
417
+
418
+ # How
419
+ This program poses the problem as a nonlinear integer programming problem.
420
+ The problem is to maximize the number of outfits, while contraining the number of clothes to be <= "this max you want to buy".
421
+ Let a, b, c etc be binary literals that represent the use of clothing type a, b, c.
422
+ These are clothing types like white dress shirt, or black hat.
423
+ We maximize the nonlinear expression abc + ade + bec + ... similar to a disjuctive normal form,
424
+ where each clause represents an outfit and is the product of clothing literals.
425
+ Each outfit clause has value 1 iff every article of clothing is used in the solution.
426
+ Pyomo is used to solve this optimization problem.
427
+
428
+ This objective takes the form of something like,
429
+ <iframe src="https://drive.google.com/file/d/1j0bBRZkdc7Ce5KWawlwrV36lI0MsMLRf/preview" width="640" height="480" allow="autoplay"></iframe>
430
+
431
+ Hart, William E., Carl Laird, Jean-Paul Watson, David L. Woodruff, Gabriel A. Hackebeil, Bethany L. Nicholson, and John D. Siirola. Pyomo – Optimization Modeling in Python. Springer, 2017.
432
+
433
+
434
+ # Caveats
435
+ This model understands fashion from a macro level.
436
+ It understands what a white shirt is, but is entirely blind to micro features like fit, brand, or shape.
437
+ It also bins different kinds of clothing and colors together.
438
+ So the model could mix up light grey sweatpants and dark grey chinos because they're both grey pants.
439
+
440
+ There is also error introduced from bad image annotations, approximate solvers, and style dataset size.
441
+
442
+ Hence it's important to look at the outfits images under possible outfits for a human touch!
443
+ """
444
+ with gr.Box():
445
+ gr.Markdown(ABOUT_MARKDOWN)
446
+
447
+
448
+
449
+ demo.launch(debug=True)
450
+
451
+
452
+
objective.PNG ADDED
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio
2
+ pyomo