Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
pharmapsychotic | 122c041aad | 2 years ago |
20 changed files with 1112 additions and 102481 deletions
@ -1,39 +0,0 @@ |
|||||||
# This workflow will upload a Python Package using Twine when a release is created |
|
||||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries |
|
||||||
|
|
||||||
# This workflow uses actions that are not certified by GitHub. |
|
||||||
# They are provided by a third-party and are governed by |
|
||||||
# separate terms of service, privacy policy, and support |
|
||||||
# documentation. |
|
||||||
|
|
||||||
name: Upload Python Package |
|
||||||
|
|
||||||
on: |
|
||||||
release: |
|
||||||
types: [published] |
|
||||||
|
|
||||||
permissions: |
|
||||||
contents: read |
|
||||||
|
|
||||||
jobs: |
|
||||||
deploy: |
|
||||||
|
|
||||||
runs-on: ubuntu-latest |
|
||||||
|
|
||||||
steps: |
|
||||||
- uses: actions/checkout@v3 |
|
||||||
- name: Set up Python |
|
||||||
uses: actions/setup-python@v3 |
|
||||||
with: |
|
||||||
python-version: '3.x' |
|
||||||
- name: Install dependencies |
|
||||||
run: | |
|
||||||
python -m pip install --upgrade pip |
|
||||||
pip install build |
|
||||||
- name: Build package |
|
||||||
run: python -m build |
|
||||||
- name: Publish package |
|
||||||
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 |
|
||||||
with: |
|
||||||
user: pharmapsychotic |
|
||||||
password: ${{ secrets.PYPI_API_TOKEN }} |
|
@ -1,10 +0,0 @@ |
|||||||
*.pyc |
|
||||||
.cog/ |
|
||||||
.vscode/ |
|
||||||
bench/ |
|
||||||
cache/ |
|
||||||
ci_env/ |
|
||||||
clip-interrogator/ |
|
||||||
clip_interrogator.egg-info/ |
|
||||||
dist/ |
|
||||||
venv/ |
|
@ -1,6 +0,0 @@ |
|||||||
include clip_interrogator/data/artists.txt |
|
||||||
include clip_interrogator/data/flavors.txt |
|
||||||
include clip_interrogator/data/mediums.txt |
|
||||||
include clip_interrogator/data/movements.txt |
|
||||||
include clip_interrogator/data/negative.txt |
|
||||||
include requirements.txt |
|
File diff suppressed because one or more lines are too long
@ -1,4 +0,0 @@ |
|||||||
from .clip_interrogator import Config, Interrogator, LabelTable, list_caption_models, list_clip_models, load_list |
|
||||||
|
|
||||||
__version__ = '0.6.0' |
|
||||||
__author__ = 'pharmapsychotic' |
|
@ -1,450 +0,0 @@ |
|||||||
import hashlib |
|
||||||
import math |
|
||||||
import numpy as np |
|
||||||
import open_clip |
|
||||||
import os |
|
||||||
import requests |
|
||||||
import time |
|
||||||
import torch |
|
||||||
|
|
||||||
from dataclasses import dataclass |
|
||||||
from PIL import Image |
|
||||||
from transformers import AutoProcessor, AutoModelForCausalLM, BlipForConditionalGeneration, Blip2ForConditionalGeneration |
|
||||||
from tqdm import tqdm |
|
||||||
from typing import List, Optional |
|
||||||
|
|
||||||
from safetensors.numpy import load_file, save_file |
|
||||||
|
|
||||||
CAPTION_MODELS = { |
|
||||||
'blip-base': 'Salesforce/blip-image-captioning-base', # 990MB |
|
||||||
'blip-large': 'Salesforce/blip-image-captioning-large', # 1.9GB |
|
||||||
'blip2-2.7b': 'Salesforce/blip2-opt-2.7b', # 15.5GB |
|
||||||
'blip2-flan-t5-xl': 'Salesforce/blip2-flan-t5-xl', # 15.77GB |
|
||||||
'git-large-coco': 'microsoft/git-large-coco', # 1.58GB |
|
||||||
} |
|
||||||
|
|
||||||
CACHE_URL_BASE = 'https://huggingface.co/pharmapsychotic/ci-preprocess/resolve/main/' |
|
||||||
|
|
||||||
|
|
||||||
@dataclass |
|
||||||
class Config: |
|
||||||
# models can optionally be passed in directly |
|
||||||
caption_model = None |
|
||||||
caption_processor = None |
|
||||||
clip_model = None |
|
||||||
clip_preprocess = None |
|
||||||
|
|
||||||
# blip settings |
|
||||||
caption_max_length: int = 32 |
|
||||||
caption_model_name: Optional[str] = 'blip-large' # use a key from CAPTION_MODELS or None |
|
||||||
caption_offload: bool = False |
|
||||||
|
|
||||||
# clip settings |
|
||||||
clip_model_name: str = 'ViT-L-14/openai' |
|
||||||
clip_model_path: Optional[str] = None |
|
||||||
clip_offload: bool = False |
|
||||||
|
|
||||||
# interrogator settings |
|
||||||
cache_path: str = 'cache' # path to store cached text embeddings |
|
||||||
download_cache: bool = True # when true, cached embeds are downloaded from huggingface |
|
||||||
chunk_size: int = 2048 # batch size for CLIP, use smaller for lower VRAM |
|
||||||
data_path: str = os.path.join(os.path.dirname(__file__), 'data') |
|
||||||
device: str = ("mps" if torch.backends.mps.is_available() else "cuda" if torch.cuda.is_available() else "cpu") |
|
||||||
flavor_intermediate_count: int = 2048 |
|
||||||
quiet: bool = False # when quiet progress bars are not shown |
|
||||||
|
|
||||||
def apply_low_vram_defaults(self): |
|
||||||
self.caption_model_name = 'blip-base' |
|
||||||
self.caption_offload = True |
|
||||||
self.clip_offload = True |
|
||||||
self.chunk_size = 1024 |
|
||||||
self.flavor_intermediate_count = 1024 |
|
||||||
|
|
||||||
class Interrogator(): |
|
||||||
def __init__(self, config: Config): |
|
||||||
self.config = config |
|
||||||
self.device = config.device |
|
||||||
self.dtype = torch.float16 if self.device == 'cuda' else torch.float32 |
|
||||||
self.caption_offloaded = True |
|
||||||
self.clip_offloaded = True |
|
||||||
self.load_caption_model() |
|
||||||
self.load_clip_model() |
|
||||||
|
|
||||||
def load_caption_model(self): |
|
||||||
if self.config.caption_model is None and self.config.caption_model_name: |
|
||||||
if not self.config.quiet: |
|
||||||
print(f"Loading caption model {self.config.caption_model_name}...") |
|
||||||
|
|
||||||
model_path = CAPTION_MODELS[self.config.caption_model_name] |
|
||||||
if self.config.caption_model_name.startswith('git-'): |
|
||||||
caption_model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch.float32) |
|
||||||
elif self.config.caption_model_name.startswith('blip2-'): |
|
||||||
caption_model = Blip2ForConditionalGeneration.from_pretrained(model_path, torch_dtype=self.dtype) |
|
||||||
else: |
|
||||||
caption_model = BlipForConditionalGeneration.from_pretrained(model_path, torch_dtype=self.dtype) |
|
||||||
self.caption_processor = AutoProcessor.from_pretrained(model_path) |
|
||||||
|
|
||||||
caption_model.eval() |
|
||||||
if not self.config.caption_offload: |
|
||||||
caption_model = caption_model.to(self.config.device) |
|
||||||
self.caption_model = caption_model |
|
||||||
else: |
|
||||||
self.caption_model = self.config.caption_model |
|
||||||
self.caption_processor = self.config.caption_processor |
|
||||||
|
|
||||||
def load_clip_model(self): |
|
||||||
start_time = time.time() |
|
||||||
config = self.config |
|
||||||
|
|
||||||
clip_model_name, clip_model_pretrained_name = config.clip_model_name.split('/', 2) |
|
||||||
|
|
||||||
if config.clip_model is None: |
|
||||||
if not config.quiet: |
|
||||||
print(f"Loading CLIP model {config.clip_model_name}...") |
|
||||||
|
|
||||||
self.clip_model, _, self.clip_preprocess = open_clip.create_model_and_transforms( |
|
||||||
clip_model_name, |
|
||||||
pretrained=clip_model_pretrained_name, |
|
||||||
precision='fp16' if config.device == 'cuda' else 'fp32', |
|
||||||
device=config.device, |
|
||||||
jit=False, |
|
||||||
cache_dir=config.clip_model_path |
|
||||||
) |
|
||||||
self.clip_model.eval() |
|
||||||
else: |
|
||||||
self.clip_model = config.clip_model |
|
||||||
self.clip_preprocess = config.clip_preprocess |
|
||||||
self.tokenize = open_clip.get_tokenizer(clip_model_name) |
|
||||||
|
|
||||||
sites = ['Artstation', 'behance', 'cg society', 'cgsociety', 'deviantart', 'dribbble', |
|
||||||
'flickr', 'instagram', 'pexels', 'pinterest', 'pixabay', 'pixiv', 'polycount', |
|
||||||
'reddit', 'shutterstock', 'tumblr', 'unsplash', 'zbrush central'] |
|
||||||
trending_list = [site for site in sites] |
|
||||||
trending_list.extend(["trending on "+site for site in sites]) |
|
||||||
trending_list.extend(["featured on "+site for site in sites]) |
|
||||||
trending_list.extend([site+" contest winner" for site in sites]) |
|
||||||
|
|
||||||
raw_artists = load_list(config.data_path, 'artists.txt') |
|
||||||
artists = [f"by {a}" for a in raw_artists] |
|
||||||
artists.extend([f"inspired by {a}" for a in raw_artists]) |
|
||||||
|
|
||||||
self._prepare_clip() |
|
||||||
self.artists = LabelTable(artists, "artists", self) |
|
||||||
self.flavors = LabelTable(load_list(config.data_path, 'flavors.txt'), "flavors", self) |
|
||||||
self.mediums = LabelTable(load_list(config.data_path, 'mediums.txt'), "mediums", self) |
|
||||||
self.movements = LabelTable(load_list(config.data_path, 'movements.txt'), "movements", self) |
|
||||||
self.trendings = LabelTable(trending_list, "trendings", self) |
|
||||||
self.negative = LabelTable(load_list(config.data_path, 'negative.txt'), "negative", self) |
|
||||||
|
|
||||||
end_time = time.time() |
|
||||||
if not config.quiet: |
|
||||||
print(f"Loaded CLIP model and data in {end_time-start_time:.2f} seconds.") |
|
||||||
|
|
||||||
def chain( |
|
||||||
self, |
|
||||||
image_features: torch.Tensor, |
|
||||||
phrases: List[str], |
|
||||||
best_prompt: str="", |
|
||||||
best_sim: float=0, |
|
||||||
min_count: int=8, |
|
||||||
max_count: int=32, |
|
||||||
desc="Chaining", |
|
||||||
reverse: bool=False |
|
||||||
) -> str: |
|
||||||
self._prepare_clip() |
|
||||||
|
|
||||||
phrases = set(phrases) |
|
||||||
if not best_prompt: |
|
||||||
best_prompt = self.rank_top(image_features, [f for f in phrases], reverse=reverse) |
|
||||||
best_sim = self.similarity(image_features, best_prompt) |
|
||||||
phrases.remove(best_prompt) |
|
||||||
curr_prompt, curr_sim = best_prompt, best_sim |
|
||||||
|
|
||||||
def check(addition: str, idx: int) -> bool: |
|
||||||
nonlocal best_prompt, best_sim, curr_prompt, curr_sim |
|
||||||
prompt = curr_prompt + ", " + addition |
|
||||||
sim = self.similarity(image_features, prompt) |
|
||||||
if reverse: |
|
||||||
sim = -sim |
|
||||||
|
|
||||||
if sim > best_sim: |
|
||||||
best_prompt, best_sim = prompt, sim |
|
||||||
if sim > curr_sim or idx < min_count: |
|
||||||
curr_prompt, curr_sim = prompt, sim |
|
||||||
return True |
|
||||||
return False |
|
||||||
|
|
||||||
for idx in tqdm(range(max_count), desc=desc, disable=self.config.quiet): |
|
||||||
best = self.rank_top(image_features, [f"{curr_prompt}, {f}" for f in phrases], reverse=reverse) |
|
||||||
flave = best[len(curr_prompt)+2:] |
|
||||||
if not check(flave, idx): |
|
||||||
break |
|
||||||
if _prompt_at_max_len(curr_prompt, self.tokenize): |
|
||||||
break |
|
||||||
phrases.remove(flave) |
|
||||||
|
|
||||||
return best_prompt |
|
||||||
|
|
||||||
def generate_caption(self, pil_image: Image) -> str: |
|
||||||
assert self.caption_model is not None, "No caption model loaded." |
|
||||||
self._prepare_caption() |
|
||||||
inputs = self.caption_processor(images=pil_image, return_tensors="pt").to(self.device) |
|
||||||
if not self.config.caption_model_name.startswith('git-'): |
|
||||||
inputs = inputs.to(self.dtype) |
|
||||||
tokens = self.caption_model.generate(**inputs, max_new_tokens=self.config.caption_max_length) |
|
||||||
return self.caption_processor.batch_decode(tokens, skip_special_tokens=True)[0].strip() |
|
||||||
|
|
||||||
def image_to_features(self, image: Image) -> torch.Tensor: |
|
||||||
self._prepare_clip() |
|
||||||
images = self.clip_preprocess(image).unsqueeze(0).to(self.device) |
|
||||||
with torch.no_grad(), torch.cuda.amp.autocast(): |
|
||||||
image_features = self.clip_model.encode_image(images) |
|
||||||
image_features /= image_features.norm(dim=-1, keepdim=True) |
|
||||||
return image_features |
|
||||||
|
|
||||||
def interrogate_classic(self, image: Image, max_flavors: int=3, caption: Optional[str]=None) -> str: |
|
||||||
"""Classic mode creates a prompt in a standard format first describing the image, |
|
||||||
then listing the artist, trending, movement, and flavor text modifiers.""" |
|
||||||
caption = caption or self.generate_caption(image) |
|
||||||
image_features = self.image_to_features(image) |
|
||||||
|
|
||||||
medium = self.mediums.rank(image_features, 1)[0] |
|
||||||
artist = self.artists.rank(image_features, 1)[0] |
|
||||||
trending = self.trendings.rank(image_features, 1)[0] |
|
||||||
movement = self.movements.rank(image_features, 1)[0] |
|
||||||
flaves = ", ".join(self.flavors.rank(image_features, max_flavors)) |
|
||||||
|
|
||||||
if caption.startswith(medium): |
|
||||||
prompt = f"{caption} {artist}, {trending}, {movement}, {flaves}" |
|
||||||
else: |
|
||||||
prompt = f"{caption}, {medium} {artist}, {trending}, {movement}, {flaves}" |
|
||||||
|
|
||||||
return _truncate_to_fit(prompt, self.tokenize) |
|
||||||
|
|
||||||
def interrogate_fast(self, image: Image, max_flavors: int=32, caption: Optional[str]=None) -> str: |
|
||||||
"""Fast mode simply adds the top ranked terms after a caption. It generally results in |
|
||||||
better similarity between generated prompt and image than classic mode, but the prompts |
|
||||||
are less readable.""" |
|
||||||
caption = caption or self.generate_caption(image) |
|
||||||
image_features = self.image_to_features(image) |
|
||||||
merged = _merge_tables([self.artists, self.flavors, self.mediums, self.movements, self.trendings], self) |
|
||||||
tops = merged.rank(image_features, max_flavors) |
|
||||||
return _truncate_to_fit(caption + ", " + ", ".join(tops), self.tokenize) |
|
||||||
|
|
||||||
def interrogate_negative(self, image: Image, max_flavors: int = 32) -> str: |
|
||||||
"""Negative mode chains together the most dissimilar terms to the image. It can be used |
|
||||||
to help build a negative prompt to pair with the regular positive prompt and often |
|
||||||
improve the results of generated images particularly with Stable Diffusion 2.""" |
|
||||||
image_features = self.image_to_features(image) |
|
||||||
flaves = self.flavors.rank(image_features, self.config.flavor_intermediate_count, reverse=True) |
|
||||||
flaves = flaves + self.negative.labels |
|
||||||
return self.chain(image_features, flaves, max_count=max_flavors, reverse=True, desc="Negative chain") |
|
||||||
|
|
||||||
def interrogate(self, image: Image, min_flavors: int=8, max_flavors: int=32, caption: Optional[str]=None) -> str: |
|
||||||
caption = caption or self.generate_caption(image) |
|
||||||
image_features = self.image_to_features(image) |
|
||||||
|
|
||||||
merged = _merge_tables([self.artists, self.flavors, self.mediums, self.movements, self.trendings], self) |
|
||||||
flaves = merged.rank(image_features, self.config.flavor_intermediate_count) |
|
||||||
best_prompt, best_sim = caption, self.similarity(image_features, caption) |
|
||||||
best_prompt = self.chain(image_features, flaves, best_prompt, best_sim, min_count=min_flavors, max_count=max_flavors, desc="Flavor chain") |
|
||||||
|
|
||||||
fast_prompt = self.interrogate_fast(image, max_flavors, caption=caption) |
|
||||||
classic_prompt = self.interrogate_classic(image, max_flavors, caption=caption) |
|
||||||
candidates = [caption, classic_prompt, fast_prompt, best_prompt] |
|
||||||
return candidates[np.argmax(self.similarities(image_features, candidates))] |
|
||||||
|
|
||||||
def rank_top(self, image_features: torch.Tensor, text_array: List[str], reverse: bool=False) -> str: |
|
||||||
self._prepare_clip() |
|
||||||
text_tokens = self.tokenize([text for text in text_array]).to(self.device) |
|
||||||
with torch.no_grad(), torch.cuda.amp.autocast(): |
|
||||||
text_features = self.clip_model.encode_text(text_tokens) |
|
||||||
text_features /= text_features.norm(dim=-1, keepdim=True) |
|
||||||
similarity = text_features @ image_features.T |
|
||||||
if reverse: |
|
||||||
similarity = -similarity |
|
||||||
return text_array[similarity.argmax().item()] |
|
||||||
|
|
||||||
def similarity(self, image_features: torch.Tensor, text: str) -> float: |
|
||||||
self._prepare_clip() |
|
||||||
text_tokens = self.tokenize([text]).to(self.device) |
|
||||||
with torch.no_grad(), torch.cuda.amp.autocast(): |
|
||||||
text_features = self.clip_model.encode_text(text_tokens) |
|
||||||
text_features /= text_features.norm(dim=-1, keepdim=True) |
|
||||||
similarity = text_features @ image_features.T |
|
||||||
return similarity[0][0].item() |
|
||||||
|
|
||||||
def similarities(self, image_features: torch.Tensor, text_array: List[str]) -> List[float]: |
|
||||||
self._prepare_clip() |
|
||||||
text_tokens = self.tokenize([text for text in text_array]).to(self.device) |
|
||||||
with torch.no_grad(), torch.cuda.amp.autocast(): |
|
||||||
text_features = self.clip_model.encode_text(text_tokens) |
|
||||||
text_features /= text_features.norm(dim=-1, keepdim=True) |
|
||||||
similarity = text_features @ image_features.T |
|
||||||
return similarity.T[0].tolist() |
|
||||||
|
|
||||||
def _prepare_caption(self): |
|
||||||
if self.config.clip_offload and not self.clip_offloaded: |
|
||||||
self.clip_model = self.clip_model.to('cpu') |
|
||||||
self.clip_offloaded = True |
|
||||||
if self.caption_offloaded: |
|
||||||
self.caption_model = self.caption_model.to(self.device) |
|
||||||
self.caption_offloaded = False |
|
||||||
|
|
||||||
def _prepare_clip(self): |
|
||||||
if self.config.caption_offload and not self.caption_offloaded: |
|
||||||
self.caption_model = self.caption_model.to('cpu') |
|
||||||
self.caption_offloaded = True |
|
||||||
if self.clip_offloaded: |
|
||||||
self.clip_model = self.clip_model.to(self.device) |
|
||||||
self.clip_offloaded = False |
|
||||||
|
|
||||||
|
|
||||||
class LabelTable(): |
|
||||||
def __init__(self, labels:List[str], desc:str, ci: Interrogator): |
|
||||||
clip_model, config = ci.clip_model, ci.config |
|
||||||
self.chunk_size = config.chunk_size |
|
||||||
self.config = config |
|
||||||
self.device = config.device |
|
||||||
self.embeds = [] |
|
||||||
self.labels = labels |
|
||||||
self.tokenize = ci.tokenize |
|
||||||
|
|
||||||
hash = hashlib.sha256(",".join(labels).encode()).hexdigest() |
|
||||||
sanitized_name = self.config.clip_model_name.replace('/', '_').replace('@', '_') |
|
||||||
self._load_cached(desc, hash, sanitized_name) |
|
||||||
|
|
||||||
if len(self.labels) != len(self.embeds): |
|
||||||
self.embeds = [] |
|
||||||
chunks = np.array_split(self.labels, max(1, len(self.labels)/config.chunk_size)) |
|
||||||
for chunk in tqdm(chunks, desc=f"Preprocessing {desc}" if desc else None, disable=self.config.quiet): |
|
||||||
text_tokens = self.tokenize(chunk).to(self.device) |
|
||||||
with torch.no_grad(), torch.cuda.amp.autocast(): |
|
||||||
text_features = clip_model.encode_text(text_tokens) |
|
||||||
text_features /= text_features.norm(dim=-1, keepdim=True) |
|
||||||
text_features = text_features.half().cpu().numpy() |
|
||||||
for i in range(text_features.shape[0]): |
|
||||||
self.embeds.append(text_features[i]) |
|
||||||
|
|
||||||
if desc and self.config.cache_path: |
|
||||||
os.makedirs(self.config.cache_path, exist_ok=True) |
|
||||||
cache_filepath = os.path.join(self.config.cache_path, f"{sanitized_name}_{desc}.safetensors") |
|
||||||
tensors = { |
|
||||||
"embeds": np.stack(self.embeds), |
|
||||||
"hash": np.array([ord(c) for c in hash], dtype=np.int8) |
|
||||||
} |
|
||||||
save_file(tensors, cache_filepath) |
|
||||||
|
|
||||||
if self.device == 'cpu' or self.device == torch.device('cpu'): |
|
||||||
self.embeds = [e.astype(np.float32) for e in self.embeds] |
|
||||||
|
|
||||||
def _load_cached(self, desc:str, hash:str, sanitized_name:str) -> bool: |
|
||||||
if self.config.cache_path is None or desc is None: |
|
||||||
return False |
|
||||||
|
|
||||||
cached_safetensors = os.path.join(self.config.cache_path, f"{sanitized_name}_{desc}.safetensors") |
|
||||||
|
|
||||||
if self.config.download_cache and not os.path.exists(cached_safetensors): |
|
||||||
download_url = CACHE_URL_BASE + f"{sanitized_name}_{desc}.safetensors" |
|
||||||
try: |
|
||||||
os.makedirs(self.config.cache_path, exist_ok=True) |
|
||||||
_download_file(download_url, cached_safetensors, quiet=self.config.quiet) |
|
||||||
except Exception as e: |
|
||||||
print(f"Failed to download {download_url}") |
|
||||||
print(e) |
|
||||||
return False |
|
||||||
|
|
||||||
if os.path.exists(cached_safetensors): |
|
||||||
try: |
|
||||||
tensors = load_file(cached_safetensors) |
|
||||||
except Exception as e: |
|
||||||
print(f"Failed to load {cached_safetensors}") |
|
||||||
print(e) |
|
||||||
return False |
|
||||||
if 'hash' in tensors and 'embeds' in tensors: |
|
||||||
if np.array_equal(tensors['hash'], np.array([ord(c) for c in hash], dtype=np.int8)): |
|
||||||
self.embeds = tensors['embeds'] |
|
||||||
if len(self.embeds.shape) == 2: |
|
||||||
self.embeds = [self.embeds[i] for i in range(self.embeds.shape[0])] |
|
||||||
return True |
|
||||||
|
|
||||||
return False |
|
||||||
|
|
||||||
def _rank(self, image_features: torch.Tensor, text_embeds: torch.Tensor, top_count: int=1, reverse: bool=False) -> str: |
|
||||||
top_count = min(top_count, len(text_embeds)) |
|
||||||
text_embeds = torch.stack([torch.from_numpy(t) for t in text_embeds]).to(self.device) |
|
||||||
with torch.cuda.amp.autocast(): |
|
||||||
similarity = image_features @ text_embeds.T |
|
||||||
if reverse: |
|
||||||
similarity = -similarity |
|
||||||
_, top_labels = similarity.float().cpu().topk(top_count, dim=-1) |
|
||||||
return [top_labels[0][i].numpy() for i in range(top_count)] |
|
||||||
|
|
||||||
def rank(self, image_features: torch.Tensor, top_count: int=1, reverse: bool=False) -> List[str]: |
|
||||||
if len(self.labels) <= self.chunk_size: |
|
||||||
tops = self._rank(image_features, self.embeds, top_count=top_count, reverse=reverse) |
|
||||||
return [self.labels[i] for i in tops] |
|
||||||
|
|
||||||
num_chunks = int(math.ceil(len(self.labels)/self.chunk_size)) |
|
||||||
keep_per_chunk = int(self.chunk_size / num_chunks) |
|
||||||
|
|
||||||
top_labels, top_embeds = [], [] |
|
||||||
for chunk_idx in tqdm(range(num_chunks), disable=self.config.quiet): |
|
||||||
start = chunk_idx*self.chunk_size |
|
||||||
stop = min(start+self.chunk_size, len(self.embeds)) |
|
||||||
tops = self._rank(image_features, self.embeds[start:stop], top_count=keep_per_chunk, reverse=reverse) |
|
||||||
top_labels.extend([self.labels[start+i] for i in tops]) |
|
||||||
top_embeds.extend([self.embeds[start+i] for i in tops]) |
|
||||||
|
|
||||||
tops = self._rank(image_features, top_embeds, top_count=top_count) |
|
||||||
return [top_labels[i] for i in tops] |
|
||||||
|
|
||||||
|
|
||||||
def _download_file(url: str, filepath: str, chunk_size: int = 4*1024*1024, quiet: bool = False): |
|
||||||
r = requests.get(url, stream=True) |
|
||||||
if r.status_code != 200: |
|
||||||
return |
|
||||||
|
|
||||||
file_size = int(r.headers.get("Content-Length", 0)) |
|
||||||
filename = url.split("/")[-1] |
|
||||||
progress = tqdm(total=file_size, unit="B", unit_scale=True, desc=filename, disable=quiet) |
|
||||||
with open(filepath, "wb") as f: |
|
||||||
for chunk in r.iter_content(chunk_size=chunk_size): |
|
||||||
if chunk: |
|
||||||
f.write(chunk) |
|
||||||
progress.update(len(chunk)) |
|
||||||
progress.close() |
|
||||||
|
|
||||||
def _merge_tables(tables: List[LabelTable], ci: Interrogator) -> LabelTable: |
|
||||||
m = LabelTable([], None, ci) |
|
||||||
for table in tables: |
|
||||||
m.labels.extend(table.labels) |
|
||||||
m.embeds.extend(table.embeds) |
|
||||||
return m |
|
||||||
|
|
||||||
def _prompt_at_max_len(text: str, tokenize) -> bool: |
|
||||||
tokens = tokenize([text]) |
|
||||||
return tokens[0][-1] != 0 |
|
||||||
|
|
||||||
def _truncate_to_fit(text: str, tokenize) -> str: |
|
||||||
parts = text.split(', ') |
|
||||||
new_text = parts[0] |
|
||||||
for part in parts[1:]: |
|
||||||
if _prompt_at_max_len(new_text + part, tokenize): |
|
||||||
break |
|
||||||
new_text += ', ' + part |
|
||||||
return new_text |
|
||||||
|
|
||||||
def list_caption_models() -> List[str]: |
|
||||||
return list(CAPTION_MODELS.keys()) |
|
||||||
|
|
||||||
def list_clip_models() -> List[str]: |
|
||||||
return ['/'.join(x) for x in open_clip.list_pretrained()] |
|
||||||
|
|
||||||
def load_list(data_path: str, filename: Optional[str] = None) -> List[str]: |
|
||||||
"""Load a list of strings from a file.""" |
|
||||||
if filename is not None: |
|
||||||
data_path = os.path.join(data_path, filename) |
|
||||||
with open(data_path, 'r', encoding='utf-8', errors='replace') as f: |
|
||||||
items = [line.strip() for line in f.readlines()] |
|
||||||
return items |
|
File diff suppressed because it is too large
Load Diff
@ -1,41 +0,0 @@ |
|||||||
3d |
|
||||||
b&w |
|
||||||
bad anatomy |
|
||||||
bad art |
|
||||||
blur |
|
||||||
blurry |
|
||||||
cartoon |
|
||||||
childish |
|
||||||
close up |
|
||||||
deformed |
|
||||||
disconnected limbs |
|
||||||
disfigured |
|
||||||
disgusting |
|
||||||
extra limb |
|
||||||
extra limbs |
|
||||||
floating limbs |
|
||||||
grain |
|
||||||
illustration |
|
||||||
kitsch |
|
||||||
long body |
|
||||||
long neck |
|
||||||
low quality |
|
||||||
low-res |
|
||||||
malformed hands |
|
||||||
mangled |
|
||||||
missing limb |
|
||||||
mutated |
|
||||||
mutation |
|
||||||
mutilated |
|
||||||
noisy |
|
||||||
old |
|
||||||
out of focus |
|
||||||
over saturation |
|
||||||
oversaturated |
|
||||||
poorly drawn |
|
||||||
poorly drawn face |
|
||||||
poorly drawn hands |
|
||||||
render |
|
||||||
surreal |
|
||||||
ugly |
|
||||||
weird colors |
|
@ -1,16 +0,0 @@ |
|||||||
build: |
|
||||||
gpu: true |
|
||||||
cuda: "11.8" |
|
||||||
python_version: "3.10" |
|
||||||
system_packages: |
|
||||||
- "libgl1-mesa-glx" |
|
||||||
- "libglib2.0-0" |
|
||||||
python_packages: |
|
||||||
- "Pillow==10.0.0" |
|
||||||
- "safetensors==0.3.3" |
|
||||||
- "tqdm==4.66.1" |
|
||||||
- "open_clip_torch==2.20.0" |
|
||||||
- "accelerate==0.22.0" |
|
||||||
- "transformers==4.33.1" |
|
||||||
|
|
||||||
predict: "predict.py:Predictor" |
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,397 @@ |
|||||||
|
#film |
||||||
|
#myportfolio |
||||||
|
#pixelart |
||||||
|
#screenshotsaturday |
||||||
|
#vfxfriday |
||||||
|
1920s |
||||||
|
1970s |
||||||
|
1990s |
||||||
|
20 megapixels |
||||||
|
2d |
||||||
|
2d game art |
||||||
|
32k uhd |
||||||
|
35mm lens |
||||||
|
3840x2160 |
||||||
|
3d |
||||||
|
4k |
||||||
|
8k |
||||||
|
8k 3d |
||||||
|
8k resolution |
||||||
|
I can't believe how beautiful this is |
||||||
|
academic art |
||||||
|
acrylic art |
||||||
|
adafruit |
||||||
|
aesthetic |
||||||
|
aftereffects |
||||||
|
airbrush art |
||||||
|
ambient occlusion |
||||||
|
ambrotype |
||||||
|
american propaganda |
||||||
|
anaglyph effect |
||||||
|
anaglyph filter |
||||||
|
anamorphic lens flare |
||||||
|
androgynous |
||||||
|
angelic photograph |
||||||
|
angular |
||||||
|
anime |
||||||
|
anime aesthetic |
||||||
|
antichrist |
||||||
|
apocalypse art |
||||||
|
apocalypse landscape |
||||||
|
art |
||||||
|
art deco |
||||||
|
art on instagram |
||||||
|
artstation hd |
||||||
|
artstation hq |
||||||
|
artwork |
||||||
|
associated press photo |
||||||
|
atmospheric |
||||||
|
award winning |
||||||
|
award-winning |
||||||
|
backlight |
||||||
|
beautiful |
||||||
|
behance hd |
||||||
|
bioluminescence |
||||||
|
biomorphic |
||||||
|
black and white |
||||||
|
black background |
||||||
|
blueprint |
||||||
|
bob ross |
||||||
|
bokeh |
||||||
|
booru |
||||||
|
bryce 3d |
||||||
|
calotype |
||||||
|
chalk art |
||||||
|
character |
||||||
|
charcoal drawing |
||||||
|
chiaroscuro |
||||||
|
childs drawing |
||||||
|
chillwave |
||||||
|
chromatic |
||||||
|
cinematic |
||||||
|
cinematic lighting |
||||||
|
cinematic view |
||||||
|
circuitry |
||||||
|
cityscape |
||||||
|
clean |
||||||
|
close up |
||||||
|
cluttered |
||||||
|
colorful |
||||||
|
colorized |
||||||
|
commission for |
||||||
|
complementary colors |
||||||
|
concept art |
||||||
|
concert poster |
||||||
|
congruent |
||||||
|
constructivism |
||||||
|
contest winner |
||||||
|
contrasting |
||||||
|
cosmic horror |
||||||
|
creative commons attribution |
||||||
|
creepypasta |
||||||
|
criterion collection |
||||||
|
cryengine |
||||||
|
cubism |
||||||
|
cyanotype |
||||||
|
d&d |
||||||
|
da vinci |
||||||
|
dark |
||||||
|
dark and mysterious |
||||||
|
darksynth |
||||||
|
datamosh |
||||||
|
daz3d |
||||||
|
dc comics |
||||||
|
demonic photograph |
||||||
|
depth of field |
||||||
|
destructive |
||||||
|
detailed |
||||||
|
detailed painting |
||||||
|
deviantart |
||||||
|
deviantart hd |
||||||
|
digital illustration |
||||||
|
digital painting |
||||||
|
digitally enhanced |
||||||
|
diorama |
||||||
|
dramatic |
||||||
|
dramatic lighting |
||||||
|
dslr |
||||||
|
dslr camera |
||||||
|
dutch golden age |
||||||
|
dye-transfer |
||||||
|
dynamic composition |
||||||
|
dynamic pose |
||||||
|
dystopian art |
||||||
|
egyptian art |
||||||
|
elegant |
||||||
|
elite |
||||||
|
enchanting |
||||||
|
epic |
||||||
|
ethereal |
||||||
|
extremely gendered |
||||||
|
fantasy |
||||||
|
fauvism |
||||||
|
feminine |
||||||
|
film grain |
||||||
|
filmic |
||||||
|
fine art |
||||||
|
fisheye lens |
||||||
|
flat colors |
||||||
|
flat shading |
||||||
|
flemish baroque |
||||||
|
flickering light |
||||||
|
flickr |
||||||
|
fractalism |
||||||
|
freakshow |
||||||
|
fresco |
||||||
|
full body |
||||||
|
full of details |
||||||
|
furaffinity |
||||||
|
future tech |
||||||
|
futuristic |
||||||
|
genderless |
||||||
|
geometric |
||||||
|
glitch art |
||||||
|
glitchy |
||||||
|
glitter |
||||||
|
global illumination |
||||||
|
glorious |
||||||
|
glowing lights |
||||||
|
glowing neon |
||||||
|
god rays |
||||||
|
golden ratio |
||||||
|
goth |
||||||
|
gothic |
||||||
|
greeble |
||||||
|
groovy |
||||||
|
grotesque |
||||||
|
hall of mirrors |
||||||
|
handsome |
||||||
|
hard surface modeling |
||||||
|
hd |
||||||
|
hd mod |
||||||
|
hdr |
||||||
|
hellish |
||||||
|
hellish background |
||||||
|
henry moore |
||||||
|
high contrast |
||||||
|
high definition |
||||||
|
high detail |
||||||
|
high detailed |
||||||
|
high dynamic range |
||||||
|
high quality |
||||||
|
high quality photo |
||||||
|
high resolution |
||||||
|
holographic |
||||||
|
horror film |
||||||
|
hyper realism |
||||||
|
hyper-realistic |
||||||
|
hypnotic |
||||||
|
ilford hp5 |
||||||
|
ilya kuvshinov |
||||||
|
imax |
||||||
|
impressionism |
||||||
|
infrared |
||||||
|
ink drawing |
||||||
|
inspirational |
||||||
|
instax |
||||||
|
intricate |
||||||
|
intricate patterns |
||||||
|
iridescent |
||||||
|
irridescent |
||||||
|
iso 200 |
||||||
|
isometric |
||||||
|
kinetic |
||||||
|
kodak ektar |
||||||
|
kodak gold 200 |
||||||
|
kodak portra |
||||||
|
lighthearted |
||||||
|
logo |
||||||
|
lomo |
||||||
|
long exposure |
||||||
|
long lens |
||||||
|
lovecraftian |
||||||
|
lovely |
||||||
|
low contrast |
||||||
|
low poly |
||||||
|
lowbrow |
||||||
|
luminescence |
||||||
|
macabre |
||||||
|
macro lens |
||||||
|
macro photography |
||||||
|
made of all of the above |
||||||
|
made of beads and yarn |
||||||
|
made of cardboard |
||||||
|
made of cheese |
||||||
|
made of crystals |
||||||
|
made of feathers |
||||||
|
made of flowers |
||||||
|
made of glass |
||||||
|
made of insects |
||||||
|
made of liquid metal |
||||||
|
made of mist |
||||||
|
made of paperclips |
||||||
|
made of plastic |
||||||
|
made of rubber |
||||||
|
made of trash |
||||||
|
made of vines |
||||||
|
made of wire |
||||||
|
made of wrought iron |
||||||
|
majestic |
||||||
|
marble sculpture |
||||||
|
marvel comics |
||||||
|
masculine |
||||||
|
masterpiece |
||||||
|
matte background |
||||||
|
matte drawing |
||||||
|
matte painting |
||||||
|
matte photo |
||||||
|
maximalist |
||||||
|
messy |
||||||
|
minimalist |
||||||
|
minimalistic |
||||||
|
mist |
||||||
|
mixed media |
||||||
|
movie poster |
||||||
|
movie still |
||||||
|
multiple exposure |
||||||
|
muted |
||||||
|
mystical |
||||||
|
national geographic photo |
||||||
|
neon |
||||||
|
nightmare |
||||||
|
nightscape |
||||||
|
octane render |
||||||
|
official art |
||||||
|
oil on canvas |
||||||
|
ominous |
||||||
|
ominous vibe |
||||||
|
ornate |
||||||
|
orthogonal |
||||||
|
outlined art |
||||||
|
outrun |
||||||
|
painterly |
||||||
|
panorama |
||||||
|
parallax |
||||||
|
pencil sketch |
||||||
|
phallic |
||||||
|
photo |
||||||
|
photo taken with ektachrome |
||||||
|
photo taken with fujifilm superia |
||||||
|
photo taken with nikon d750 |
||||||
|
photo taken with provia |
||||||
|
photocollage |
||||||
|
photocopy |
||||||
|
photoillustration |
||||||
|
photorealistic |
||||||
|
physically based rendering |
||||||
|
picasso |
||||||
|
pixel perfect |
||||||
|
pixiv |
||||||
|
playstation 5 screenshot |
||||||
|
polished |
||||||
|
polycount |
||||||
|
pop art |
||||||
|
post processing |
||||||
|
poster art |
||||||
|
pre-raphaelite |
||||||
|
prerendered graphics |
||||||
|
pretty |
||||||
|
provia |
||||||
|
ps1 graphics |
||||||
|
psychedelic |
||||||
|
quantum wavetracing |
||||||
|
ray tracing |
||||||
|
realism |
||||||
|
redshift |
||||||
|
reimagined by industrial light and magic |
||||||
|
renaissance painting |
||||||
|
rendered in cinema4d |
||||||
|
rendered in maya |
||||||
|
rendered in unreal engine |
||||||
|
repeating pattern |
||||||
|
retrowave |
||||||
|
rich color palette |
||||||
|
rim light |
||||||
|
rococo |
||||||
|
rough |
||||||
|
rtx |
||||||
|
rtx on |
||||||
|
sabattier effect |
||||||
|
sabattier filter |
||||||
|
sanctuary |
||||||
|
sci-fi |
||||||
|
seapunk |
||||||
|
sense of awe |
||||||
|
sensual |
||||||
|
shallow depth of field |
||||||
|
sharp focus |
||||||
|
shiny |
||||||
|
shiny eyes |
||||||
|
shot on 70mm |
||||||
|
sketchfab |
||||||
|
skeuomorphic |
||||||
|
smokey background |
||||||
|
smooth |
||||||
|
soft light |
||||||
|
soft mist |
||||||
|
soviet propaganda |
||||||
|
speedpainting |
||||||
|
stained glass |
||||||
|
steampunk |
||||||
|
stipple |
||||||
|
stock photo |
||||||
|
stockphoto |
||||||
|
storybook illustration |
||||||
|
strange |
||||||
|
streetscape |
||||||
|
studio light |
||||||
|
studio lighting |
||||||
|
studio photography |
||||||
|
studio portrait |
||||||
|
stylish |
||||||
|
sunrays shine upon it |
||||||
|
surrealist |
||||||
|
symmetrical |
||||||
|
synthwave |
||||||
|
tarot card |
||||||
|
tattoo |
||||||
|
telephoto lens |
||||||
|
terragen |
||||||
|
tesseract |
||||||
|
thx sound |
||||||
|
tilt shift |
||||||
|
tintype photograph |
||||||
|
toonami |
||||||
|
trance compilation cd |
||||||
|
trypophobia |
||||||
|
ue5 |
||||||
|
uhd image |
||||||
|
ukiyo-e |
||||||
|
ultra detailed |
||||||
|
ultra hd |
||||||
|
ultra realistic |
||||||
|
ultrafine detail |
||||||
|
unreal engine |
||||||
|
unreal engine 5 |
||||||
|
vaporwave |
||||||
|
velvia |
||||||
|
vibrant colors |
||||||
|
vivid colors |
||||||
|
volumetric lighting |
||||||
|
voxel art |
||||||
|
vray |
||||||
|
vray tracing |
||||||
|
wallpaper |
||||||
|
watercolor |
||||||
|
wavy |
||||||
|
whimsical |
||||||
|
white background |
||||||
|
wiccan |
||||||
|
wide lens |
||||||
|
wimmelbilder |
||||||
|
windows vista |
||||||
|
windows xp |
||||||
|
woodcut |
||||||
|
xbox 360 graphics |
||||||
|
y2k aesthetic |
||||||
|
zbrush |
@ -1,45 +0,0 @@ |
|||||||
import sys |
|
||||||
from PIL import Image |
|
||||||
from cog import BasePredictor, Input, Path |
|
||||||
|
|
||||||
from clip_interrogator import Config, Interrogator |
|
||||||
|
|
||||||
|
|
||||||
class Predictor(BasePredictor): |
|
||||||
def setup(self): |
|
||||||
self.ci = Interrogator(Config( |
|
||||||
clip_model_name="ViT-L-14/openai", |
|
||||||
clip_model_path='cache', |
|
||||||
device='cuda:0', |
|
||||||
)) |
|
||||||
|
|
||||||
def predict( |
|
||||||
self, |
|
||||||
image: Path = Input(description="Input image"), |
|
||||||
clip_model_name: str = Input( |
|
||||||
default="ViT-L-14/openai", |
|
||||||
choices=["ViT-L-14/openai", "ViT-H-14/laion2b_s32b_b79k", "ViT-bigG-14/laion2b_s39b_b160k"], |
|
||||||
description="Choose ViT-L for Stable Diffusion 1, ViT-H for Stable Diffusion 2, or ViT-bigG for Stable Diffusion XL.", |
|
||||||
), |
|
||||||
mode: str = Input( |
|
||||||
default="best", |
|
||||||
choices=["best", "classic", "fast", "negative"], |
|
||||||
description="Prompt mode (best takes 10-20 seconds, fast takes 1-2 seconds).", |
|
||||||
), |
|
||||||
) -> str: |
|
||||||
"""Run a single prediction on the model""" |
|
||||||
image = Image.open(str(image)).convert("RGB") |
|
||||||
self.switch_model(clip_model_name) |
|
||||||
if mode == 'best': |
|
||||||
return self.ci.interrogate(image) |
|
||||||
elif mode == 'classic': |
|
||||||
return self.ci.interrogate_classic(image) |
|
||||||
elif mode == 'fast': |
|
||||||
return self.ci.interrogate_fast(image) |
|
||||||
elif mode == 'negative': |
|
||||||
return self.ci.interrogate_negative(image) |
|
||||||
|
|
||||||
def switch_model(self, clip_model_name: str): |
|
||||||
if clip_model_name != self.ci.config.clip_model_name: |
|
||||||
self.ci.config.clip_model_name = clip_model_name |
|
||||||
self.ci.load_clip_model() |
|
@ -1,3 +0,0 @@ |
|||||||
[build-system] |
|
||||||
requires = ["setuptools"] |
|
||||||
build-backend = "setuptools.build_meta" |
|
@ -1,9 +0,0 @@ |
|||||||
torch>=1.13.0 |
|
||||||
torchvision |
|
||||||
Pillow |
|
||||||
requests |
|
||||||
safetensors |
|
||||||
tqdm |
|
||||||
open_clip_torch |
|
||||||
accelerate |
|
||||||
transformers>=4.27.1 |
|
@ -1,95 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import argparse |
|
||||||
import csv |
|
||||||
import os |
|
||||||
import requests |
|
||||||
import torch |
|
||||||
from PIL import Image |
|
||||||
from clip_interrogator import Interrogator, Config, list_clip_models |
|
||||||
|
|
||||||
def inference(ci, image, mode): |
|
||||||
image = image.convert('RGB') |
|
||||||
if mode == 'best': |
|
||||||
return ci.interrogate(image) |
|
||||||
elif mode == 'classic': |
|
||||||
return ci.interrogate_classic(image) |
|
||||||
else: |
|
||||||
return ci.interrogate_fast(image) |
|
||||||
|
|
||||||
def main(): |
|
||||||
parser = argparse.ArgumentParser() |
|
||||||
parser.add_argument('-c', '--clip', default='ViT-L-14/openai', help='name of CLIP model to use') |
|
||||||
parser.add_argument('-d', '--device', default='auto', help='device to use (auto, cuda or cpu)') |
|
||||||
parser.add_argument('-f', '--folder', help='path to folder of images') |
|
||||||
parser.add_argument('-i', '--image', help='image file or url') |
|
||||||
parser.add_argument('-m', '--mode', default='best', help='best, classic, or fast') |
|
||||||
parser.add_argument("--lowvram", action='store_true', help="Optimize settings for low VRAM") |
|
||||||
|
|
||||||
args = parser.parse_args() |
|
||||||
if not args.folder and not args.image: |
|
||||||
parser.print_help() |
|
||||||
exit(1) |
|
||||||
|
|
||||||
if args.folder is not None and args.image is not None: |
|
||||||
print("Specify a folder or batch processing or a single image, not both") |
|
||||||
exit(1) |
|
||||||
|
|
||||||
# validate clip model name |
|
||||||
models = list_clip_models() |
|
||||||
if args.clip not in models: |
|
||||||
print(f"Could not find CLIP model {args.clip}!") |
|
||||||
print(f" available models: {models}") |
|
||||||
exit(1) |
|
||||||
|
|
||||||
# select device |
|
||||||
if args.device == 'auto': |
|
||||||
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
|
||||||
if not torch.cuda.is_available(): |
|
||||||
print("CUDA is not available, using CPU. Warning: this will be very slow!") |
|
||||||
else: |
|
||||||
device = torch.device(args.device) |
|
||||||
|
|
||||||
# generate a nice prompt |
|
||||||
config = Config(device=device, clip_model_name=args.clip) |
|
||||||
if args.lowvram: |
|
||||||
config.apply_low_vram_defaults() |
|
||||||
ci = Interrogator(config) |
|
||||||
|
|
||||||
# process single image |
|
||||||
if args.image is not None: |
|
||||||
image_path = args.image |
|
||||||
if str(image_path).startswith('http://') or str(image_path).startswith('https://'): |
|
||||||
image = Image.open(requests.get(image_path, stream=True).raw).convert('RGB') |
|
||||||
else: |
|
||||||
image = Image.open(image_path).convert('RGB') |
|
||||||
if not image: |
|
||||||
print(f'Error opening image {image_path}') |
|
||||||
exit(1) |
|
||||||
print(inference(ci, image, args.mode)) |
|
||||||
|
|
||||||
# process folder of images |
|
||||||
elif args.folder is not None: |
|
||||||
if not os.path.exists(args.folder): |
|
||||||
print(f'The folder {args.folder} does not exist!') |
|
||||||
exit(1) |
|
||||||
|
|
||||||
files = [f for f in os.listdir(args.folder) if f.endswith('.jpg') or f.endswith('.png')] |
|
||||||
prompts = [] |
|
||||||
for file in files: |
|
||||||
image = Image.open(os.path.join(args.folder, file)).convert('RGB') |
|
||||||
prompt = inference(ci, image, args.mode) |
|
||||||
prompts.append(prompt) |
|
||||||
print(prompt) |
|
||||||
|
|
||||||
if len(prompts): |
|
||||||
csv_path = os.path.join(args.folder, 'desc.csv') |
|
||||||
with open(csv_path, 'w', encoding='utf-8', newline='') as f: |
|
||||||
w = csv.writer(f, quoting=csv.QUOTE_MINIMAL) |
|
||||||
w.writerow(['image', 'prompt']) |
|
||||||
for file, prompt in zip(files, prompts): |
|
||||||
w.writerow([file, prompt]) |
|
||||||
|
|
||||||
print(f"\n\n\n\nGenerated {len(prompts)} and saved to {csv_path}, enjoy!") |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
main() |
|
@ -1,34 +0,0 @@ |
|||||||
import os |
|
||||||
|
|
||||||
import pkg_resources |
|
||||||
from setuptools import setup, find_packages |
|
||||||
|
|
||||||
setup( |
|
||||||
name="clip-interrogator", |
|
||||||
version="0.6.0", |
|
||||||
license='MIT', |
|
||||||
author='pharmapsychotic', |
|
||||||
author_email='me@pharmapsychotic.com', |
|
||||||
url='https://github.com/pharmapsychotic/clip-interrogator', |
|
||||||
description="Generate a prompt from an image", |
|
||||||
long_description=open('README.md', encoding='utf-8').read(), |
|
||||||
long_description_content_type="text/markdown", |
|
||||||
packages=find_packages(), |
|
||||||
install_requires=[ |
|
||||||
str(r) |
|
||||||
for r in pkg_resources.parse_requirements( |
|
||||||
open(os.path.join(os.path.dirname(__file__), "requirements.txt")) |
|
||||||
) |
|
||||||
], |
|
||||||
include_package_data=True, |
|
||||||
extras_require={'dev': ['pytest']}, |
|
||||||
classifiers=[ |
|
||||||
'Intended Audience :: Developers', |
|
||||||
'Intended Audience :: Science/Research', |
|
||||||
'License :: OSI Approved :: MIT License', |
|
||||||
'Topic :: Education', |
|
||||||
'Topic :: Scientific/Engineering', |
|
||||||
'Topic :: Scientific/Engineering :: Artificial Intelligence', |
|
||||||
], |
|
||||||
keywords=['blip','clip','prompt-engineering','stable-diffusion','text-to-image'], |
|
||||||
) |
|
Loading…
Reference in new issue