jpfraneto commited on
Commit
a524e5f
1 Parent(s): f78e363

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +282 -3
README.md CHANGED
@@ -1,3 +1,282 @@
1
- ---
2
- license: mit
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ ---
4
+
5
+ This model was trained using the 8888 images of the [Anky Genesis NFT Collection](https://drive.google.com/drive/folders/1OBDQ08r8pLN4nfNf-48j87wzUEmF-ox4?usp=sharing), and its mission is to transform an image into pixel art, like so:
6
+
7
+ ![Anky Degen Pixel Example](https://github.com/jpfraneto/images/blob/main/ankydegenpixel.png?raw=true)
8
+
9
+ The code used for training it is the following:
10
+
11
+ ```
12
+ import os
13
+ import torch
14
+ import torch.nn as nn
15
+ import torch.optim as optim
16
+ from torch.utils.data import DataLoader, Dataset
17
+ from torchvision import transforms
18
+ from PIL import Image
19
+ import numpy as np
20
+
21
+ # Custom dataset for loading the images
22
+ class PixelArtDataset(Dataset):
23
+ def __init__(self, image_folder, transform=None):
24
+ self.image_folder = image_folder
25
+ self.transform = transform
26
+ self.image_files = [f"{i}.png" for i in range(1, 8889)]
27
+
28
+ # Debug: Check if images are correctly listed
29
+ print(f"Total images found: {len(self.image_files)}")
30
+
31
+ def __len__(self):
32
+ return len(self.image_files)
33
+
34
+ def __getitem__(self, idx):
35
+ img_path = os.path.join(self.image_folder, self.image_files[idx])
36
+ image = Image.open(img_path).convert("RGB")
37
+ if self.transform:
38
+ image = self.transform(image)
39
+ return image, image
40
+
41
+ # Define the neural network
42
+ class PixelArtGenerator(nn.Module):
43
+ def __init__(self):
44
+ super(PixelArtGenerator, self).__init__()
45
+ print("Initializing PixelArtGenerator Model...")
46
+ self.encoder = nn.Sequential(
47
+ nn.Conv2d(3, 64, kernel_size=4, stride=2, padding=1),
48
+ nn.ReLU(),
49
+ nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1),
50
+ nn.BatchNorm2d(128),
51
+ nn.ReLU(),
52
+ nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1),
53
+ nn.BatchNorm2d(256),
54
+ nn.ReLU()
55
+ )
56
+ self.decoder = nn.Sequential(
57
+ nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),
58
+ nn.BatchNorm2d(128),
59
+ nn.ReLU(),
60
+ nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),
61
+ nn.BatchNorm2d(64),
62
+ nn.ReLU(),
63
+ nn.ConvTranspose2d(64, 3, kernel_size=4, stride=2, padding=1),
64
+ nn.Tanh()
65
+ )
66
+
67
+ def forward(self, x):
68
+ x = self.encoder(x)
69
+ x = self.decoder(x)
70
+ return x
71
+
72
+ def train(model, dataloader, criterion, optimizer, device, epochs=50):
73
+ print("Starting training...")
74
+ model.train()
75
+ for epoch in range(epochs):
76
+ running_loss = 0.0
77
+ print(f"Epoch [{epoch+1}/{epochs}] starting...")
78
+ for batch_idx, (input_images, target_images) in enumerate(dataloader):
79
+ input_images, target_images = input_images.to(device), target_images.to(device)
80
+ optimizer.zero_grad()
81
+ outputs = model(input_images)
82
+ loss = criterion(outputs, target_images)
83
+ loss.backward()
84
+ optimizer.step()
85
+ running_loss += loss.item()
86
+
87
+ # Debug: Print progress for every batch
88
+ if batch_idx % 10 == 0:
89
+ print(f"Epoch [{epoch+1}/{epochs}], Batch [{batch_idx+1}/{len(dataloader)}], Loss: {loss.item():.4f}")
90
+
91
+ print(f"Epoch [{epoch+1}/{epochs}] completed with Loss: {running_loss/len(dataloader):.4f}")
92
+
93
+ def create_pixel_art(model, input_image_path, output_image_path, device):
94
+ print("Creating pixel art...")
95
+ model.eval()
96
+ transform = transforms.Compose([
97
+ transforms.Resize((64, 64)),
98
+ transforms.ToTensor(),
99
+ transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
100
+ ])
101
+ image = Image.open(input_image_path).convert("RGB")
102
+ input_image = transform(image).unsqueeze(0).to(device)
103
+ with torch.no_grad():
104
+ output_image = model(input_image).squeeze(0).cpu().numpy()
105
+ output_image = np.transpose(output_image, (1, 2, 0))
106
+ output_image = (output_image * 0.5 + 0.5) * 255.0
107
+ output_image = np.clip(output_image, 0, 255).astype(np.uint8)
108
+ output_image = Image.fromarray(output_image)
109
+ output_image.save(output_image_path)
110
+ print(f"Pixel art saved to {output_image_path}")
111
+
112
+ if __name__ == "__main__":
113
+ # Transform for input images
114
+ print("Setting up image transformations...")
115
+ transform = transforms.Compose([
116
+ transforms.Resize((64, 64)), # Resize to 64x64 for input
117
+ transforms.ToTensor(),
118
+ transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
119
+ ])
120
+
121
+ # Load dataset
122
+ print("Loading dataset...")
123
+ image_folder = "./" # Change this to your images folder path
124
+ dataset = PixelArtDataset(image_folder, transform)
125
+ dataloader = DataLoader(dataset, batch_size=8, shuffle=True) # Reduce batch size for debugging
126
+
127
+ # Check for GPU availability
128
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
129
+ print(f"Using device: {device}")
130
+
131
+ # Initialize the model, criterion, and optimizer
132
+ model = PixelArtGenerator().to(device)
133
+ criterion = nn.MSELoss()
134
+ optimizer = optim.Adam(model.parameters(), lr=0.0002)
135
+
136
+ # Enable data parallelism if multiple GPUs are available
137
+ if torch.cuda.device_count() > 1:
138
+ print(f"Using {torch.cuda.device_count()} GPUs")
139
+ model = nn.DataParallel(model)
140
+
141
+ # Train the model
142
+ train(model, dataloader, criterion, optimizer, device, epochs=50)
143
+
144
+ # Save the model
145
+ torch.save(model.state_dict(), "pixel_art_generator.pth")
146
+ print("Model saved as 'pixel_art_generator.pth'")
147
+
148
+ # Create pixel art from a new input image
149
+ input_image_path = "input_image.png" # Path to the high-resolution input image
150
+ output_image_path = "pixel_art.png" # Path to save the generated pixel art
151
+ create_pixel_art(model, input_image_path, output_image_path, device)
152
+ print("Pixel art creation completed.")
153
+ ```
154
+
155
+ The training happened on a Cognition PRO called poiesis. It consisted of 50 epochs, and it lasted for about 4 hours running on 2x NVIDIA RTX 4090.
156
+
157
+ Its intended usage is for it to transform any image into its corresponding in pixels, as you can see on this one.
158
+
159
+ For running it like such, you can run the following python code on the containing folder of the model (for transforming an image called pfp.png):
160
+
161
+ ```
162
+ import torch
163
+ import torch.nn as nn
164
+ from PIL import Image
165
+ import numpy as np
166
+ from torchvision import transforms
167
+ import os
168
+
169
+ # Define the neural network (same as the one used during training)
170
+ class PixelArtGenerator(nn.Module):
171
+ def __init__(self):
172
+ super(PixelArtGenerator, self).__init__()
173
+ print("Initializing PixelArtGenerator Model...")
174
+ self.encoder = nn.Sequential(
175
+ nn.Conv2d(3, 64, kernel_size=4, stride=2, padding=1),
176
+ nn.ReLU(),
177
+ nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1),
178
+ nn.BatchNorm2d(128),
179
+ nn.ReLU(),
180
+ nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1),
181
+ nn.BatchNorm2d(256),
182
+ nn.ReLU()
183
+ )
184
+ self.decoder = nn.Sequential(
185
+ nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1),
186
+ nn.BatchNorm2d(128),
187
+ nn.ReLU(),
188
+ nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),
189
+ nn.BatchNorm2d(64),
190
+ nn.ReLU(),
191
+ nn.ConvTranspose2d(64, 3, kernel_size=4, stride=2, padding=1),
192
+ nn.Tanh()
193
+ )
194
+
195
+ def forward(self, x):
196
+ x = self.encoder(x)
197
+ x = self.decoder(x)
198
+ return x
199
+
200
+ def create_pixel_art(model, input_image_path, output_image_path, device):
201
+ print(f"Creating pixel art for {input_image_path}...")
202
+
203
+ # Check if the input image file exists
204
+ if not os.path.isfile(input_image_path):
205
+ print(f"Error: Input image file '{input_image_path}' not found.")
206
+ return
207
+
208
+ model.eval()
209
+ print("Model set to evaluation mode.")
210
+
211
+ # Define the transformation for the input image
212
+ transform = transforms.Compose([
213
+ transforms.Resize((64, 64)),
214
+ transforms.ToTensor(),
215
+ transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
216
+ ])
217
+ print("Image transformation defined.")
218
+
219
+ # Load and preprocess the input image
220
+ image = Image.open(input_image_path).convert("RGB")
221
+ input_image = transform(image).unsqueeze(0).to(device)
222
+ print(f"Input image '{input_image_path}' loaded and preprocessed.")
223
+
224
+ # Generate pixel art using the model
225
+ with torch.no_grad():
226
+ output_image = model(input_image).squeeze(0).cpu().numpy()
227
+ print("Pixel art generated by the model.")
228
+
229
+ # Post-process and save the output image
230
+ output_image = np.transpose(output_image, (1, 2, 0))
231
+ output_image = (output_image * 0.5 + 0.5) * 255.0
232
+ output_image = np.clip(output_image, 0, 255).astype(np.uint8)
233
+ output_image = Image.fromarray(output_image)
234
+
235
+ # Scale up the image to iPhone 11 width (828 pixels)
236
+ scaled_output_image = output_image.resize((828, int(828 * output_image.size[1] / output_image.size[0])), Image.NEAREST)
237
+ scaled_output_image.save(output_image_path)
238
+ print(f"Pixel art saved to '{output_image_path}'.")
239
+
240
+ if __name__ == "__main__":
241
+ print("Starting pixel art generation script...")
242
+
243
+ # Load the trained model
244
+ model = PixelArtGenerator()
245
+ model_path = "pixel_art_generator.pth" # Path to the saved model
246
+ print(f"Loading model from '{model_path}'...")
247
+
248
+ # Load model with handling for DataParallel
249
+ state_dict = torch.load(model_path)
250
+ if 'module.' in list(state_dict.keys())[0]:
251
+ # Remove 'module.' prefix if model was saved with DataParallel
252
+ state_dict = {k.replace('module.', ''): v for k, v in state_dict.items()}
253
+ model.load_state_dict(state_dict)
254
+ print("Model loaded successfully.")
255
+
256
+ # Check for GPU availability
257
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
258
+ model.to(device)
259
+ print(f"Using device: {device}")
260
+
261
+ # Define the input and output paths for the single image
262
+ input_image_path = "pfp.jpeg" # Path to the input image
263
+ output_image_path = "pfp_pixelated.png" # Path to save the generated pixel art
264
+
265
+ # Create pixel art for the single image
266
+ create_pixel_art(model, input_image_path, output_image_path, device)
267
+
268
+ print("Pixel art creation completed for the single image.")
269
+
270
+ ```
271
+
272
+ Hope you enjoy, and any questions that you may have, feel free to reach out to @jpfraneto on telegram.
273
+
274
+ If you want to contribute to Anky, we have plenty of compute available, and a powerful story (and intention) that puts the unfolding of AI at the core of our experience as humans.
275
+
276
+ Think of it as a playground for your inner child, with boundless potential.
277
+
278
+ Our farcaster channel is here: https://warpcast.com/~/channel/anky
279
+
280
+ Your uniqueness is a gift.
281
+
282
+ 🎩