├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── prompt_tree.yml ├── scripts └── clonecleaner.py └── style.css /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 artyfacialintelagent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CloneCleaner 2 | 3 | An extension for Automatic1111 to work around Stable Diffusion's "clone problem". It automatically modifies your prompts with random names, nationalities, hair style and hair color to create more variations in generated people. 4 | 5 | # What it does 6 | 7 | Many generations of model finetuning and merging have greatly improved the image quality of Stable Diffusion 1.5 when generating humans - but at the cost of overtraining and loss of variability. This manifests as "clones", where batch generations using the same or similar prompts but different random seeds often have identical facial features. 8 | 9 | CloneCleaner adds randomized tokens to your prompt that varies the look of a person for every generated image seed. For example, one seed might get "Elsie from Australia, long waist-length updo ginger hair" and the next "Samreen from Bangladesh, medium shoulder-length frizzy coffee-colored hair". This makes every seed quite unique and effectively mitigates the "sameface" problem of many popular but heavily overtrained Stable Diffusion models. So it's basically wildcards, except the tiresome setup work has been done for you and you're ready to go - with lots of easy customization options to control the randomization. 10 | 11 | A key detail is that the token randomization seed is (by default) identical to the main image seed - this ensures that the same "person" will be generated again if you modify the prompt or reload the metadata. 12 | 13 | # Installation in Automatic1111 14 | 15 | Enter this url **manually** in auto1111's extension tab: 16 | 17 | https://github.com/artyfacialintelagent/CloneCleaner.git 18 | 19 | This first release of CloneCleaner is a public beta and **currently only works for female characters**. Options for male (and indeterminate gender if I can get it to work) coming soon-ish! 20 | 21 | # How it works 22 | 23 | Prompts are randomized using wildcards, except they're hardcoded in the extension with logic to match ethnicity with typical names and common hair colors for each country, in order to get consistent appearance and quality of the generated images. Main steps: 24 | 25 | 1. Set the token randomization seed to the main image seed (or optionally a different random seed or a user-specified one). 26 | 2. Select a random REGION among the following: Europe (mainland incl. Russia), Anglosphere (US, Can, Aus, NZ, UK), Latin America, MENA (Middle-East & North Africa), Sub-Saharan Africa, East Asia or South Asia. 27 | 3. Select a random COUNTRY in that region - but note that CloneCleaner only has a sample of countries of each region, the database is not (yet) comprehensive. 28 | 4. Select a random FIRST NAME for people in that country. 29 | 5. Select a random MAIN HAIR COLOR (only black, brown, red, blonde, other), weighted for what is typical in that country. Then select a SPECIFIC HAIR COLOR with more "colorful" (sorry) language for more variability. Color tokens are carefully selected and limited using tricks like reduced attention and prompt editing to minimize "color bleeding" into the rest of your prompt. 30 | 6. Select random HAIR STYLE and HAIR LENGTH. 31 | 7. Assemble the prompt insert: "[FIRST NAME] from [COUNTRY], [HAIR LENGTH] [HAIR STYLE] [SPECIFIC HAIR COLOR] hair". Insert it at beginning or end of the prompt depending on user options 32 | 8. Iterate for remaining images of the batch. 33 | 34 | Any of the above token components can be optionally excluded from the prompt randomization. There are also customization options to exclude specific regions, hair lengths and hair color. 35 | 36 | # Sample images 37 | 38 | The following images are produced using **consecutive seeds**, so they are **NOT CHERRY-PICKED** in any way. But to be fair, not all models work quite as well as these - some models are so overtrained that they just can't be saved. The sample PNGs hosted here on Github should contain full metadata, so you can download and inspect them in the PNGinfo tab, or send them to txt2img to reproduce them. 39 | 40 | Right-click and select "Open image in a new tab" to view at 100%, or right-click and select "Save image as" to download. 41 | 42 | ### Absolute Reality v1 43 | 44 | These images were created using the [Absolute Reality model](https://civitai.com/models/81458/absolutereality), from a simple test prompt I made up. 45 | 46 | **Absolute Reality v1 (baseline model images)** 47 | ![absolutereality_seeds](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/d69ef1e4-6cf6-4401-97bb-bc0eeeef8a2a) 48 | ![absolutereality1](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/64f4fb70-0764-470d-a00a-07b8137000f5) 49 | 50 | **Absolute Reality v1 + CloneCleaner (default settings)** 51 | ![absolutereality2](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/a495ac69-cf17-4be9-814f-389403280c39) 52 | 53 | **Absolute Reality v1 + CloneCleaner (East Asia only)** 54 | ![absolutereality3](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/9aeeb238-e980-43a8-b5aa-c54baec0a41d) 55 | 56 | **Absolute Reality v1 + CloneCleaner (Anglosphere + Europe only, short hair only)** 57 | ![absolutereality4](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/f0218f86-9d53-4127-b9d9-5233e4e46776) 58 | 59 | ### Photon v1 60 | 61 | Using the prompt from the sample image hosted on the [Photon model page on Civitai](https://civitai.com/models/84728/photon), slightly modified to make it more SFW for Github. 62 | 63 | **Photon v1 (baseline model images)** 64 | ![photon_seeds](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/33fad4a4-651b-4806-92cc-3c1a01d58fc9) 65 | ![photon1](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/cedea29e-c2c5-4c53-9569-2929db095971) 66 | 67 | **Photon v1 + CloneCleaner (default settings)** 68 | ![photon2](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/5da77a58-06f2-45c7-8920-95bf25390de9) 69 | 70 | **Photon v1 + CloneCleaner (Africa + South Asia only, no blonde or "other" hair color)** 71 | ![photon3](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/cf8759c4-9ada-4c6e-be6e-4829e3c34eeb) 72 | 73 | **Photon v1 + CloneCleaner (Europe only, reddish hair only)** 74 | ![photon4](https://github.com/artyfacialintelagent/CloneCleaner/assets/137619889/58d1755a-1d0c-4c20-bc98-f00a80816147) 75 | 76 | # Some tips 77 | 78 | I am very happy with how well this simple prompt modification scheme works. But the best part about CloneCleaner is how it made me completely re-evaluate my opinion of many models, mostly positively. So be sure to retest your models using CloneCleaner - they may yet surprise you! 79 | 80 | I recommend using **simple prompts** (< 50 tokens), **simple negatives** (< 30 tokens) and **limited attention weighting** (never > :1.2, except as noted below). An effective minimal negative prompt appears below. Just start with this as a basis and add whatever your image seems to need. 81 | 82 | **Negative prompt**: *ugly, asian, underage, 3d render, cartoon, doll, (bad low worst quality:1.3)* 83 | 84 | The token "asian" is included to counter the heavy bias towards Korean and Chinese women among most popular models. Asian characters should still appear and look perfectly fine even including this token. Usually the attention setting of the final quality prompt should stick to the range 1.1 - 1.4, but a small number of Asian-oriented models can benefit from high (~1.5) or extreme values (up to 2.0!). Note that this is the exception that proves the rule - in most models such extreme weights would heavily "overcook" your images and destroy both quality and variability. 85 | 86 | I rarely use attention weights above 1.0 in my prompts and **never** use attention weights above 1.2 for any other tokens in my prompt other than this general quality negative. In my experience this greatly benefits image consistency and reduces mutations, bad hands and other monstrosities without having to explicitly include these things in your negatives. 87 | -------------------------------------------------------------------------------- /prompt_tree.yml: -------------------------------------------------------------------------------- 1 | hair: 2 | length: 3 | short: [short, (short-cut:1.2)] 4 | medium: ["medium (shoulder-length:1.2)", "medium (chin-length:1.2)"] 5 | long: [long, "long (waist-length:1.2)"] 6 | style: 7 | short: [straight, bangs, combed, "[undercut|punk]", undercut, brushed-back, slicked-back, brush-cut, pixie-cut, semi-buzzcut, buzzcut, mohawk-style, curly, spiky hairstyle, tussled, bob-cut, pigtails, twintails, ponytail] 8 | medium: [straight, windblown, bangs, combed, brushed-back, slicked-back, brush-cut, grown-out pixie-cut, wavy, curly, mohawk-style, frizzy, wild, messy, bob-cut, updo, pigtails, twintails, ponytail] 9 | long: [straight, windblown, bangs, combed, brushed-back, slicked-back, brush-cut, wavy, curly, frizzy, wild, messy, updo, glorious, pigtails, twintails, ponytail] 10 | color: 11 | blonde: [platinum-blonde, ash-blonde, champagne-blonde, sandy-blonde, honey-blonde, dirty-blonde, "[blonde:strawberry-blonde:.1]", 12 | "[blonde:vanilla-blonde:.2]", "[blonde:golden-blonde:.2]", "[blonde:reddish-blonde:.2]", "[blonde:rose-gold:.2]"] 13 | red: [auburn, ginger, peach, "[auburn:coral:.3]", "[auburn:copper:.3]", "[auburn:reddish:.4]", "[auburn:crimson:.4]", "[auburn:orange:.4]"] 14 | brown: [mocca, chestnut, hazelnut, caramel, cinnamon, coffee, chocolate, charcoal, "[honey-brown:.3]", "[golden-brown:.3]", 15 | "[noisette:.3]", "[bronze:.3]", "[mahogany:.2]", "[maroon:.4]", "[burgundy:.4]"] 16 | black: ["[black:.3]", "[nearly black:.3]", "[blue-black:.3]", "[violet-black:.3]", "[reddish-black:.3]"] 17 | other: ["[blonde:pastel:.2]", "[blonde:gray-blonde:.2]", "[blonde:silver-blonde:.2]", "[blonde:neon:.3]", "[blonde:multicolored:.3]", 18 | "[blonde:rainbow:.3]", "[blonde:pink:.3]", "[blonde:emerald:.3]", "[blonde:mint:.3]", "[blonde:teal:.3]", "[blonde:indigo:.3]", 19 | "[blonde:lilac:.3]", "[blonde:lavender:.3]", "[blonde:violet:.3]"] 20 | defaultweight: 21 | Europe: {blonde: 20, red: 15, brown: 35, black: 25, other: 5} 22 | Anglosphere: {blonde: 15, red: 10, brown: 35, black: 35, other: 5} 23 | MENA: {blonde: 5, red: 5, brown: 10, black: 75, other: 5} 24 | Sub-Saharan Africa: {blonde: 5, red: 5, brown: 10, black: 75, other: 5} 25 | Latin America: {blonde: 10, red: 5, brown: 30, black: 50, other: 5} 26 | East Asia: {blonde: 10, red: 5, brown: 10, black: 70, other: 5} 27 | South Asia: {blonde: 5, red: 5, brown: 10, black: 75, other: 5} 28 | country: 29 | Europe: 30 | Sweden: 31 | weight: 3 32 | hair: {blonde: 35, red: 15, brown: 30, black: 15, other: 5} 33 | names: [Astrid, Freja, Maja, Ebba, Linnea, Agnes, Amelia, Wilma, Elin, Lovisa, Liv, Alva, Märta, Sigrid, Lova, Emelie, Ida, Åsa, Hilma, Frida, Tyra, Siv, Linn] 34 | Norway: 35 | weight: 2 36 | hair: {blonde: 35, red: 15, brown: 30, black: 15, other: 5} 37 | names: [Hedda, Tiril, Tone, Agnes, Silje, Vilde, Martine, Stine, Maren, Mari, Synne, Hanne, Sigrid, Ingvild, Lene, Wenche, Maiken, Ragnhild] 38 | Denmark: 39 | weight: 2 40 | hair: {blonde: 35, red: 15, brown: 30, black: 15, other: 5} 41 | names: [Nanna, Signe, Amalie, Asta, Nora, Mette, Kirsten, Hanne, Helle, Lene, Lærke, Gry, Laura, Esther, Mathilde, Vera] 42 | Finland: 43 | weight: 2 44 | hair: {blonde: 35, red: 15, brown: 30, black: 15, other: 5} 45 | names: [Aino, Helmi, Aada, Onni, Venla, Iida, Eevi, Lilja, Kaarina, Enni, Elli, Pihla, Hannele, Marjatta, Minea, Hilla, Kerttu, Siiri, Veera, Nelli] 46 | Scotland: 47 | weight: 2 48 | hair: {blonde: 20, red: 30, brown: 25, black: 20, other: 5} 49 | names: [Isla, Ava, Amelia, Jamie, Sarah, Lucy, Fiona, Evie, Iona, Amy, Eilidh, Bonnie, Sienna, Callie, Skye, Holly, Eilish, Caitlin, Mairi, Moira] 50 | Ireland: 51 | weight: 2 52 | hair: {blonde: 20, red: 30, brown: 25, black: 20, other: 5} 53 | names: [Aoife, Niamh, Caoimhe, Maeve, Cian, Fiadh, Orla, Cara, Molly, Shannon, Kelly, Eabha , Roisin, Riley, Kennedy, Sloane, Fiona, Aisling] 54 | Netherlands: 55 | weight: 3 56 | names: [Mila, Yara, Evi, Saar, Lieke, Noor, Lotte, Milou, Nova, Fenna, Isa, Roos, Loïs, Esmee, Fien, Lara, Jasmijn, Kiki, Veerle, Juul, Femke, Philou, Guusje] 57 | Belgium: 58 | weight: 3 59 | names: [Marie, Olivia, Camille, Mila, Alexis, Julie, Sacha, Martine, Nathalie, Monique, Rita, Nicole, Christine, Isabelle] 60 | Germany: 61 | weight: 4 62 | names: [Johanna, Heidi, Elke, Ilse, Ursula, Frieda, Helga, Anja, Eva, Leni, Berta, Eleonor, Frederike, Franka, Henriette, Irma, Luisa, Margarete] 63 | Czech Republic: 64 | weight: 3 65 | names: [Barbora, Agata, Adela, Marketa, Karolina, Aneta, Jana, Tereza, Andrea, Zuzana, Martina, Dominika, Ema] 66 | Poland: 67 | weight: 4 68 | names: [Antonina, Beata, Anastazja, Aniela, Genowefa, Adelajda, Brygida, Alina, Aldona, Estera, Ewelina, Adela, Augustyna, Aneta, Albina, Alfreda] 69 | England: 70 | weight: 4 71 | names: [Ivy, Amelia, Emily, Alice, Hannah, Eleanor, Daisy, Phoebe, Evie, Florence, Ellie, Poppy, Rosie, Anne, Harriet, Jane, Rebecca, Mary, Alice, Elizabeth, Abigail, Holly, Lucy, Nancy] 72 | France: 73 | weight: 4 74 | hair: {blonde: 15, red: 10, brown: 35, black: 35, other: 5} 75 | names: [Camille, Chloé, Amélie, Celine, Anais, Dominique, Elise, Genevieve, Colette, Delphine, Jeanne, Sylvie, Margot, Eloise, Cecile, Inès, Lucie, Léonie] 76 | Switzerland: 77 | weight: 3 78 | hair: {blonde: 15, red: 10, brown: 35, black: 35, other: 5} 79 | names: [Laura, Alina, Mila, Lena, Lina, Alessia, Chiara, Andrea, Jana, Malia, Elea, Liana, Lara, Lia, Livia, Melina] 80 | Austria: 81 | weight: 3 82 | names: [Johanna, Lena, Alina, Lea, Valentina, Amélie, Sophie, Magdalena, Marlene, Daniela, Hanna, Leni, Lara, Paulina, Frieda, Mathea, Liana] 83 | Hungary: 84 | weight: 3 85 | names: [Lili, Aliz, Bianka, Lilla, Dorina, Dora, Mira, Zita, Emese, Izabella, Liza, Regina, Vivien] 86 | Slovakia: 87 | weight: 3 88 | names: [Ema, Nina, Eliška, Viktória, Natália, Nela, Diana, Tamara, Júlia, Karolína, Michaela, Rebeka, Dominika, Lenka, Betka] 89 | Croatia: 90 | weight: 3 91 | names: [Elena, Nika, Marta, Marija, Andrea, Ines, Adrijana, Mirjana, Lucija, Petra, Rita, Iva, Mila, Leona, Tena, Franka, Katja, Maša, Nikol, Bruna] 92 | Serbia: 93 | weight: 3 94 | names: [Natalija, Ljubica, Milena, Marija, Adrijana, Mirjana, Jovana, Aleksa, Anja, Gordana, Tijana, Marina, Iskra, Petra] 95 | Portugal: 96 | weight: 3 97 | names: [Leonor, Matilde, Beatriz, Carolina, Mariana, Ana, Sofia, Francisca, Inês, Margarida, Benedita, Madalena, Joana, Camila] 98 | Spain: 99 | weight: 4 100 | hair: {blonde: 10, red: 5, brown: 30, black: 50, other: 5} 101 | names: [Ana, Sofia, Isabella, Lucia, Elena, Carmen, Lola, Mariana, Gabriela, Daniela, Adriana, Valeria, Esmeralda, Juana, Natalia] 102 | Italy: 103 | weight: 4 104 | hair: {blonde: 10, red: 5, brown: 30, black: 50, other: 5} 105 | names: [Francesca, Chiara, Giulia, Aurora, Bianca, Alessia, Andrea, Caterina, Lucia, Gaia, Isabella, Giovanna, Elisa, Gabriella, Valentina, Viola] 106 | Greece: 107 | weight: 3 108 | hair: {blonde: 10, red: 5, brown: 30, black: 50, other: 5} 109 | names: [Eleni, Katerina, Vasiliki, Sophia, Angeliki, Georgia, Dimitra, Konstandina, Paraskevi, Anastasia, Joanna, Danae, Athina, Melina, Ioanna] 110 | Romania: 111 | weight: 3 112 | names: [Antonia, Andreea, Ioana, Alexandra, Luminita, Alina, Bianca, Iolanda, Lavinia, Daniela, Stefana, Doina, Simona, Iulian, Claudia, Angelika] 113 | Turkey: 114 | weight: 4 115 | names: [Elif, Ayşe, Asli, Azra, Aysun, Emel, Dilara, Derya, Afet, Alara, Beyza, Damla, Meryem, Ceren, Nuray, Miray, Ayten, Esra, Eymen, Caria, Esma] 116 | Ukraine: 117 | weight: 4 118 | names: [Aleksandra, Daryna, Olena, Lavra, Ivanna, Anastasia, Bohdana, Maryska, Ionna, Alisa, Nyura, Alina, Aneta, Myroslava, Marynia, Galyna, Nastunye, Nastasiya] 119 | Belarus: 120 | weight: 3 121 | names: [Alina, Elena, Ekaterina, Anya, Faina, Darya, Svetlana, Aleksandra, Natasha, Arina, Alisa, Natalia] 122 | Russia: 123 | weight: 5 124 | names: [Anastasia, Irina, Galina, Anya, Alina, Svetlana, Mila, Ekaterina, Angelina, Oksana, Olga, Nikita, Natasha, Arina, Kira, Sasha, Alisa, Nadia, Alla, Darya] 125 | Anglosphere: 126 | England: 127 | weight: 4 128 | names: [Ivy, Amelia, Emily, Alice, Hannah, Eleanor, Daisy, Phoebe, Evie, Florence, Ellie, Poppy, Rosie, Anne, Harriet, Jane, Rebecca, Mary, Alice, Elizabeth, Abigail, Holly, Lucy, Nancy] 129 | USA: 130 | weight: 5 131 | names: [Charlotte, Amelia, Emma, Abigail, Alice, Mia, Eleanor, Sarah, Evelyn, Olivia, Grace, Sophia, Emily, Zuri, Destiny, Amari, Nia, Michelle, Brianna, Ebony, 132 | Eshe, Candice, Tiana, Toni, Jasmine, Simone, Tiffany, Nala, Camila, Sofia, Elena, Valentina, Natalia, Lucia, Alaia, Ximena, Valeria, Catalina] 133 | Canada: 134 | weight: 3 135 | names: [Emily, Amelia, Sarah, Mila, Ellie, Mackenzie, Claire, Lily, Zoey, Evelyn, Avery, Abigail, Madison, Madeline, Everly, Ivy, Hailey, Addison] 136 | Australia: 137 | weight: 3 138 | names: [Charlotte, Amelia, Ava, Sienna, Matilda, Evie, Zara, Mackenzie, Aria, Violet, Florence, Ivy, Elsie, Layla, Frankie, Daisy, Sadie, Stella, Millie] 139 | New Zealand: 140 | weight: 1 141 | names: [Lilly, Lainie, Heide, Emme, Opal, Lacey, Livvie, Adalie, Sidonie, Dunja, Gitta, Kiani, Kenia, Rubia, Ophélie, Zäzilie, Zina, Marli, Meya] 142 | MENA: 143 | Israel: 144 | weight: 2 145 | hair: {blonde: 15, red: 10, brown: 35, black: 35, other: 5} 146 | names: [Ariel, Leah, Rachel, Esther, Naomi, Talia, Shira, Yael, Lia, Aviva, Moriah, Delilah, Romi] 147 | Lebanon: 148 | weight: 2 149 | names: [Nour, Aya, Alaa, Souad, Reewa, Ilham, Salwa, Sawsane, Fatme, Dina, Elza, Ghofran, Farah] 150 | Iran: 151 | weight: 4 152 | names: [Bahar, Fatemeh, Anahita, Fariba, Parisa, Shirin, Darya, Zahra, Maryam, Yasmin, Rana, Mahsa, Nasrin, Cyra, Haleh] 153 | Syria: 154 | weight: 2 155 | names: [Amena, Reem, Qamar, Haya, Ousa, Rosarita, Aischa, Hala, Uri, Rojda, Layal, Ranim, Souzan] 156 | Saudi Arabia: 157 | weight: 3 158 | names: [Fatima, Jamila, Amira, Hanifa, Farida, Halima, Farah, Malika, Nabila, Habiba, Maha, Karima, Khalida, Maryam, Tahira, Ghada, Nadia, Naila] 159 | Egypt: 160 | weight: 4 161 | names: [Jana, Amunet, Mariam, Salma, Jamila, Aya, Anippe, Halima, Jomana, Isis, Renenutet, Farida, Dalia, Dendera, Eshe, Amal, Yasmin] 162 | Algeria: 163 | weight: 3 164 | names: [Yasmine, Amina, Inčs, Amel, Meriem, Rania, Asma, Nesrine, Mina, Feriel, Anais, Ikram, Camelia, Chiraz, Dounia, Samira] 165 | Morocco: 166 | weight: 3 167 | names: [Amina, Fatima, Salma, Aziza, Faiza, Hajar, Ikram, Farah, Naima, Layla, Adil, Amal, Amira, Jamila, Marwa, Inès, Rania, Zineb] 168 | Sub-Saharan Africa: 169 | Nigeria: 170 | weight: 5 171 | names: [Tami, Remi, Sade, Ima, Toya, Tobi, Tomi, Tari, Ajah, Tona, Nneka, Dola, Chioma, Chimere, Zelie, Tonna, Timi, Amarachi, Chiamaka, Adanna, Chinyere] 172 | Ethiopia: 173 | weight: 4 174 | names: [Kayla, Aisha, Amara, Ayana, Zena, Sheba, Amira, Gabra, Jahzara, Makda, Nyala] 175 | Congo: 176 | weight: 4 177 | names: [Belvie, Mado, Rosine, Ruth, Malicia, Exaucée, Keicha, Grasnie, Benie, Chinelvie, Fiavina, Mireille, Aminata, Dayana, Peniel, Taime] 178 | Tanzania: 179 | weight: 3 180 | names: [Mila, Nia, Lulu, Malia, Imani, Asha, Zahra, Aida, Asma, Kali, Lela, Habiba, Rabia, Tisha, Shani, Khadija, Naima] 181 | South Africa: 182 | weight: 3 183 | hair: {blonde: 10, red: 5, brown: 30, black: 50, other: 5} 184 | names: [Melokuhle, Omphile, Iminathi, Lisakhanya, Lethabo, Amahle, Lesedi, Rethabile, Christal, Danelle, Alida, Marli, Gerrie, Mariette, Aletta, Leane, Lizanne, Lindie, Elize] 185 | Kenya: 186 | weight: 3 187 | names: [Mila, Imani, Cora, Nia, Kamaria, Nadia, Mumbi, Lulu, Zahra, Lela, Habiba, Shani, Khadija, Naima, Aluna, Kamaria, Zakiya] 188 | Uganda: 189 | weight: 3 190 | names: [Florence, Joan, Sarah, Alice, Tracy, Evelyn, Hanifah, Mariam, Charity, Martha, Angel, Catherine, Patricia, Blessing, Patience, Diana, Ritah] 191 | Sudan: 192 | weight: 3 193 | names: [Roaa, Ruba, Abrar, Felicia, Akong, Doaa, Esraa, Rayan, Nancy, Anila, Kate, Naelia] 194 | Angola: 195 | weight: 2 196 | names: [Ooli, Samara, Hannah, Jazmine, Leila, Hiki, Miranda, Rosine, Cynthia, Nattat, Lara, Christina, Abigail] 197 | Mozambique: 198 | weight: 2 199 | names: [Gaia, Gabrielle, Chelsea, Leila, Hedda, Hyacinta, Xiluva] 200 | Ghana: 201 | weight: 2 202 | names: [Beatrice, Sandra, Priscilla, Abigail, Angela, Esther, Barbara, Alberta, Doris, Linda, Cecilia, Blessing, Maame, Ruth, Patricia, Nancy, Eunice, Janet] 203 | Madagascar: 204 | weight: 2 205 | names: [Miora, Stephanie, Aina, Rotsy, Domoina, Cynthia, Raissa, Erica, Aliciah, Lili, Miantsa, Christelle, Linah, Tsiky, Hanitra] 206 | Cameroon: 207 | weight: 2 208 | names: [Joaddan, Armelle, Sandra, Esther, Daniella, Tracy, Rosy, Mylléna, Jade, Carine, Maeva, Tatiana] 209 | Latin America: 210 | Brazil: 211 | weight: 5 212 | names: [Maria, Fernanda, Ana, Gabriela, Aline, Francisca, Beatriz, Julia, Bruna, Antonia, Clara, Helena, Marcia, Flavia, Juliana, Patricia, Vitoria, Bianca] 213 | Mexico: 214 | weight: 4 215 | names: [Juana, Gabriela, Isabella, Elena, Alejandra, Lola, Ana, Lucia, Rosa, Veronica, Daniela, Fernanda, Valeria, Valentina, Carmen, Catalina, Leticia, Francisca, Mariana, Luna] 216 | Colombia: 217 | weight: 3 218 | names: [Isabella, Ana, Daniela, Mariana, Gabriela, Valentina, Luciana, Carmen, Adriana, Guadalupe, Antonia, Natalia, Lucia, Bianca, Clara, Lina, Rosa, Lola] 219 | Argentina: 220 | weight: 3 221 | names: [Camila, Agustina, Paula, Valentina, Ana, Julieta, Micaela, Martina, Daiana, Natalia, Carolina, Noelia, Lucia, Juana] 222 | Peru: 223 | weight: 3 224 | names: [Agata, Alejandra, Antonella, Arsenia, Beatriz, Claudia, Daniela, Dominga, Evita, Guadalupe, Irene, Karmina, Manuela, Maritza, Ofelia, Pilar, Roberta, Teresa, Valentina, Xaviera, Ximena] 225 | Venezuela: 226 | weight: 3 227 | names: [Gioia, Aymar, Consuelo, Mariángela, Gabriela, Vanessa, Selene, Susana, Paola, Federica, Mariam, Claudia, Carolina, Ligia] 228 | Chile: 229 | weight: 2 230 | names: [Isabella, Valentina, Laura, Catalina, Fernanda, Mila, Florencia, Julieta, Gabriela, Isidora, Martina, Josefa, Daniela, Antonia, Constanza, Agustina, Lucia] 231 | Ecuador: 232 | weight: 2 233 | names: [Ana, Gabriela, Maria, Camila, Carla, Elena, Graciela, Lucia, Giselle, Andrea, Daniela, Viviana, Juliana] 234 | Guatemala: 235 | weight: 2 236 | names: [Michelle, Andrea, Esmeralda, Rosa, Luz, Belen, Camila, Isabela, Angelita] 237 | Cuba: 238 | weight: 1 239 | names: [Adelgonda, Agata, Alegria, Alejandra, Angela, Arsenia, Beatriz, Bertalina, Daniela, Dominga, Evita, Georgina, Guadalupe, Havana, Ivelisse, Karmina, Manuela, Maritza, Pabla, Pilar, Roberta, Teresa, Ximena, Yamile] 240 | East Asia: 241 | China: 242 | weight: 5 243 | names: [Huan, Ling, Feng, Bao, Jie, Ping, Mei, Liu, Xia, Lian, Chun, Chang, Qing, Xiang, Xue, Qiao, Lei, Caihong, Ying, Jiayi, Lanying] 244 | Mongolia: 245 | weight: 1 246 | names: [Sunjidmaa, Minjinsor, Darimaa, Solongo, Yanjmaa, Ariunzaya, Khongorzul, Gankhuyagiin, Davaademberel, Batkhuyagiin, Batchimeg, Yanjaa, Ankhmaa, Namgar, Nominjin] 247 | South-Korea: 248 | weight: 2 249 | names: [Seo-yeon, Min-seo, Seo-hyeon, Su-bin, Yoo-jin, Min-ji, Seo-yeong, Ji-won, Soo-min, Ye-won, Mi-sun, Hyo-Sonn, Sun-Hee] 250 | Taiwan: 251 | weight: 1 252 | names: [Shu-fen, Shu-hui, Mei-ling, Ya-ting, Mei-hua, Li-hua, Shu-chen, Yi-chun, Tzu-hui, Xian-cha, Ching-min, Chun-yi, Jiau-hua, Ming-zhu, Pei-chien, Pei-yu, Shu-Fang, Xiu-xi] 253 | Hong Kong: 254 | weight: 1 255 | names: [Ellie, Avery, Charlotte, Renee, Cheryl, Evelyn, Hailey, Cynthia, Sylvia, Ally, Hannah, Chloe, Daphne, Aria, Riley, Emily, Mandy, Amber, Catherine, Natalie, Lily, Michelle] 256 | Japan: 257 | weight: 2 258 | names: [Aoi, Akari, Sakura, Ayaka, Akira, Aki, Hinata, Akane, Hikari, Keiko, Kei, Chiyo, Kaede, Ayame, Hanako, Ayako, Akiko, Chika, Ayumi, Yuki] 259 | Indonesia: 260 | weight: 2 261 | names: [Aulia, Budiwati, Bulan, Citra, Gendis, Indri, Kadek, Kemala, Komang, Lestari, Maharani, Melati, Mawar, Mayang, Ningrum, Ningsih, Permata, Pertiwi, Puspita, Putri, Ratih, Sinta, Siti, Wati, Yanti] 262 | Philippines: 263 | weight: 2 264 | names: [Joyce, Cindy, Ruby, Michelle, Serena, Jewel, Nancy, Alodia, Bianca, Chynna, Coleen, Diane, Isabel, Janine, Mariel, Nina, Rachel, Valerie] 265 | Vietnam: 266 | weight: 2 267 | names: [Anh, Bao, Chau, Chinh, Chi, Dao, Duong, Giang, Hoa, Huong, Khanh, Khuyen, Linh, Loan, Minh, Mai, Ngan, Nhung, Nguyet, Phuong, Quyen, Suong, Thanh, Thuy, Trinh, Vinh, Xinh, Xuyen] 268 | Thailand: 269 | weight: 2 270 | names: [Dara, Kamala, Mali, Han, Chariya, Arich, Anchali, Chanthira, Mani, Rune, Kanya, Khajee, Naiyana, Pakpao, Ying, Chalermwan, Chantana, Hansa, Maliwan] 271 | South Asia: 272 | India: 273 | weight: 5 274 | names: [Saanvi, Anika, Aditi, Prisha, Aarna, Divya, Parvati, Jiya, Riya, Aaradhya, Myra, Isha, Siya, Anaya, Anaisha, Jaya, Aarya, Ahana, Zara, Gayatri, Aarushi, Aarvi, Adya, Damini] 275 | Pakistan: 276 | weight: 2 277 | names: [Fatima, Inaya, Haniya, Maira, Zainab, Aafreen, Zoya, Neha, Aabish, Aaeedah, Eman, Aaminah, Aalia, Aamaal, Aamira, Aadila, Anabia, Anam, Sana] 278 | Bangladesh: 279 | weight: 2 280 | names: [Barsha, Farhan, Megh, Chandni, Rifah, Hridi, Labiba, Rafia, Disha, Muhaiminul, Abida, Oporajita, Sharmin, Fariha, Nawrin, Samreen, Anamika, Rafsan, Meghbalika, Sanjana, Ranya] 281 | Nepal: 282 | weight: 1 283 | names: [Dhanvi, Ditya, Gamya, Ehani, Chirasmi, Fatehjit, Binsa, Chaha, Bhavisana, Bilhana, Shanoli, Chirashree, Bhavaroopa, Kasmitha, Kopisha, Feba, Feshikha, Chaarumathi, Eenakshi, Chantin, Chimini, Baijanthi] 284 | Sri Lanka: 285 | weight: 1 286 | names: [Hiruni, Tharushi, Sachini, Bhagya, Nethmi, Janani, Sanduni, Thilini, Rashmi, Samadhi, Harshani, Upeksha, Nipuni, Dinithi, Chathu, Senuri, Hansani, Chalani, Shehara, Gayani] 287 | -------------------------------------------------------------------------------- /scripts/clonecleaner.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os 3 | import random 4 | import sys 5 | import yaml 6 | 7 | from modules import scripts, script_callbacks, shared, paths 8 | from modules.processing import Processed 9 | from modules.ui_components import FormRow, FormColumn, FormGroup, ToolButton 10 | from modules.ui import random_symbol, reuse_symbol, gr_show 11 | from modules.generation_parameters_copypaste import parse_generation_parameters 12 | from pprint import pprint 13 | 14 | def read_yaml(): 15 | promptfile = os.path.join(scripts.basedir(), "prompt_tree.yml") 16 | with open(promptfile, "r", encoding="utf8") as stream: 17 | prompt_tree = yaml.safe_load(stream) 18 | return prompt_tree 19 | 20 | def get_last_params(declone_seed, gallery_index): 21 | filename = os.path.join(paths.data_path, "params.txt") 22 | if os.path.exists(filename): 23 | with open(filename, "r", encoding="utf8") as file: 24 | prompt = file.read() 25 | 26 | if gallery_index > 0: 27 | gallery_index -= 1 28 | params = parse_generation_parameters(prompt) 29 | if params.get("CC_use_main_seed", "") == "True": 30 | return [int(float(params.get("Seed", "-0.0"))) + gallery_index, gr_show(False)] 31 | else: 32 | return [int(float(params.get("CC_declone_seed", "-0.0"))) + gallery_index, gr_show(False)] 33 | 34 | def sorted_difference(a, b): 35 | newlist = list(set(a).difference(b)) 36 | newlist.sort() 37 | return newlist 38 | 39 | class CloneCleanerScript(scripts.Script): 40 | prompt_tree = read_yaml() # maybe make this an instance property later 41 | 42 | def title(self): 43 | return "CloneCleaner" 44 | 45 | # show menu in either txt2img or img2img 46 | def show(self, is_img2img): 47 | return scripts.AlwaysVisible 48 | 49 | def ui(self, is_img2img): 50 | with gr.Accordion("CloneCleaner (beta!)", open=True): 51 | dummy_component = gr.Label(visible=False) 52 | regions = self.prompt_tree["country"].keys() 53 | hairlength = self.prompt_tree["hair"]["length"].keys() 54 | haircolor = self.prompt_tree["hair"]["color"].keys() 55 | with FormRow(): 56 | with FormColumn(min_width=160): 57 | is_enabled = gr.Checkbox(value=True, label="Enable CloneCleaner") 58 | with FormColumn(elem_id="CloneCleaner_gender"): 59 | gender = gr.Radio(["female", "male", "generic"], value="female", label="Male & generic not yet implemented.", elem_classes="ghosted") 60 | gender.style(container=False, item_container=False) 61 | with FormRow(elem_id="CloneCleaner_components"): 62 | components = ["name", "country", "hair length", "hair style", "hair color"] 63 | use_components = gr.CheckboxGroup(components, label="Use declone components", value=components) 64 | with FormRow(elem_id="CloneCleaner_midsection"): 65 | with FormGroup(): 66 | insert_start = gr.Checkbox(value=True, label="Put declone tokens at beginning of prompt") 67 | declone_weight = gr.Slider(minimum=0.0, maximum=2.0, step=0.05, value=1.0, label="Weight of declone tokens", elem_id="CloneCleaner_slider") 68 | with FormGroup(): 69 | use_main_seed = gr.Checkbox(value=True, label="Use main image seed for decloning") 70 | with FormRow(variant="compact", elem_id="CloneCleaner_seed_row", elem_classes="ghosted"): 71 | declone_seed = gr.Number(label='Declone seed', value=-1, elem_id="CloneCleaner_seed") 72 | declone_seed.style(container=False) 73 | random_seed = ToolButton(random_symbol, elem_id="CloneCleaner_random_seed", label='Random seed') 74 | reuse_seed = ToolButton(reuse_symbol, elem_id="CloneCleaner_reuse_seed", label='Reuse seed') 75 | with FormRow(elem_id="CloneCleaner_exclude_row") as exclude_row: 76 | exclude_regions = gr.Dropdown(choices=regions, label="Exclude regions", multiselect=True) 77 | exclude_hairlength = gr.Dropdown(choices=hairlength, label="Exclude hair lengths", multiselect=True) 78 | exclude_haircolor = gr.Dropdown(choices=haircolor, label="Exclude hair colors", multiselect=True) 79 | 80 | jstoggle = "() => {document.getElementById('CloneCleaner_seed_row').classList.toggle('ghosted')}" 81 | jsclickseed = "() => {setRandomSeed('CloneCleaner_seed')}" 82 | jsgetgalleryindex = "(x, y) => [x, selected_gallery_index()]" 83 | other_jstoggles = "() => {" + \ 84 | "const labels = document.getElementById('CloneCleaner_components').getElementsByTagName('label');" + \ 85 | "const excludelabels = document.getElementById('CloneCleaner_exclude_row').getElementsByTagName('label');" + \ 86 | "excludelabels[1].classList.toggle('ghosted', !labels[2].firstChild.checked);" + \ 87 | "excludelabels[2].classList.toggle('ghosted', !labels[4].firstChild.checked);" + \ 88 | "}" 89 | use_main_seed.change(fn=None, _js=jstoggle) 90 | random_seed.click(fn=None, _js=jsclickseed, show_progress=False, inputs=[], outputs=[]) 91 | reuse_seed.click(fn=get_last_params, _js=jsgetgalleryindex, show_progress=False, inputs=[declone_seed, dummy_component], outputs=[declone_seed, dummy_component]) 92 | use_components.change(fn=None, _js=other_jstoggles) 93 | 94 | def list_from_params_key(key, params): 95 | regionstring = params.get(key, "") 96 | regions = regionstring.split(",") if regionstring else [] 97 | return gr.update(value = regions) 98 | 99 | self.infotext_fields = [ 100 | (is_enabled, "CloneCleaner enabled"), 101 | (gender, "CC_gender"), 102 | (insert_start, "CC_insert_start"), 103 | (declone_weight, "CC_declone_weight"), 104 | (use_main_seed, "CC_use_main_seed"), 105 | (declone_seed, "CC_declone_seed"), 106 | (exclude_regions, lambda params:list_from_params_key("CC_exclude_regions", params)), 107 | (exclude_hairlength, lambda params:list_from_params_key("CC_exclude_hairlength", params)), 108 | (exclude_haircolor, lambda params:list_from_params_key("CC_exclude_haircolor", params)) 109 | ] 110 | return [is_enabled, gender, insert_start, declone_weight, use_main_seed, declone_seed, use_components, exclude_regions, exclude_hairlength, exclude_haircolor] 111 | 112 | def process(self, p, is_enabled, gender, insert_start, declone_weight, use_main_seed, declone_seed, use_components, exclude_regions, exclude_hairlength, exclude_haircolor): 113 | if not is_enabled: 114 | return 115 | 116 | if use_main_seed: 117 | declone_seed = p.all_seeds[0] 118 | elif declone_seed == -1: 119 | declone_seed = int(random.randrange(4294967294)) 120 | else: 121 | declone_seed = int(declone_seed) 122 | 123 | # original_prompt = p.all_prompts[0] 124 | # settings = f"gender={gender}, beginning={insert_start}, declone_weight={declone_weight}, main_seed={use_main_seed}, " + \ 125 | # f"declone_seed={declone_seed}, exclude_regions={exclude_regions}" 126 | p.extra_generation_params["CloneCleaner enabled"] = True 127 | p.extra_generation_params["CC_gender"] = gender 128 | p.extra_generation_params["CC_insert_start"] = insert_start 129 | p.extra_generation_params["CC_declone_weight"] = declone_weight 130 | p.extra_generation_params["CC_use_main_seed"] = use_main_seed 131 | p.extra_generation_params["CC_declone_seed"] = declone_seed 132 | if exclude_regions: 133 | p.extra_generation_params["CC_exclude_regions"] = ",".join(exclude_regions) 134 | if exclude_hairlength: 135 | p.extra_generation_params["CC_exclude_hairlength"] = ",".join(exclude_hairlength) 136 | if exclude_haircolor: 137 | p.extra_generation_params["CC_exclude_haircolor"] = ",".join(exclude_haircolor) 138 | 139 | countrytree = self.prompt_tree["country"] 140 | hairtree = self.prompt_tree["hair"] 141 | 142 | regions = sorted_difference(countrytree.keys(), exclude_regions) 143 | hairlengths = sorted_difference(hairtree["length"].keys(), exclude_hairlength) 144 | haircolors = sorted_difference(hairtree["color"].keys(), exclude_haircolor) 145 | 146 | use_name = "name" in use_components 147 | use_country = "country" in use_components 148 | use_length = "hair length" in use_components 149 | use_style = "hair style" in use_components 150 | use_color = "hair color" in use_components 151 | 152 | for i, prompt in enumerate(p.all_prompts): # for each image in batch 153 | rng = random.Random() 154 | seed = p.all_seeds[i] if use_main_seed else declone_seed + i 155 | rng.seed(seed) 156 | 157 | region = rng.choice(regions) 158 | countries = list(countrytree[region].keys()) 159 | countryweights = [countrytree[region][cty]["weight"] for cty in countries] 160 | country = rng.choices(countries, weights=countryweights)[0] 161 | 162 | countrydata = countrytree[region][country] 163 | hairdata = countrydata.get("hair", hairtree["defaultweight"][region]) 164 | maincolor = rng.choices(haircolors, weights=[hairdata[col] for col in haircolors])[0] 165 | color = rng.choice(hairtree["color"][maincolor]) 166 | mainlength = rng.choice(hairlengths) 167 | length = rng.choice(hairtree["length"][mainlength]) 168 | style = rng.choice(hairtree["style"][mainlength]) 169 | name = rng.choice(countrydata["names"]) 170 | 171 | inserted_prompt = "" 172 | 173 | if use_name or use_country: 174 | inserted_prompt += name if use_name else "person" 175 | inserted_prompt += " from " + country if use_country else "" 176 | 177 | if use_length or use_style or use_color: 178 | if inserted_prompt: 179 | inserted_prompt += ", " 180 | if use_length: 181 | inserted_prompt += length + " " 182 | if use_style: 183 | inserted_prompt += style + " " 184 | if use_color: 185 | inserted_prompt += color + " " 186 | inserted_prompt += "hair" 187 | 188 | if inserted_prompt: 189 | if declone_weight != 1: 190 | inserted_prompt = f"({inserted_prompt}:{declone_weight})" 191 | 192 | if insert_start: 193 | p.all_prompts[i] = inserted_prompt + ", " + prompt 194 | else: 195 | p.all_prompts[i] = prompt + ", " + inserted_prompt 196 | 197 | # def postprocess_batch(self, p, *args, **kwargs): 198 | # p.all_prompts[0] = p.prompt # gets saved in file metadata AND in batch file metadata 199 | 200 | # def process_batch(self, p, *args, **kwargs): 201 | # p.extra_generation_params["CC_TEST"] = "whatever" 202 | # p.all_prompts[0] = p.prompt + " SUFFIX" 203 | 204 | def postprocess(self, p, processed, *args): 205 | with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file: 206 | p.all_prompts[0] = p.prompt 207 | processed = Processed(p, [], p.seed, "") 208 | file.write(processed.infotext(p, 0)) 209 | 210 | # read with shared.opts.prompt_database_path 211 | def on_ui_settings(): 212 | info = shared.OptionInfo("prompt_tree.yml", "CloneCleaner prompt database path", section=("clonecleaner", "CloneCleaner")) 213 | shared.opts.add_option("prompt_database_path", info) 214 | # shared.opts.add_option("option1", shared.OptionInfo( 215 | # False, 216 | # "option1 description", 217 | # gr.Checkbox, 218 | # {"interactive": True}, 219 | # section=('template', "Template")) 220 | # ) 221 | 222 | 223 | script_callbacks.on_ui_settings(on_ui_settings) 224 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .ghosted { 2 | opacity: 0.5 !important; 3 | pointer-events: none !important; 4 | } 5 | 6 | /* #CloneCleaner_gender { 7 | padding: 0 0 10px 0 !important; 8 | } 9 | 10 | #CloneCleaner_gender > div > label { 11 | padding: var(--checkbox-label-padding) 0 !important; 12 | } */ 13 | 14 | #CloneCleaner_components { 15 | margin-top: -10px; 16 | } 17 | 18 | #CloneCleaner_slider { 19 | width: 280px; 20 | margin-top: 15px; 21 | } 22 | 23 | #CloneCleaner_seed{ 24 | min-width: min(80px,100%); 25 | } 26 | 27 | #CloneCleaner_midsection { 28 | margin-top: 10px; 29 | } 30 | 31 | #CloneCleaner_exclude_row { 32 | margin-top: 10px; 33 | } 34 | --------------------------------------------------------------------------------