import requests import os import faiss import numpy as np import replicate from dotenv import load_dotenv from geopy.geocoders import Nominatim from werkzeug.utils import secure_filename CLOTHES_TYPES = ["tops", "bottoms", "shoes", "outerwear"] load_dotenv() WEATHER_API_KEY = os.getenv("WEATHER_API_KEY") def allowed_file(filename): """Checks if uploaded file is allowed""" ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS def save_file(file, app): """Makes filename unique and saves it""" filename = secure_filename(file.filename) name, extension = os.path.splitext(filename) counter = 1 while os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'], filename)): filename = f"{name}_{counter}{extension}" counter += 1 file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(file_path) return file_path def get_image_embeddings(image): output = replicate.run( "daanelson/imagebind:0383f62e173dc821ec52663ed22a076d9c970549c209666ac3db181618b7a304", input={ "input": image, "modality": "vision" } ) return output def get_text_embeddings(text): output = replicate.run( "daanelson/imagebind:0383f62e173dc821ec52663ed22a076d9c970549c209666ac3db181618b7a304", input={ "text_input": text, "modality": "text" } ) return output def get_clothing_type(image): output = replicate.run( "yorickvp/llava-v1.6-34b:41ecfbfb261e6c1adf3ad896c9066ca98346996d7c4045c5bc944a79d430f174", input={ "image": image, "prompt": f"What is this piece of clothing? Please select ONLY ONE CHOICE: {CLOTHES_TYPES}. \ If you are in doubt, just pick ONE OF THEM.\ \nIf you think it's outerwear but it doesn't have a zipper or buttons, it should \ be considered as: 'tops'. Keep in mind that you shouldn't write anything except one \ of the 4 choices, and no other text." } ) return "".join(output).lower() def get_user_closet_length(query, collection): user_doc = collection.find_one(query) if user_doc: closet = user_doc.get('closet', {}) return len(closet) raise ValueError("User not found") def save_data_to_db(data:dict, db): collection = db.users query = {'_id': data["username"]} new_item = { 'path': data["image_path"], 'type': data["type"], 'embedding': data["image_embeddings"] } closet_length = get_user_closet_length(query, collection) new_item_key = f"item{closet_length + 1}" collection.update_one( {"_id": data["username"]}, {"$set": {f"closet.{new_item_key}": new_item}} ) def get_city_from_coord(latitude, longitude): geolocator = Nominatim(user_agent="city_name_app") location = geolocator.reverse((latitude, longitude), exactly_one=True) address = location.address if location else None if address: city = address.split(",")[-3] return city.strip() return None def fetch_weather(latitude, longitude): city_name = get_city_from_coord(latitude, longitude) if not city_name: return None url = f"http://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={WEATHER_API_KEY}&units=metric" response = requests.get(url) data = response.json() return data["main"]["feels_like"] def get_gender_by_username(username:str, db): collection = db.users document = collection.find_one({"_id": username}) if not document: return None return document.get("gender") def prompt_gpt(client, gender, context, temperature): """given some context, returns a dictionary describing what you should wear in your outfit.""" outfit = {} history = [{"role": "system", "content": "You are a fashion expert who is dedicated to picking outfits for a user. \ You will receive context from the user that will help you choose an outfit. \ An outfit contains four items: top, bottom, shoes, outwear. \ The outwear is optional and depends on the weather, if it is not needed, \ only write `none` and nothing else. You will need to provide a description for each item one by one.\ I want the description to include: color, style, fit, and material. Make sure the different items \ go well toghether in terms of style, color and other factors. the answer should be in this format: \ 'description': "}] prompt = {"role": "user", "content": f"-Context: `{context}`,\n-Gender: `{gender}`\ \n-Temperature: `{temperature}` degrees celsius.\ \nGiven this context, generate a description for what I should wear as a top:"} history.append(prompt) response = client.chat.completions.create( model="gpt-3.5-turbo", messages= history ) content = response.choices[0].message.content history.append({"role": "assistant", "content": content}) outfit["tops"] = content[15:] prompt = {"role": "user", "content": f"Given the previous answers, generate a description for what \ I should wear as a bottom:"} history.append(prompt) response = client.chat.completions.create( model="gpt-3.5-turbo", messages= history ) content = response.choices[0].message.content history.append({"role": "assistant", "content": content}) outfit["bottoms"] = content[15:] prompt = {"role": "user", "content": f"Given the previous answers, generate a description for what \ I should wear as shoes:"} history.append(prompt) response = client.chat.completions.create( model="gpt-3.5-turbo", messages= history ) content = response.choices[0].message.content history.append({"role": "assistant", "content": content}) outfit["shoes"] = content[15:] prompt = {"role": "user", "content": f"Given the previous answers, and given the temperature, \ generate a description for what I should wear as outwear. If the temperature I gave you \ ({temperature}) is above 25, write none."} history.append(prompt) response = client.chat.completions.create( model="gpt-3.5-turbo", messages= history ) content = response.choices[0].message.content history.append({"role": "assistant", "content": content}) if not "none" in content.lower(): outfit["outerwear"] = content[15:] return outfit def get_items_by_type(db, username, item_type:str): collection = db.users document = collection.find_one({"_id": username}) if not document: return None closet = document.get("closet", {}) items = [closet[key] for key in closet if closet[key].get("type") == item_type] return items def get_items_embeddings(items): embeddings = [] for item in items: embedding = item.get("embedding") embeddings.append(embedding) return embeddings def get_most_similar_embedding(embedding, embeddings_list): embedding = np.asarray(embedding, dtype=np.float32) embeddings_list = np.asarray(embeddings_list, dtype=np.float32) d = embedding.shape[0] index = faiss.IndexFlatL2(d) index.add(embeddings_list) _, most_similar_index = index.search(np.expand_dims(embedding, axis=0), 1) return most_similar_index[0][0] def get_outfit(outfit_description, username, db): outfit = [] for item_type, description in outfit_description.items(): description_embedding = get_text_embeddings(description) items = get_items_by_type(db, username, item_type) items_embeddings= get_items_embeddings(items) outfit_item_index = get_most_similar_embedding(description_embedding, items_embeddings) outfit_item_path = items[outfit_item_index]["path"] if item_type == "outerwear": outfit.insert(0, outfit_item_path) else: outfit.append(outfit_item_path) return outfit