├── .gitignore ├── LICENSE ├── README.md ├── README_TCH.md ├── image ├── UI.png ├── add_clear.png ├── import.png ├── save_txt.png ├── save_txt_naifu.png └── save_txt_novelai.png ├── sample ├── naifu-sample.png ├── naifu-sample.txt ├── novelai-sample.png ├── novelai-sample.txt ├── stable-diffusion-test.png └── stable-diffusion-test.txt └── script ├── ai_tag_extractor.py ├── ai_tag_extractor_cmd.py ├── get_info.py └── get_info_short.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, JingShing 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English | [繁體中文](README_TCH.md) 2 | # AI-image-tag-extractor 3 | A tool to help you get image info. 4 | 5 | It is now perfect for extract Stable Diffusion image. 6 | 7 | It can also get otehr image info from other AI image. 8 | # Support AI image 9 | - [x] Stable diffusion 10 | - [x] Naifu(4chan ver) 11 | - [x] NovelAI 12 | # How to use 13 | * import 14 | * import image you like it will extract info and show in text box. 15 | * save 16 | * it will save a same file name as your image txt file in the same folder of your exe. 17 | * cmd(command or linux) 18 | * using python activate ```ai_tag_extractor_cmd.py``` then input the image file path you want to get info. 19 | # Sample Image 20 | It is spawn by Stable diffusion. And it is sample for showcase below. 21 | ## Stable-diffusion image 22 | ![test-stable-diffusion](/sample/stable-diffusion-test.png) 23 | ## Naifu(4chan ver) image 24 | ![test-naifu](/sample/naifu-sample.png) 25 | ## NovelAI image 26 | ![test-novelai](/sample/novelai-sample.png) 27 | 28 | # UI 29 | ![UI](image/add_clear.png) 30 | # After import image file 31 | ![import](image/import.png) 32 | # Save to txt 33 | ## stable-diffusion format 34 | ![save](image/save_txt.png) 35 | ## Naifu(4chan ver) format 36 | ![save_naifu](image/save_txt_naifu.png) 37 | ## NovelAI format 38 | ![save](image/save_txt_novelai.png) 39 | 40 | # Log 41 | ## ver 1.0 42 | * can import image and get image info. 43 | * can save image info 44 | * can support stable-diffusion 45 | * can support Naifu(4chan ver) 46 | ## ver 1.1 47 | * now it can support NovelAI image 48 | ## ver 1.1.1 49 | * fixed wrong file format import run time error. 50 | ## ver 1.2 51 | * add clear button 52 | * add status 53 | * ![add_clear](image/add_clear.png) 54 | ## ver 1.2.1 55 | * add ```ai_tag_extractor_cmd.py``` to use this tool in cmd or linux 56 | -------------------------------------------------------------------------------- /README_TCH.md: -------------------------------------------------------------------------------- 1 | [English](README.md) | 繁體中文 2 | # AI 圖片 關鍵字讀取器 3 | 一個幫你獲得圖片資訊的工具。 4 | 5 | 目前完美適配 Stable diffusion。 6 | 7 | 這個工具也能幫你獲得其他AI圖片資訊。 8 | # 目前支持的AI圖片 9 | - [x] Stable diffusion 10 | - [x] Naifu(4chan版本) 11 | - [x] NovelAI(官網版本) 12 | # 如何使用 13 | * import 14 | * 導入想提取資訊的圖片,它會幫忙提取出資訊,並填到文字框中。 15 | * save 16 | * 它會在和此提取器同個資料夾中創建一個同名的 txt 檔,並把所有資訊填到裡面。 17 | * cmd 環境(純指令或 linux 環境) 18 | * 使用 python 啟動 ```ai_tag_extractor_cmd.py``` 輸入需要拆解資訊的圖片路徑 19 | # 範例圖片 20 | 這個圖片是用 Stable diffusion 生成的。將會作為下方展示的範例圖片。 21 | 22 | ## Stable-diffusion 圖片 23 | ![test-stable-diffusion](/sample/stable-diffusion-test.png) 24 | ## Naifu(4chan 版本) 圖片 25 | ![test-naifu](/sample/naifu-sample.png) 26 | ## NovelAI 圖片 27 | ![test-novelai](/sample/novelai-sample.png) 28 | # UI 29 | ![UI](image/add_clear.png) 30 | # After import image file 31 | ![import](image/import.png) 32 | # 保存成 txt 33 | ## stable-diffusion 格式 34 | ![save](image/save_txt.png) 35 | ## Naifu(4chan ver) 格式 36 | ![save_naifu](image/save_txt_naifu.png) 37 | ## NovelAI 格式 38 | ![save](image/save_txt_novelai.png) 39 | 40 | # 紀錄 41 | ## 版本 1.0 42 | * 可以導入圖片得到圖片資訊。 43 | * 可以儲存圖片資訊。 44 | * 可以支持 stable-diffusion。 45 | * 可以支持 Naifu(4chan版本)。 46 | ## 版本 1.1 47 | * 現在支持 NovelAI 官網圖片的格式。 48 | ## 版本 1.1.1 49 | * 修復導入錯誤檔案的 run time error。 50 | ## 版本 1.2 51 | * 新增清除按鈕 52 | * 新增狀態提示 53 | * ![add_clear](image/add_clear.png) 54 | ## 版本 1.2.1 55 | * 新增 ```ai_tag_extractor_cmd.py``` 可以在 cmd 和 linux 環境使用 56 | -------------------------------------------------------------------------------- /image/UI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/image/UI.png -------------------------------------------------------------------------------- /image/add_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/image/add_clear.png -------------------------------------------------------------------------------- /image/import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/image/import.png -------------------------------------------------------------------------------- /image/save_txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/image/save_txt.png -------------------------------------------------------------------------------- /image/save_txt_naifu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/image/save_txt_naifu.png -------------------------------------------------------------------------------- /image/save_txt_novelai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/image/save_txt_novelai.png -------------------------------------------------------------------------------- /sample/naifu-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/sample/naifu-sample.png -------------------------------------------------------------------------------- /sample/naifu-sample.txt: -------------------------------------------------------------------------------- 1 | parameters 2 | ((masterpiece)), ((ultra-detailed)), ((illustration)), 1 girl,solo,highly detailed,extremely detailed CG unity 8k wallpaper, 3 | (Brilliant starry sky),wind,((cinematic lighting)),cinematic angle,((beautiful detailed face)),(Gradient color eyes), 4 | (Jewel-like eyes),(beautiful detailed eyes),(Eyes highlight), 5 | ((among blue-purple flowers)),(beautiful detailed hands),(Hands covered by flowers),Blur hands,(Hide hands),arm_support,(Flowers blooming),((Colorful plumage)), ribbons, ,hair_bow,lacy,long_sleeves,buttons, pleated skirt,cross-laced foot_wear, 6 | detailed wet clothes,Cold smile,((disheveled hair)),good lighting,glass tint, 7 | flowing hair, glossy hair, Silky hair, ((frills)), (big top sleeves), floating, overexposure,Random pose, hairs between eyes, 8 | flowers, 9 | (beautiful detailed sky), CG,misty,posing sketch, 10 | (oil painting),Thick coating,first-person view 11 | ,hair dripping,cold and elegant 12 | Negative prompt: nsfw, owres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, 13 | worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, 14 | username, blurry,missing fingers,bad hands,missing arms, long neck, Humpbacked 15 | Steps: 45, Sampler: Euler, CFG scale: 4.5, Seed: 2593613875, Size: 1024x768, Model hash: e6e8e1fc, Eta: 0.68, Clip skip: 2, ENSD: 31337 16 | -------------------------------------------------------------------------------- /sample/novelai-sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/sample/novelai-sample.png -------------------------------------------------------------------------------- /sample/novelai-sample.txt: -------------------------------------------------------------------------------- 1 | Title 2 | AI generated image 3 | Description 4 | masterpiece, best quality, atmosphere, delicate, extremely detailed, extremely detailed CG unity 8k wallpaper, (1male) ,good anatomy, pale skin,short light blue hair with long side bangs, yellow eyes, evil smile,white very long jacket, white clothes,dark tone art, alchemical room 5 | Software 6 | NovelAI 7 | Source 8 | Stable Diffusion 1D44365E 9 | Comment 10 | {"steps": 28, "sampler": "k_euler_ancestral", "seed": 3444734261, "strength": 0.69, "noise": 0.667, "scale": 11.0, "uc": "lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, bad feet, futa, futanari, yaoi,huge_breasts, large_breasts"} 11 | -------------------------------------------------------------------------------- /sample/stable-diffusion-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JingShing/AI-image-tag-extractor/bda8a2bd1da8c14c4a2c5f770cd7d014521ef257/sample/stable-diffusion-test.png -------------------------------------------------------------------------------- /sample/stable-diffusion-test.txt: -------------------------------------------------------------------------------- 1 | parameters 2 | extremely detailed CG unity 8k wallpaper, high quality, flat color, 4 years old, a beautiful lovely sweet cute loli girl, black and red cloak, red pupil, white hair, not face viewer, cold attitude, bowtie, nude 3 | Negative prompt: lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, bad feet 4 | Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 1263451577, Size: 512x512, Model hash: 925997e9 5 | -------------------------------------------------------------------------------- /script/ai_tag_extractor.py: -------------------------------------------------------------------------------- 1 | from tkinter import * 2 | from tkinter.ttk import * 3 | from tkinter import messagebox 4 | from tkinter import filedialog 5 | from PIL import Image 6 | 7 | def load_spell_list(path): 8 | file = open(path, encoding="utf-8") 9 | word_list = [] 10 | for line in file.readlines(): 11 | line = line.replace('\n', '') 12 | word_list.append(line) 13 | file.close() 14 | return word_list 15 | 16 | class Gui_helper_main: 17 | def __init__(self): 18 | self.root = Tk() 19 | self.frame = None 20 | self.frame_index = 0 21 | self.root.geometry('500x350') 22 | self.root.title('Tag Extractor') 23 | self.root.protocol("WM_DELETE_WINDOW", self.quit) 24 | # maker info 25 | self.maker_name = Label(self.root, text="Maker : JingShing") 26 | self.maker_name.grid(column=0, row=3, sticky=N+W) 27 | 28 | self.frames = [page_module(self)] 29 | self.switch_frame(0) 30 | 31 | def switch_frame(self, index): 32 | if self.frame is not None: 33 | self.frame.grid_forget() 34 | self.frame_index = index 35 | self.frame = self.frames[self.frame_index] 36 | self.frame.grid(column=0, row=0, sticky=N+W) 37 | 38 | def run(self): 39 | self.root.mainloop() 40 | 41 | def quit(self): 42 | if messagebox.askyesno('Confirm','Are you sure you want to quit?'): 43 | self.root.quit() 44 | 45 | class page_module(Frame): 46 | def __init__(self, master): 47 | Frame.__init__(self, master = master.root) 48 | self.main = master 49 | self.master = master.root 50 | self.data_dict = {} 51 | self.novelai_dict = {} 52 | self.all_text = Text(self, width=30, height=10) 53 | self.all_text.grid(column=2, row=0, sticky=N+W, columnspan=3, rowspan=5) 54 | self.all_label = Label(self, text="all info") 55 | self.all_label.grid(column=2, row=3) 56 | 57 | self.prompt_text = Text(self, width=30, height=5) 58 | self.prompt_text.grid(column=0, row=0, sticky=N+W, columnspan=2) 59 | self.prompt_label = Label(self, text="prompt") 60 | self.prompt_label.grid(column=0, row=1) 61 | self.negative_text = Text(self, width=30, height=5) 62 | self.negative_text.grid(column=0, row=2, sticky=N+W, columnspan=2) 63 | self.negative_label = Label(self, text="negative prompt") 64 | self.negative_label.grid(column=0, row=3) 65 | self.parameter_text = Text(self, width=30, height=5) 66 | self.parameter_text.grid(column=0, row=4, sticky=N+W, columnspan=2) 67 | self.parameter_label = Label(self, text="parameter") 68 | self.parameter_label.grid(column=0, row=5) 69 | self.import_button = Button(self, text='import', command=self.import_set) 70 | self.import_button.grid(column=0, row=6, sticky=N+W) 71 | self.save_button = Button(self, text='save', command=self.save_set) 72 | self.save_button.grid(column=1, row=6, sticky=N+W) 73 | self.clear_button = Button(self, text='clear', command=self.text_clear) 74 | self.clear_button.grid(column=2, row=6, sticky=N+W) 75 | 76 | self.status_name = StringVar() 77 | self.status = 'None' 78 | self.now_status_str = 'Now status : ' 79 | self.status_display = Label(self, textvariable=self.status_name) 80 | self.status_display.grid(column=0, row=7, sticky=N+W, rowspan=3) 81 | self.set_label_text() 82 | 83 | def set_label_text(self): 84 | self.status_name.set(self.now_status_str + self.status) 85 | 86 | def add_text(self, text): 87 | if type(text) == dict: 88 | if 'Title' in text:# for novelai image 89 | self.novelai_dict = text.copy() 90 | else:# for naifu or stable-diffusion image 91 | self.novelai_dict.clear() 92 | self.dict_to_text(text) 93 | else: 94 | self.data_dict['spell'] = text 95 | self.get_info() 96 | self.text_clear() 97 | if self.data_dict['spell'] != '': 98 | self.all_text.insert(END, self.data_dict['spell']) 99 | self.prompt_text.insert(END, self.data_dict['prompt']) 100 | self.negative_text.insert(END, self.data_dict['negative']) 101 | self.parameter_text.insert(END, self.data_dict['parameter']) 102 | 103 | def text_clear(self): 104 | self.all_text.delete(1.0, 'end') 105 | self.prompt_text.delete(1.0, 'end') 106 | self.negative_text.delete(1.0, 'end') 107 | self.parameter_text.delete(1.0, 'end') 108 | self.status = 'had clear' 109 | self.set_label_text() 110 | 111 | def get_info(self):# use info dict to get info 112 | if 'Title' in self.novelai_dict:# for novelai image 113 | self.data_dict['prompt'] = self.novelai_dict['Description'] 114 | self.data_dict['negative'] = self.novelai_dict['Comment'].split('uc')[-1].replace('"}', '').replace('"', '').replace(": ", '') 115 | self.data_dict['parameter'] = self.novelai_dict['Comment'].replace(self.data_dict['negative'], '').replace('"', '').replace("{", "").replace("}", "") 116 | else:# for naifu or stable-diffusion image 117 | if len(self.data_dict['spell'])>0:# to keep no spell 118 | info = self.data_dict['spell'].replace("parameters\n","").replace("Negative prompt: ", "").split("\n") 119 | self.data_dict['prompt']=info[0] 120 | self.data_dict['negative']=info[1] 121 | self.data_dict['parameter']=info[2] 122 | 123 | def dict_to_text(self, dict1):# change info dict to txt format 124 | self.data_dict['spell'] = '' 125 | if dict1: 126 | for key in dict1: 127 | if type(key) == str and type(dict1[key])==str:# to keep weird type 128 | self.data_dict['spell']+=key + '\n' 129 | self.data_dict['spell']+=dict1[key] + '\n' 130 | 131 | def import_set(self):# import image to get info 132 | set_path = filedialog.askopenfilename(filetypes = (("png file","*.png"),("all files","*.*"))) 133 | self.status = 'loading' 134 | self.set_label_text() 135 | if set_path: 136 | self.text_clear()# now will clear text box after import 137 | im = Image.open(set_path) 138 | im.load() 139 | self.data_dict['file_name'] = set_path.split('/')[-1].split('.')[0] 140 | self.add_text(im.info) 141 | self.status = 'had imported set' 142 | self.set_label_text() 143 | 144 | def save_set(self):# save to txt 145 | f = open(self.data_dict['file_name'] + ".txt", "w") 146 | f.write(str(self.data_dict['spell'])) 147 | f.close() 148 | self.status = 'save complete' 149 | self.set_label_text() 150 | 151 | main = Gui_helper_main() 152 | main.run() 153 | -------------------------------------------------------------------------------- /script/ai_tag_extractor_cmd.py: -------------------------------------------------------------------------------- 1 | try: 2 | # try to import Pillow 3 | from PIL import Image 4 | print("Please enter the file path: ", end='') 5 | # get file path if file path has ' or " will be deleted 6 | file_path = input().replace('\'', '').replace('"','') 7 | try: 8 | # try to load image 9 | info = Image.open(file_path).info 10 | if info: 11 | # print all info on cmd 12 | for key in info.keys(): 13 | print(key + ': ' + info[key]) 14 | else: 15 | print("nothing info in it!") 16 | except: 17 | # wrong format or cannot load image 18 | print("Wrong file format!") 19 | except: 20 | # you need to get pillow to use this code 21 | print("You don't have Pillow please install it by this command: pip install Pillow") 22 | -------------------------------------------------------------------------------- /script/get_info.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | #filename = 'test.png' 3 | print("Please enter the file path") 4 | filename = input() 5 | im = Image.open(filename) 6 | #im.load() 7 | print(im.info) 8 | -------------------------------------------------------------------------------- /script/get_info_short.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | print(Image.open(input()).info) 3 | --------------------------------------------------------------------------------