Dress a manequin with a set of predefined rules and an input of clothes using a SAT solver.
In this section both installation and running is described.
Only one prerequisite is required to run the project:
- Python 3.6, or higher
Please follow the instructions in the Python official downloads page.
-
Clone (or download) the repository.
-
(Optional) Create a virtual environment, and activate it.
This is highly recommended, so all dependencies are isolated from the rest of the Python installation.
Help Docs: Creating virtual environments -
Install the needed requirements from the
requirements.txt
file:
pip install requirements.txt
Now the project is ready to run. Two options are available: running it from a script, or from the web interface.
To launch the web interface, just run the following command:
flask run
Open a new browser and go to localhost:5000. You can directly use that interface to visualize the problem.
You may want to take a look at the **example script available at main.py**
.
- Import
from store import FashionStore
. - Create a new
store = FashionStore()
object (no params). It will automatically parse thedata/wardrobe.json
file with all the available garments, colors and constraints. - Add the clothes you want to combine in the manequin:
- Use the
store.parse_clothes(text)
method, wheretext
is a string with lines with thegarment,color
format. - Use the
store.add_cloth(garment, color)
method, wheregarment
is aGarment
object andcolor
is aColor
object.
- Use the
- Invoke the
dresses = store.dress()
method.
Now the dresses
object will contain a list with all the available dresses. A dress is identified with a list of
Cloth
objects, which contain both a Garment
and a Color
.
The basic idea of this problem is that we have a fashion store, and we need to dress our mannequin. To do that we
have a set of colored garments (a "cloth"), and we need to find a combination.
Since this problem is very loosely defined, we need to come up with constraints and with an appropriate problem size.
First we have to define the two set of colors (
$C = \{ \textrm{Red}, \textrm{Blue}, \textrm{Cyan}, \textrm{Green}, \textrm{Yellow}, \textrm{Orange}, \textrm{Purple}, \textrm{White}, \textrm{Grey}, \textrm{Black} \}$ $G = \{ \textrm{Hat}, \textrm{Cap}, \textrm{Umbrella}, \textrm{T-Shirt}, \textrm{Shirt}, \textrm{Top}, \textrm{Jacket}, \textrm{Tie}, \textrm{Gloves}, \textrm{Shorts}, \textrm{Jeans}, \textrm{Pants}, \textrm{Skirt}, \textrm{Tennis}, \textrm{Moccasin}, \textrm{Ice-Skates} \}$
Given this two sets we needed to create some constraints over them that will always be added. They are hardcoded as they specify, for instance, which garments (or colors) should or should not go together, and we have worked out these constraints with the following boolean expression:
Boolean expression | Constrain |
---|---|
You can't wear an Hat and a Cap at the same time | |
You can't wear a T-Shirt and a Shirt at the same time | |
You can't wear a T-Shirt and a Top at the same time | |
You can't wear Gloves and Gauntlet at the same time | |
You can't wear Shorts and Jeans at the same time | |
You can't wear Shorts and Pants at the same time | |
You can't wear Shorts and a Skirt at the same time | |
You can't wear Jeans and Pants at the same time | |
You can't wear Jeans and a Skirt at the same time | |
You can't wear Pant and a Skirt at the same time | |
You can't wear Tennis shoes and Mocassin at the same time | |
You can't wear Tennis shoes and Ice-Skates at the same time | |
You can't wear Moccasin and Ice-Skates at the same time | |
You can't wear one Red and one Blue garment at the same time | |
You can't wear one Red and one Blue garment at the same time | |
You can't wear one Red and one Blue garment at the same time | |
You must wear a Shirt under a Tie | |
You must wear a Jacket if you are wearing Gloves | |
You must wear a Jacket if you are wearing Gaunlet | |
You must wear Jeans if you are wearing a Jacket |
After that we need to go over the input, made of pairs of a garment and a color, and create the final constrains as explained in the Store section of the Backend Implementation.
The backend of the project can be understood as the implementation of the logical project. This has been done in
Python, aided by the z3-solver
library.
Object-oriented programming has been used to ease access to data in the entire project.
The models.py
file has all the needed classes.
There is an abstract BaseModel
class that is later inherited by both Color
and Garment
.
Both Color
and Garment
classes are quite similar. They both have an ID and a fancy name, but Garment
has
an aditional z-index
field (used later by the frontend). They also both generate an internal Bool
object that is
used by z3
(it has the type_X
format, where type
is either color
or garment
, and X
is the ID).
And the last class, Cloth
, has both a Garment
and a Color
(so it can be seen as a tuple of these). But it also
generates "two" Bool
objects: one that is the raw Bool
(with format cloth_C_G
, where G
is the color name
and g
is the garment name), and another one as a "rule" which is the implication of this Bool
with the
conjunction of both garment and color (Implies(cloth_C_G, And(garment.to_bool(), color.to_bool()))
).
Finally, after defining the models, only the fashion store is missing. For such purpuse, the store.py
contains a
FashionStore
class.
When creating a new instance of this class, it will automatically parse the data/wardrobe.json
file. This file
shall contain a list of supported garments, a list of supported colors, and all the constraints.
Regarding the constraints, there can be multiple types of constraints, but they must define which object they target
(garments or colors), the type of rules (negation or implication), and a list of target values.
Once a new object has been created, two methods can be called to add available clothes: either add_cloth
or
parse_clothes
. Internally, clothes are stored in a dictionary, where the key is a Garment
object and the
value is a list of Color
(so it is possible to support multiple colors for a given garment).
add_cloth
will receive a Cloth
object, and will decompose it into the garment and color. They will be appended to
the dictionary once confirmed that they are both supported.
parse_clothes
may be used after reading a text file with the format garment,color
. It will go through all lines
and, if it matches the format and both garment and color are supported, will append the respective cloth to the
dictionary.
Finally, the dress
method will generate all possible permutations of garments with the list of colors. It will create
a new Solver
object for each possible permutation. This Solver
object is populated as follows:
- All the constraint rules as defined in the
data/wardrobe.json
file. - A bidirectional implication to target cloth with garment and color. As this is not natively supported by
Z3
, it was implemented with three different subrules:- For each cloth that is "available" in the permutation, the following rule:
Implies(cloth, And(cloth.garment, cloth.color))
- "Picking this cloth implies picking both its garment and color"
- For each garment used in any cloth, the following rule:
Implies(garment, Or(cloth1_with_garment, cloth2_with_garment, ...))
- "Picking this garment implies picking any of the available clothes that are using it"
- For each color used in any cloth, the following rule:
Implies(color, Or(cloth1_with_color, cloth2_with_color, ...))
- "Picking this color implies picking any of the available clothes that are using it"
- For each cloth that is "available" in the permutation, the following rule:
- All available clothes wrapped in an
Or
:Or(cloth1, cloth2, cloth3, cloth4, ...)
- "Any of these clothes are available to be picked"
It will return a list of available dresses. This list will have a minimum size of 0 (if it cannot be solved), up to
the number of permutations (if all permutations can be solved).
Thus, each dress will contain a list of Cloth
object ("non-conflicting clothes").
In order to make the frontend, first we started by writing HTML/CSS templates. Then, we connected the frontend to the backend using Flask.
For the template, we chose a very simplistic design, with vibrant yet high contrasting colors to make the user
experience more enjoyable.
The background was done using a css 3d zig-zag pattern found which can be found
here.
As mentioned before, we are running flask to connect back end and the front end. The HTML form on the front end POSTs
the form information including the uploaded file to the python script.
After the execution of the script, the outfits are returned to the frontend through a JSON object and are displayed to
the user.
We are using over 160 image models to display every garment of every color and the recommended outfit to the user.
In order to visualize the outfits even better, we hired the most handsome model of all time, Diego, to pose with each
and every outfit.
This was by far the most expensive part of the project.