├── .gitignore ├── README.md ├── ScaleDown.java ├── bsplines.py ├── classes.py ├── convert-vid ├── generate-all-outputs ├── helpers.py ├── hull.py ├── img ├── bsplines │ ├── gaxe2_axbattler_01_bsplines.png │ ├── gaxe2_axbattler_02_bsplines.png │ ├── gaxe_skeleton_bsplines.png │ ├── icon_atari_bomb_bsplines.png │ ├── icon_disk_bsplines.png │ ├── invaders_01_bsplines.png │ ├── invaders_02_bsplines.png │ ├── invaders_03_bsplines.png │ ├── invaders_04_bsplines.png │ ├── invaders_05_bsplines.png │ ├── invaders_06_bsplines.png │ ├── mana_granpa_bsplines.png │ ├── mana_joch_bsplines.png │ ├── mana_rabite_bsplines.png │ ├── mana_randi_01_bsplines.png │ ├── mana_randi_02_bsplines.png │ ├── mana_salamando_bsplines.png │ ├── mana_sword_bsplines.png │ ├── sbm1_01_bsplines.png │ ├── sbm1_02_bsplines.png │ ├── sbm1_03_bsplines.png │ ├── sbm1_04_bsplines.png │ ├── sbm4_01_bsplines.png │ ├── sbm4_02_bsplines.png │ ├── sbm4_03_bsplines.png │ ├── sbm4_04_bsplines.png │ ├── sma_chest_bsplines.png │ ├── sma_peach_01_bsplines.png │ ├── sma_peach_02_bsplines.png │ ├── sma_toad_bsplines.png │ ├── smb_jump_bsplines.png │ ├── smw2_koopa_bsplines.png │ ├── smw2_yoshi_01_bsplines.png │ ├── smw2_yoshi_02_bsplines.png │ ├── smw_boo_bsplines.png │ ├── smw_bowser_bsplines.png │ ├── smw_cape_mario_yoshi_bsplines.png │ ├── smw_dolphin_bsplines.png │ ├── smw_help_bsplines.png │ ├── smw_mario_bsplines.png │ ├── smw_mario_yoshi_bsplines.png │ ├── smw_mushroom_bsplines.png │ ├── smw_yoshi_bsplines.png │ ├── vikings_baelog_bsplines.png │ ├── vikings_eric_bsplines.png │ ├── vikings_olaf_bsplines.png │ ├── vista_cursor_bsplines.png │ ├── win31_386_bsplines.png │ ├── win31_control_panel_bsplines.png │ ├── win31_cursor_bsplines.png │ ├── win31_fonts_bsplines.png │ ├── win31_keyboard_bsplines.png │ ├── win31_ports_bsplines.png │ └── win31_setup_bsplines.png ├── gaxe2_axbattler_01.png ├── gaxe2_axbattler_02.png ├── gaxe_skeleton.png ├── icon_atari_bomb.png ├── icon_disk.png ├── invaders_01.png ├── invaders_02.png ├── invaders_03.png ├── invaders_04.png ├── invaders_05.png ├── invaders_06.png ├── mana_granpa.png ├── mana_joch.png ├── mana_rabite.png ├── mana_randi_01.png ├── mana_randi_02.png ├── mana_salamando.png ├── mana_sword.png ├── sbm1_01.png ├── sbm1_02.png ├── sbm1_03.png ├── sbm1_04.png ├── sbm4_01.png ├── sbm4_02.png ├── sbm4_03.png ├── sbm4_04.png ├── similarity │ ├── gaxe2_axbattler_01_similarity.png │ ├── gaxe2_axbattler_02_similarity.png │ ├── gaxe_skeleton_similarity.png │ ├── icon_atari_bomb_similarity.png │ ├── icon_disk_similarity.png │ ├── invaders_01_similarity.png │ ├── invaders_02_similarity.png │ ├── invaders_03_similarity.png │ ├── invaders_04_similarity.png │ ├── invaders_05_similarity.png │ ├── invaders_06_similarity.png │ ├── mana_granpa_similarity.png │ ├── mana_joch_similarity.png │ ├── mana_rabite_similarity.png │ ├── mana_randi_01_similarity.png │ ├── mana_randi_02_similarity.png │ ├── mana_salamando_similarity.png │ ├── mana_sword_similarity.png │ ├── sbm1_01_similarity.png │ ├── sbm1_02_similarity.png │ ├── sbm1_03_similarity.png │ ├── sbm1_04_similarity.png │ ├── sbm4_01_similarity.png │ ├── sbm4_02_similarity.png │ ├── sbm4_03_similarity.png │ ├── sbm4_04_similarity.png │ ├── sma_chest_similarity.png │ ├── sma_peach_01_similarity.png │ ├── sma_peach_02_similarity.png │ ├── sma_toad_similarity.png │ ├── smb_jump_similarity.png │ ├── smw2_koopa_similarity.png │ ├── smw2_yoshi_01_similarity.png │ ├── smw2_yoshi_02_similarity.png │ ├── smw_boo_similarity.png │ ├── smw_bowser_similarity.png │ ├── smw_cape_mario_yoshi_similarity.png │ ├── smw_dolphin_similarity.png │ ├── smw_help_similarity.png │ ├── smw_mario_similarity.png │ ├── smw_mario_yoshi_similarity.png │ ├── smw_mushroom_similarity.png │ ├── smw_yoshi_similarity.png │ ├── vikings_baelog_similarity.png │ ├── vikings_eric_similarity.png │ ├── vikings_olaf_similarity.png │ ├── vista_cursor_similarity.png │ ├── win31_386_similarity.png │ ├── win31_control_panel_similarity.png │ ├── win31_cursor_similarity.png │ ├── win31_fonts_similarity.png │ ├── win31_keyboard_similarity.png │ ├── win31_ports_similarity.png │ └── win31_setup_similarity.png ├── sma_chest.png ├── sma_peach_01.png ├── sma_peach_02.png ├── sma_toad.png ├── smb_jump.png ├── smw2_koopa.png ├── smw2_yoshi_01.png ├── smw2_yoshi_02.png ├── smw_boo.png ├── smw_bowser.png ├── smw_cape_mario_yoshi.png ├── smw_dolphin.png ├── smw_help.png ├── smw_mario.png ├── smw_mario_yoshi.png ├── smw_mushroom.png ├── smw_yoshi.png ├── vedges │ ├── gaxe2_axbattler_01_vedges.png │ ├── gaxe2_axbattler_02_vedges.png │ ├── gaxe_skeleton_vedges.png │ ├── icon_atari_bomb_vedges.png │ ├── icon_disk_vedges.png │ ├── invaders_01_vedges.png │ ├── invaders_02_vedges.png │ ├── invaders_03_vedges.png │ ├── invaders_04_vedges.png │ ├── invaders_05_vedges.png │ ├── invaders_06_vedges.png │ ├── mana_granpa_vedges.png │ ├── mana_joch_vedges.png │ ├── mana_rabite_vedges.png │ ├── mana_randi_01_vedges.png │ ├── mana_randi_02_vedges.png │ ├── mana_salamando_vedges.png │ ├── mana_sword_vedges.png │ ├── sbm1_01_vedges.png │ ├── sbm1_02_vedges.png │ ├── sbm1_03_vedges.png │ ├── sbm1_04_vedges.png │ ├── sbm4_01_vedges.png │ ├── sbm4_02_vedges.png │ ├── sbm4_03_vedges.png │ ├── sbm4_04_vedges.png │ ├── sma_chest_vedges.png │ ├── sma_peach_01_vedges.png │ ├── sma_peach_02_vedges.png │ ├── sma_toad_vedges.png │ ├── smb_jump_vedges.png │ ├── smw2_koopa_vedges.png │ ├── smw2_yoshi_01_vedges.png │ ├── smw2_yoshi_02_vedges.png │ ├── smw_boo_vedges.png │ ├── smw_bowser_vedges.png │ ├── smw_cape_mario_yoshi_vedges.png │ ├── smw_dolphin_vedges.png │ ├── smw_help_vedges.png │ ├── smw_mario_vedges.png │ ├── smw_mario_yoshi_vedges.png │ ├── smw_mushroom_vedges.png │ ├── smw_yoshi_vedges.png │ ├── vikings_baelog_vedges.png │ ├── vikings_eric_vedges.png │ ├── vikings_olaf_vedges.png │ ├── vista_cursor_vedges.png │ ├── win31_386_vedges.png │ ├── win31_control_panel_vedges.png │ ├── win31_cursor_vedges.png │ ├── win31_fonts_vedges.png │ ├── win31_keyboard_vedges.png │ ├── win31_ports_vedges.png │ └── win31_setup_vedges.png ├── vikings_baelog.png ├── vikings_eric.png ├── vikings_olaf.png ├── vista_cursor.png ├── voronoi │ ├── gaxe2_axbattler_01_voronoi.png │ ├── gaxe2_axbattler_02_voronoi.png │ ├── gaxe_skeleton_voronoi.png │ ├── icon_atari_bomb_voronoi.png │ ├── icon_disk_voronoi.png │ ├── invaders_01_voronoi.png │ ├── invaders_02_voronoi.png │ ├── invaders_03_voronoi.png │ ├── invaders_04_voronoi.png │ ├── invaders_05_voronoi.png │ ├── invaders_06_voronoi.png │ ├── mana_granpa_voronoi.png │ ├── mana_joch_voronoi.png │ ├── mana_rabite_voronoi.png │ ├── mana_randi_01_voronoi.png │ ├── mana_randi_02_voronoi.png │ ├── mana_salamando_voronoi.png │ ├── mana_sword_voronoi.png │ ├── sbm1_01_voronoi.png │ ├── sbm1_02_voronoi.png │ ├── sbm1_03_voronoi.png │ ├── sbm1_04_voronoi.png │ ├── sbm4_01_voronoi.png │ ├── sbm4_02_voronoi.png │ ├── sbm4_03_voronoi.png │ ├── sbm4_04_voronoi.png │ ├── sma_chest_voronoi.png │ ├── sma_peach_01_voronoi.png │ ├── sma_peach_02_voronoi.png │ ├── sma_toad_voronoi.png │ ├── smb_jump_voronoi.png │ ├── smw2_koopa_voronoi.png │ ├── smw2_yoshi_01_voronoi.png │ ├── smw2_yoshi_02_voronoi.png │ ├── smw_boo_voronoi.png │ ├── smw_bowser_voronoi.png │ ├── smw_cape_mario_yoshi_voronoi.png │ ├── smw_dolphin_voronoi.png │ ├── smw_help_voronoi.png │ ├── smw_mario_voronoi.png │ ├── smw_mario_yoshi_voronoi.png │ ├── smw_mushroom_voronoi.png │ ├── smw_yoshi_voronoi.png │ ├── vikings_baelog_voronoi.png │ ├── vikings_eric_voronoi.png │ ├── vikings_olaf_voronoi.png │ ├── vista_cursor_voronoi.png │ ├── win31_386_voronoi.png │ ├── win31_control_panel_voronoi.png │ ├── win31_cursor_voronoi.png │ ├── win31_fonts_voronoi.png │ ├── win31_keyboard_voronoi.png │ ├── win31_ports_voronoi.png │ └── win31_setup_voronoi.png ├── win31_386.png ├── win31_control_panel.png ├── win31_cursor.png ├── win31_fonts.png ├── win31_keyboard.png ├── win31_ports.png └── win31_setup.png ├── main.py ├── notes ├── 114688_2.pdf ├── 8x8.png ├── Depixelizing pixelArt.pdf ├── bsplines-outline ├── cython.txt ├── inputs.txt ├── notespixel.txt ├── pixel.pdf ├── smw_boo_nearest_16x.png ├── spline_logic └── voronoi_logic ├── resize-frames ├── run-all ├── scrape-images.py ├── so.py └── tex ├── proposal ├── Makefile ├── corner.png ├── islands.png ├── jagged.png └── proposal.tex └── report ├── Makefile └── report.tex /.gitignore: -------------------------------------------------------------------------------- 1 | dep_old/ 2 | img_manual/ 3 | .DS_Store 4 | *.c 5 | *.o 6 | *.so 7 | *.pyc 8 | curvey-master/ 9 | *.bak 10 | vid/ 11 | *.class 12 | *.log 13 | *.aux 14 | *.out 15 | tex/proposal/proposal.pdf 16 | tex/report/report.pdf 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About 2 | ----- 3 | 4 | Digital Image Processing course project. 5 | 6 | References 7 | ---------- 8 | 9 | http://research.microsoft.com/en-us/um/people/kopf/pixelart/index.html 10 | http://www.ucsp.edu.pe/sibgrapi2013/eproceedings/technical/114688_2.pdf 11 | 12 | Using pypy 13 | ---------- 14 | 15 | virtualenv -p `which pypy` depixelizing 16 | cd depixelizing 17 | sudo bin/pip install Pillow 18 | echo 'from PIL import Image' > pypy-pil-test.py 19 | bin/pypy pypy-pil-test.py 20 | 21 | Using `pypy` as above, we observed a speed *drop* on the incomplete codebase. 22 | -------------------------------------------------------------------------------- /ScaleDown.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import javax.imageio.ImageIO; 3 | import java.io.IOException; 4 | import java.awt.image.BufferedImage; 5 | 6 | public class ScaleDown { 7 | public static void main(String[] args) { 8 | try { 9 | int scalingFactor = Integer.parseInt(args[1]); 10 | int scalingFactorSqr = scalingFactor*scalingFactor; 11 | BufferedImage input = ImageIO.read(new File(args[0])); 12 | int height = input.getHeight(); 13 | int width = input.getWidth(); 14 | BufferedImage output = new BufferedImage(height/scalingFactor, width/scalingFactor, input.getType()); 15 | for (int i = 0; i < height; i += scalingFactor) { 16 | for (int j = 0; j < width; j += scalingFactor) { 17 | int red = 0, green = 0, blue = 0; 18 | for (int ii = i; ii < i+scalingFactor; ii++) { 19 | for (int jj = j; jj < j+scalingFactor; jj++) { 20 | int color = input.getRGB(jj, ii); 21 | red += (color & 0x00ff0000) >> 16; 22 | green += (color & 0x0000ff00) >> 8; 23 | blue += (color & 0x000000ff); 24 | } 25 | } 26 | red /= scalingFactorSqr; 27 | green /= scalingFactorSqr; 28 | blue /= scalingFactorSqr; 29 | int x = j/scalingFactor; 30 | int y = i/scalingFactor; 31 | output.setRGB(x, y, (red << 16) + (green << 8) + blue); 32 | } 33 | } 34 | ImageIO.write(output, "png", new File(args[2])); 35 | } 36 | catch (IOException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /bsplines.py: -------------------------------------------------------------------------------- 1 | # credit - this code is a modified version of http://stackoverflow.com/a/24693358 2 | 3 | from numpy import array, linspace 4 | import matplotlib.pyplot as plt 5 | import scipy.interpolate as si 6 | 7 | def bspline(pts, degree, smoothness): 8 | # cycle check 9 | periodic = False 10 | if pts[0] == pts[-1]: 11 | # pts.pop() 12 | periodic = True 13 | pts = pts[:-1] 14 | 15 | if periodic: pts = pts + pts[0 : degree+1] 16 | else: pts = [pts[0]] + pts + [pts[-1],pts[-1]] 17 | 18 | pts = array(pts) 19 | n_points = len(pts) 20 | x, y = pts[:,0], pts[:,1] 21 | 22 | t = range(len(x)) 23 | ipl_t = linspace(1.0, len(pts) - degree, smoothness) 24 | 25 | x_tup = si.splrep(t, x, k=degree, per=periodic) 26 | y_tup = si.splrep(t, y, k=degree, per=periodic) 27 | x_list = list(x_tup) 28 | xl = x.tolist() 29 | 30 | y_list = list(y_tup) 31 | yl = y.tolist() 32 | 33 | if periodic: 34 | x_list[1] = [0.0] + xl + [0.0, 0.0, 0.0, 0.0] 35 | y_list[1] = [0.0] + yl + [0.0, 0.0, 0.0, 0.0] 36 | 37 | x_i = si.splev(ipl_t, x_list) 38 | y_i = si.splev(ipl_t, y_list) 39 | 40 | return zip(x_i, y_i) -------------------------------------------------------------------------------- /classes.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | def __init__(self, image, x, y, rgb): 3 | self.image = image 4 | self.x = x 5 | self.y = y 6 | self.neighbours = set([]) 7 | self.rgb = rgb 8 | # ALL voronoi cell points 9 | # we take the convex hull of these to get 10 | # the actual voronoi cell points 11 | self.vor_pts = [] 12 | 13 | # connect two nodes 14 | def make_conn(self, n): 15 | if n is not None: 16 | self.neighbours.add(n) 17 | n.neighbours.add(self) 18 | 19 | def remove_conn(self, n): 20 | if n is not None: 21 | self.neighbours.remove(n) 22 | n.neighbours.remove(self) 23 | 24 | def get_xy(self): 25 | return (self.x, self.y) 26 | 27 | def print_neighbours(self): 28 | print [ne.get_xy() for ne in self.neighbours] 29 | 30 | def __eq__(self, other): 31 | return self.get_xy() == other.get_xy() 32 | 33 | def __repr__(self): 34 | return str(self.get_xy()) 35 | 36 | class Point(object): 37 | def __init__(self, x, y): 38 | self.x = x 39 | self.y = y 40 | # nodes whose voronoi cells contain this point as a vertex 41 | # in other words, the set of nodes that 'own' this point 42 | self.nodes = set([]) 43 | # visible edges that this point is a part of 44 | self.vedges = set([]) 45 | # neighbouring points dict 46 | # this maps an owner node to the set of neighbours the 47 | # point has with respect to that node 48 | self.neighbours = {} 49 | 50 | def add_node(self, n): 51 | self.nodes.append(n) 52 | 53 | def add_vedge(self, ve): 54 | self.vedges.add(ve) 55 | 56 | def get_xy(self): 57 | return (self.x, self.y) 58 | 59 | def __eq__(self, other): 60 | return self.get_xy() == other.get_xy() 61 | 62 | def __repr__(self): 63 | return str(self.get_xy()) 64 | 65 | def all_neighbours(self): 66 | return set.union(*[self.neighbours[n] for n in self.nodes]) 67 | 68 | class VisibleEdge(object): 69 | def __init__(self, points=[]): 70 | # list of points comprising the visible edge 71 | self.points = points 72 | self.bspline = None 73 | 74 | def get_endpoints(self): 75 | return (self.points[0], self.points[-1]) 76 | 77 | def __getitem__(self, key): 78 | return self.points[key] -------------------------------------------------------------------------------- /convert-vid: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # `avconv -i vid/4x.mp4 -r 30 vid/extracted/still_%04d.png` 4 | Dir.glob('vid/resized/*.png') do |image_name| 5 | output_name = image_name.gsub('resized/', 'voronoi/') 6 | puts "Saving #{output_name}" 7 | `python main.py --render voronoi --image #{image_name} --save #{output_name} --scale 4` 8 | end -------------------------------------------------------------------------------- /generate-all-outputs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | Dir.glob('img/*.png') do |image_name| 4 | %w[similarity voronoi vedges bsplines].each do |render_stage| 5 | output_name = image_name.gsub('img/', "img/#{render_stage}/").gsub('.png', "_#{render_stage}.png") 6 | puts "Saving #{output_name}" 7 | `python main.py --render #{render_stage} --image #{image_name} --save #{output_name}` 8 | end 9 | end -------------------------------------------------------------------------------- /helpers.py: -------------------------------------------------------------------------------- 1 | import math, random 2 | 3 | from OpenGL.GL import * 4 | from OpenGL.GLUT.freeglut import * 5 | from OpenGL.GL.framebufferobjects import * 6 | from OpenGL.GLUT import * 7 | from OpenGL.GLU import * 8 | 9 | from hull import * 10 | 11 | def process_keyboard_input(key, x, y): 12 | global window_id 13 | if key == chr(27): 14 | glutLeaveMainLoop() 15 | 16 | def init_scene(w, h, scale): 17 | glClearColor(1.0, 1.0, 1.0, 1.0) 18 | glColor3f(0.0, 0.0, 0.0) 19 | glMatrixMode(GL_PROJECTION) 20 | glLoadIdentity() 21 | gluOrtho2D(0, scale*w, 0, scale*h) 22 | glPointSize(3) 23 | 24 | def set_random_color(): 25 | glColor3ub(random.randint(0,255), random.randint(0,255), random.randint(0,255)) 26 | 27 | def draw_pixel_centres(w, h, im, nodes, scale): 28 | for x in xrange(w): 29 | for y in xrange(h): 30 | draw_pixel_centre(x, h-y-1, im, nodes, scale) 31 | 32 | def draw_pixel_centre(x, y, im, nodes, scale): 33 | _, h = im.size 34 | r, g, b = get_node(x, h-y-1, im, nodes).rgb 35 | glColor3ub(r, g, b) 36 | perturb = 0.075 37 | 38 | pts = [(scale*(x+0.5-perturb), scale*(y+0.5-perturb)), 39 | (scale*(x+0.5-perturb), scale*(y+0.5+perturb)), 40 | (scale*(x+0.5+perturb), scale*(y+0.5+perturb)), 41 | (scale*(x+0.5+perturb), scale*(y+0.5-perturb))] 42 | 43 | # fill 44 | glBegin(GL_POLYGON) 45 | for x, y in pts: 46 | glVertex2f(x, y) 47 | glEnd() 48 | 49 | # stroke 50 | glColor3ub(255-r, 255-g, 255-b) 51 | glBegin(GL_LINE_LOOP) 52 | for x, y in pts: 53 | glVertex2f(x, y) 54 | glEnd() 55 | 56 | def get_node(x, y, im, nodes): 57 | w, h = im.size 58 | if x < 0 or y < 0 or x >= w or y >= h: 59 | return None 60 | index = x + y * w 61 | return nodes[index] 62 | 63 | # convert rgb to yuv 64 | def rgb2yuv(r,g,b): 65 | r1 = r / 255.0 66 | g1 = g / 255.0 67 | b1 = b / 255.0 68 | y = (0.299 * r1) + (0.587 * g1) + (0.114 * b1) 69 | u = 0.492 * (b1 - y) 70 | v = 0.877 * (r1 - y) 71 | return (y, u, v) 72 | 73 | # compare YUV values of two pixels, return 74 | # True if they are different, else False 75 | def pixels_are_dissimilar(rgb1, rgb2): 76 | r1, g1, b1 = rgb1 77 | r2, g2, b2 = rgb2 78 | y1, u1, v1 = rgb2yuv(r1, g1, b1) 79 | y2, u2, v2 = rgb2yuv(r2, g2, b2) 80 | ydiff = abs(y1 - y2) > 48.0/255 81 | udiff = abs(u1 - u2) > 7.0/255 82 | vdiff = abs(v1 - v2) > 6.0/255 83 | return ydiff or udiff or vdiff 84 | 85 | def is_shading_edge(rgb1, rgb2): 86 | y1, u1, v1 = rgb2yuv(*rgb1) 87 | y2, u2, v2 = rgb2yuv(*rgb2) 88 | dist = (y1 - y2)**2 +(u1 - u2)**2 + (v1 - v2)**2 89 | return (dist <= (float(100)/255)**2) 90 | 91 | def is_contour_edge(pt1, pt2): 92 | intersection = pt1.nodes & pt2.nodes 93 | if len(intersection) == 1: 94 | return True 95 | else: 96 | node1, node2 = intersection 97 | return not is_shading_edge(node1.rgb, node2.rgb) 98 | 99 | def angle(o, a, b): 100 | def d(l, m): return (l.x - m.x)**2 + (l.y - m.y)**2 101 | P12, P23, P13 = d(o, a), d(a, b), d(o, b) 102 | angle = math.acos((P12 + P13 - P23) / (2 * math.sqrt(P12 * P13))) 103 | return math.degrees(angle) # converted from radians 104 | 105 | # pt1 and pt2 are two polygon vertices in the simplified voronoi 106 | # diagram this function checks if the reshaped pixels corresponding 107 | # to the two polygons on either side of the edge joining pt1 to pt2 108 | # are different enough for the edge to be classified as visible 109 | def polygons_are_dissimilar(pt1, pt2): 110 | # get the pixels associated with both points and take the intersection of the two sets 111 | # this either has size 2 or size 1 112 | # the second case happens when the two polygon points are on the boundary 113 | # in this case, the edge is trivially not a visible edge since there is no 114 | # need to draw b-splines along the boundary 115 | # note that here, by "visible edge" we mean a single-length visible edge 116 | # whereas elsewhere, we use it to mean "a sequence of nodes separating 2 regions" 117 | intersection = pt1.nodes & pt2.nodes 118 | if len(intersection) == 1: 119 | return True 120 | else: 121 | assert len(intersection) == 2 122 | node1, node2 = intersection 123 | return pixels_are_dissimilar(node1.rgb, node2.rgb) 124 | 125 | def process_command_line_arg(argname, necessary=False, needs_arg=True, missing_error=''): 126 | # print argname, needs_arg 127 | if argname not in sys.argv: 128 | if necessary: 129 | sys.stderr.write(missing_error + '\n') 130 | exit(1) 131 | else: 132 | return None 133 | index = sys.argv.index(argname) 134 | if needs_arg: 135 | if len(sys.argv) < index + 2: 136 | sys.stderr.write(argname + ' needs an argument\n') 137 | exit(1) 138 | else: 139 | return sys.argv[index+1] 140 | else: 141 | return True 142 | 143 | def color_pixels_bsplines(im ,scale, nodes): 144 | w, h = im.size 145 | count = 0 146 | # glBegin(GL_POINTS) 147 | for x in xrange(w*scale): 148 | for y in xrange(h*scale): 149 | n = get_node(x//scale, y//scale, im, nodes) 150 | # who does this pixel belong to? 151 | candidates = [ne for ne in n.neighbours] 152 | candidates.append(n) 153 | result = None 154 | for node in candidates: 155 | if is_inside((x, y), node.vor_pts, scale): 156 | result = node 157 | break 158 | if result is None: 159 | print (x, y), [c.vor_pts for c in candidates] 160 | count += 1 161 | # continue 162 | # node_x, node_y = result.get_xy() 163 | # r, g, b = get_node(node_x, node_y, im, nodes).rgb 164 | # glColor3ub(r, g, b) 165 | # glVertex2f(x, h-y) 166 | print count 167 | # glEnd() -------------------------------------------------------------------------------- /hull.py: -------------------------------------------------------------------------------- 1 | # find the convex hull of a bunch of points represented as 2-tuples 2 | # using andrew's monotone chain algorithm 3 | # http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain 4 | # does not remove collinear points if we convert <= to < 5 | def convex_hull(points, keep_collinear=True): 6 | points = sorted(set(points)) 7 | 8 | if len(points) <= 1: 9 | return points 10 | 11 | def cross(o, a, b): 12 | return - (a[0] - o[0]) * (b[1] - o[1]) + (a[1] - o[1]) * (b[0] - o[0]) 13 | 14 | # Build lower hull 15 | lower = [] 16 | for p in points: 17 | if keep_collinear: 18 | while len(lower) >= 2 and cross(lower[-2], lower[-1], p) < 0: 19 | lower.pop() 20 | else: 21 | while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0: 22 | lower.pop() 23 | lower.append(p) 24 | 25 | # Build upper hull 26 | upper = [] 27 | for p in reversed(points): 28 | if keep_collinear: 29 | while len(upper) >= 2 and cross(upper[-2], upper[-1], p) < 0: 30 | upper.pop() 31 | else: 32 | while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0: 33 | upper.pop() 34 | upper.append(p) 35 | 36 | # Concatenation of the lower and upper hulls gives the convex hull. 37 | # Last point of each list is omitted because it is repeated at the beginning of the other list. 38 | return lower[:-1] + upper[:-1] 39 | 40 | # find the convex hull of a bunch of points represented as 2-tuples 41 | # we use the Jarvis march: http://en.wikipedia.org/wiki/Gift_wrapping_algorithm 42 | # may remove collinear points, not used here 43 | def jarvis_march(pts): 44 | if len(pts) == 0: 45 | return [] 46 | 47 | result = [] 48 | 49 | # first, find the leftmost point 50 | point_on_hull = sorted(pts, key=lambda x: x[0])[0] 51 | 52 | endpoint = None 53 | # note: python copies tuples. no need to worry about references here 54 | while True: 55 | result.append(point_on_hull) 56 | endpoint = pts[0] 57 | for j in xrange(1, len(pts)): 58 | if endpoint == point_on_hull or is_to_the_left(pts[j], result[-1], endpoint): 59 | endpoint = pts[j] 60 | point_on_hull = endpoint 61 | if endpoint == result[0]: 62 | break 63 | 64 | return result 65 | 66 | # is a to the left of the line from b to c as seen from b? 67 | # http://kukuruku.co/hub/algorithms/a-point-localization-in-a-polygon 68 | # note: the PIL system is left-handed, so the > must be replaced by a < 69 | # OpenGL on the other hand is right-handed 70 | def is_to_the_left(a, b, c): 71 | bc = (c[0] - b[0], c[1] - b[1]) # vector from b to c 72 | ca = (a[0] - c[0], a[1] - c[1]) # vector from c to a 73 | return bc[0]*ca[1] - bc[1]*ca[0] < 0 74 | 75 | # 'pix' is a pixel with coordinates (x, y) 76 | # where 0 <= x < w * scale 77 | # and 0 <= y < h * scale 78 | # and w = width of the input image 79 | # and h = height of the input image 80 | # and scale = upscaling factor 81 | # this function returns true if the pixel 'pix' 82 | # is inside 'cvh', which is a convex hull 83 | def is_inside(pix, cvh, scale): 84 | # simple hack: 85 | # 1. downscale pix 86 | # 2. append it to the collinear-triple-less convex hull of n 87 | # (assume this is precomputed) 88 | # 3. compute the convex hull of this new list 89 | # 4. return is_it_the_same_as_before 90 | pts = [i for i in cvh] # deep copy of n.vor_pts 91 | old = set(remove_all_collinear(pts)) 92 | new_pt = (float(pix[0])/scale, float(pix[1])/scale) 93 | pts.append(new_pt) 94 | new = set(convex_hull(pts, False)) 95 | return new == old 96 | 97 | # returns true if point objects p1 p2 and p3 are collinear 98 | def is_straight_line(p1, p2, p3): 99 | a, b, c = p1.get_xy(), p2.get_xy(), p3.get_xy() 100 | return is_straight_line_tuples(a, b, c) 101 | 102 | # returns true if tuples a b and c are collinear 103 | def is_straight_line_tuples(a, b, c): 104 | if a[1] == b[1]: return b[1] == c[1] 105 | if c[1] == b[1]: return b[1] == a[1] 106 | slope1 = float(a[0]-b[0]) / float(a[1]-b[1]) 107 | slope2 = float(c[0]-b[0]) / float(c[1]-b[1]) 108 | return slope1 == slope2 109 | 110 | # given a convex hull, returns a new convex hull without any collinear points 111 | def remove_all_collinear(pts): 112 | length = len(pts) 113 | to_remove = set() 114 | for i in xrange(len(pts)): 115 | pt1 = pts[i] 116 | pt2 = pts[(i+1) % length] 117 | pt3 = pts[(i+2) % length] 118 | if is_straight_line_tuples(pt1, pt2, pt3) and pt2 not in to_remove: 119 | to_remove.add(pt2) 120 | return [pt for pt in pts if pt not in to_remove] 121 | 122 | def test_is_inside(): 123 | c = convex_hull([(0,0), (1,0), (1,1), (1,-1), (2,0), (2,1)]) 124 | assert is_inside((3.8, 2.2), c, 2) is False 125 | assert is_inside((1.9, 1.1), c, 1) is False 126 | assert is_inside((1, 1.1), c, 1) is False 127 | c = convex_hull([(15.75, 10.25), (16.0, 11.0), (17.0, 11.0), (16.75, 10.25), (16.25, 9.75)]) 128 | assert is_inside((67, 40), c, 4) is False 129 | 130 | if __name__ == '__main__': 131 | test_is_inside() -------------------------------------------------------------------------------- /img/bsplines/gaxe2_axbattler_01_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/gaxe2_axbattler_01_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/gaxe2_axbattler_02_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/gaxe2_axbattler_02_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/gaxe_skeleton_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/gaxe_skeleton_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/icon_atari_bomb_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/icon_atari_bomb_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/icon_disk_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/icon_disk_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/invaders_01_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/invaders_01_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/invaders_02_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/invaders_02_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/invaders_03_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/invaders_03_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/invaders_04_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/invaders_04_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/invaders_05_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/invaders_05_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/invaders_06_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/invaders_06_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/mana_granpa_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/mana_granpa_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/mana_joch_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/mana_joch_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/mana_rabite_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/mana_rabite_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/mana_randi_01_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/mana_randi_01_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/mana_randi_02_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/mana_randi_02_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/mana_salamando_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/mana_salamando_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/mana_sword_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/mana_sword_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm1_01_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm1_01_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm1_02_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm1_02_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm1_03_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm1_03_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm1_04_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm1_04_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm4_01_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm4_01_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm4_02_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm4_02_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm4_03_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm4_03_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sbm4_04_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sbm4_04_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sma_chest_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sma_chest_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sma_peach_01_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sma_peach_01_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sma_peach_02_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sma_peach_02_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/sma_toad_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/sma_toad_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smb_jump_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smb_jump_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw2_koopa_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw2_koopa_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw2_yoshi_01_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw2_yoshi_01_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw2_yoshi_02_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw2_yoshi_02_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_boo_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_boo_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_bowser_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_bowser_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_cape_mario_yoshi_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_cape_mario_yoshi_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_dolphin_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_dolphin_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_help_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_help_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_mario_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_mario_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_mario_yoshi_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_mario_yoshi_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_mushroom_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_mushroom_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/smw_yoshi_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/smw_yoshi_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/vikings_baelog_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/vikings_baelog_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/vikings_eric_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/vikings_eric_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/vikings_olaf_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/vikings_olaf_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/vista_cursor_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/vista_cursor_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/win31_386_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/win31_386_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/win31_control_panel_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/win31_control_panel_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/win31_cursor_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/win31_cursor_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/win31_fonts_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/win31_fonts_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/win31_keyboard_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/win31_keyboard_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/win31_ports_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/win31_ports_bsplines.png -------------------------------------------------------------------------------- /img/bsplines/win31_setup_bsplines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/bsplines/win31_setup_bsplines.png -------------------------------------------------------------------------------- /img/gaxe2_axbattler_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/gaxe2_axbattler_01.png -------------------------------------------------------------------------------- /img/gaxe2_axbattler_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/gaxe2_axbattler_02.png -------------------------------------------------------------------------------- /img/gaxe_skeleton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/gaxe_skeleton.png -------------------------------------------------------------------------------- /img/icon_atari_bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/icon_atari_bomb.png -------------------------------------------------------------------------------- /img/icon_disk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/icon_disk.png -------------------------------------------------------------------------------- /img/invaders_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/invaders_01.png -------------------------------------------------------------------------------- /img/invaders_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/invaders_02.png -------------------------------------------------------------------------------- /img/invaders_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/invaders_03.png -------------------------------------------------------------------------------- /img/invaders_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/invaders_04.png -------------------------------------------------------------------------------- /img/invaders_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/invaders_05.png -------------------------------------------------------------------------------- /img/invaders_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/invaders_06.png -------------------------------------------------------------------------------- /img/mana_granpa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/mana_granpa.png -------------------------------------------------------------------------------- /img/mana_joch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/mana_joch.png -------------------------------------------------------------------------------- /img/mana_rabite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/mana_rabite.png -------------------------------------------------------------------------------- /img/mana_randi_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/mana_randi_01.png -------------------------------------------------------------------------------- /img/mana_randi_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/mana_randi_02.png -------------------------------------------------------------------------------- /img/mana_salamando.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/mana_salamando.png -------------------------------------------------------------------------------- /img/mana_sword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/mana_sword.png -------------------------------------------------------------------------------- /img/sbm1_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm1_01.png -------------------------------------------------------------------------------- /img/sbm1_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm1_02.png -------------------------------------------------------------------------------- /img/sbm1_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm1_03.png -------------------------------------------------------------------------------- /img/sbm1_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm1_04.png -------------------------------------------------------------------------------- /img/sbm4_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm4_01.png -------------------------------------------------------------------------------- /img/sbm4_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm4_02.png -------------------------------------------------------------------------------- /img/sbm4_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm4_03.png -------------------------------------------------------------------------------- /img/sbm4_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sbm4_04.png -------------------------------------------------------------------------------- /img/similarity/gaxe2_axbattler_01_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/gaxe2_axbattler_01_similarity.png -------------------------------------------------------------------------------- /img/similarity/gaxe2_axbattler_02_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/gaxe2_axbattler_02_similarity.png -------------------------------------------------------------------------------- /img/similarity/gaxe_skeleton_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/gaxe_skeleton_similarity.png -------------------------------------------------------------------------------- /img/similarity/icon_atari_bomb_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/icon_atari_bomb_similarity.png -------------------------------------------------------------------------------- /img/similarity/icon_disk_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/icon_disk_similarity.png -------------------------------------------------------------------------------- /img/similarity/invaders_01_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/invaders_01_similarity.png -------------------------------------------------------------------------------- /img/similarity/invaders_02_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/invaders_02_similarity.png -------------------------------------------------------------------------------- /img/similarity/invaders_03_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/invaders_03_similarity.png -------------------------------------------------------------------------------- /img/similarity/invaders_04_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/invaders_04_similarity.png -------------------------------------------------------------------------------- /img/similarity/invaders_05_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/invaders_05_similarity.png -------------------------------------------------------------------------------- /img/similarity/invaders_06_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/invaders_06_similarity.png -------------------------------------------------------------------------------- /img/similarity/mana_granpa_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/mana_granpa_similarity.png -------------------------------------------------------------------------------- /img/similarity/mana_joch_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/mana_joch_similarity.png -------------------------------------------------------------------------------- /img/similarity/mana_rabite_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/mana_rabite_similarity.png -------------------------------------------------------------------------------- /img/similarity/mana_randi_01_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/mana_randi_01_similarity.png -------------------------------------------------------------------------------- /img/similarity/mana_randi_02_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/mana_randi_02_similarity.png -------------------------------------------------------------------------------- /img/similarity/mana_salamando_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/mana_salamando_similarity.png -------------------------------------------------------------------------------- /img/similarity/mana_sword_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/mana_sword_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm1_01_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm1_01_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm1_02_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm1_02_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm1_03_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm1_03_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm1_04_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm1_04_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm4_01_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm4_01_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm4_02_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm4_02_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm4_03_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm4_03_similarity.png -------------------------------------------------------------------------------- /img/similarity/sbm4_04_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sbm4_04_similarity.png -------------------------------------------------------------------------------- /img/similarity/sma_chest_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sma_chest_similarity.png -------------------------------------------------------------------------------- /img/similarity/sma_peach_01_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sma_peach_01_similarity.png -------------------------------------------------------------------------------- /img/similarity/sma_peach_02_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sma_peach_02_similarity.png -------------------------------------------------------------------------------- /img/similarity/sma_toad_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/sma_toad_similarity.png -------------------------------------------------------------------------------- /img/similarity/smb_jump_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smb_jump_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw2_koopa_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw2_koopa_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw2_yoshi_01_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw2_yoshi_01_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw2_yoshi_02_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw2_yoshi_02_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_boo_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_boo_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_bowser_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_bowser_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_cape_mario_yoshi_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_cape_mario_yoshi_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_dolphin_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_dolphin_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_help_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_help_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_mario_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_mario_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_mario_yoshi_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_mario_yoshi_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_mushroom_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_mushroom_similarity.png -------------------------------------------------------------------------------- /img/similarity/smw_yoshi_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/smw_yoshi_similarity.png -------------------------------------------------------------------------------- /img/similarity/vikings_baelog_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/vikings_baelog_similarity.png -------------------------------------------------------------------------------- /img/similarity/vikings_eric_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/vikings_eric_similarity.png -------------------------------------------------------------------------------- /img/similarity/vikings_olaf_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/vikings_olaf_similarity.png -------------------------------------------------------------------------------- /img/similarity/vista_cursor_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/vista_cursor_similarity.png -------------------------------------------------------------------------------- /img/similarity/win31_386_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/win31_386_similarity.png -------------------------------------------------------------------------------- /img/similarity/win31_control_panel_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/win31_control_panel_similarity.png -------------------------------------------------------------------------------- /img/similarity/win31_cursor_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/win31_cursor_similarity.png -------------------------------------------------------------------------------- /img/similarity/win31_fonts_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/win31_fonts_similarity.png -------------------------------------------------------------------------------- /img/similarity/win31_keyboard_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/win31_keyboard_similarity.png -------------------------------------------------------------------------------- /img/similarity/win31_ports_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/win31_ports_similarity.png -------------------------------------------------------------------------------- /img/similarity/win31_setup_similarity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/similarity/win31_setup_similarity.png -------------------------------------------------------------------------------- /img/sma_chest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sma_chest.png -------------------------------------------------------------------------------- /img/sma_peach_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sma_peach_01.png -------------------------------------------------------------------------------- /img/sma_peach_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sma_peach_02.png -------------------------------------------------------------------------------- /img/sma_toad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/sma_toad.png -------------------------------------------------------------------------------- /img/smb_jump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smb_jump.png -------------------------------------------------------------------------------- /img/smw2_koopa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw2_koopa.png -------------------------------------------------------------------------------- /img/smw2_yoshi_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw2_yoshi_01.png -------------------------------------------------------------------------------- /img/smw2_yoshi_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw2_yoshi_02.png -------------------------------------------------------------------------------- /img/smw_boo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_boo.png -------------------------------------------------------------------------------- /img/smw_bowser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_bowser.png -------------------------------------------------------------------------------- /img/smw_cape_mario_yoshi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_cape_mario_yoshi.png -------------------------------------------------------------------------------- /img/smw_dolphin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_dolphin.png -------------------------------------------------------------------------------- /img/smw_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_help.png -------------------------------------------------------------------------------- /img/smw_mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_mario.png -------------------------------------------------------------------------------- /img/smw_mario_yoshi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_mario_yoshi.png -------------------------------------------------------------------------------- /img/smw_mushroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_mushroom.png -------------------------------------------------------------------------------- /img/smw_yoshi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/smw_yoshi.png -------------------------------------------------------------------------------- /img/vedges/gaxe2_axbattler_01_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/gaxe2_axbattler_01_vedges.png -------------------------------------------------------------------------------- /img/vedges/gaxe2_axbattler_02_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/gaxe2_axbattler_02_vedges.png -------------------------------------------------------------------------------- /img/vedges/gaxe_skeleton_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/gaxe_skeleton_vedges.png -------------------------------------------------------------------------------- /img/vedges/icon_atari_bomb_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/icon_atari_bomb_vedges.png -------------------------------------------------------------------------------- /img/vedges/icon_disk_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/icon_disk_vedges.png -------------------------------------------------------------------------------- /img/vedges/invaders_01_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/invaders_01_vedges.png -------------------------------------------------------------------------------- /img/vedges/invaders_02_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/invaders_02_vedges.png -------------------------------------------------------------------------------- /img/vedges/invaders_03_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/invaders_03_vedges.png -------------------------------------------------------------------------------- /img/vedges/invaders_04_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/invaders_04_vedges.png -------------------------------------------------------------------------------- /img/vedges/invaders_05_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/invaders_05_vedges.png -------------------------------------------------------------------------------- /img/vedges/invaders_06_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/invaders_06_vedges.png -------------------------------------------------------------------------------- /img/vedges/mana_granpa_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/mana_granpa_vedges.png -------------------------------------------------------------------------------- /img/vedges/mana_joch_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/mana_joch_vedges.png -------------------------------------------------------------------------------- /img/vedges/mana_rabite_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/mana_rabite_vedges.png -------------------------------------------------------------------------------- /img/vedges/mana_randi_01_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/mana_randi_01_vedges.png -------------------------------------------------------------------------------- /img/vedges/mana_randi_02_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/mana_randi_02_vedges.png -------------------------------------------------------------------------------- /img/vedges/mana_salamando_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/mana_salamando_vedges.png -------------------------------------------------------------------------------- /img/vedges/mana_sword_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/mana_sword_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm1_01_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm1_01_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm1_02_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm1_02_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm1_03_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm1_03_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm1_04_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm1_04_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm4_01_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm4_01_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm4_02_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm4_02_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm4_03_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm4_03_vedges.png -------------------------------------------------------------------------------- /img/vedges/sbm4_04_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sbm4_04_vedges.png -------------------------------------------------------------------------------- /img/vedges/sma_chest_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sma_chest_vedges.png -------------------------------------------------------------------------------- /img/vedges/sma_peach_01_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sma_peach_01_vedges.png -------------------------------------------------------------------------------- /img/vedges/sma_peach_02_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sma_peach_02_vedges.png -------------------------------------------------------------------------------- /img/vedges/sma_toad_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/sma_toad_vedges.png -------------------------------------------------------------------------------- /img/vedges/smb_jump_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smb_jump_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw2_koopa_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw2_koopa_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw2_yoshi_01_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw2_yoshi_01_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw2_yoshi_02_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw2_yoshi_02_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_boo_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_boo_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_bowser_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_bowser_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_cape_mario_yoshi_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_cape_mario_yoshi_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_dolphin_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_dolphin_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_help_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_help_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_mario_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_mario_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_mario_yoshi_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_mario_yoshi_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_mushroom_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_mushroom_vedges.png -------------------------------------------------------------------------------- /img/vedges/smw_yoshi_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/smw_yoshi_vedges.png -------------------------------------------------------------------------------- /img/vedges/vikings_baelog_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/vikings_baelog_vedges.png -------------------------------------------------------------------------------- /img/vedges/vikings_eric_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/vikings_eric_vedges.png -------------------------------------------------------------------------------- /img/vedges/vikings_olaf_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/vikings_olaf_vedges.png -------------------------------------------------------------------------------- /img/vedges/vista_cursor_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/vista_cursor_vedges.png -------------------------------------------------------------------------------- /img/vedges/win31_386_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/win31_386_vedges.png -------------------------------------------------------------------------------- /img/vedges/win31_control_panel_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/win31_control_panel_vedges.png -------------------------------------------------------------------------------- /img/vedges/win31_cursor_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/win31_cursor_vedges.png -------------------------------------------------------------------------------- /img/vedges/win31_fonts_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/win31_fonts_vedges.png -------------------------------------------------------------------------------- /img/vedges/win31_keyboard_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/win31_keyboard_vedges.png -------------------------------------------------------------------------------- /img/vedges/win31_ports_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/win31_ports_vedges.png -------------------------------------------------------------------------------- /img/vedges/win31_setup_vedges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vedges/win31_setup_vedges.png -------------------------------------------------------------------------------- /img/vikings_baelog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vikings_baelog.png -------------------------------------------------------------------------------- /img/vikings_eric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vikings_eric.png -------------------------------------------------------------------------------- /img/vikings_olaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vikings_olaf.png -------------------------------------------------------------------------------- /img/vista_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/vista_cursor.png -------------------------------------------------------------------------------- /img/voronoi/gaxe2_axbattler_01_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/gaxe2_axbattler_01_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/gaxe2_axbattler_02_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/gaxe2_axbattler_02_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/gaxe_skeleton_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/gaxe_skeleton_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/icon_atari_bomb_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/icon_atari_bomb_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/icon_disk_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/icon_disk_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/invaders_01_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/invaders_01_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/invaders_02_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/invaders_02_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/invaders_03_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/invaders_03_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/invaders_04_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/invaders_04_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/invaders_05_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/invaders_05_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/invaders_06_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/invaders_06_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/mana_granpa_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/mana_granpa_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/mana_joch_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/mana_joch_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/mana_rabite_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/mana_rabite_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/mana_randi_01_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/mana_randi_01_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/mana_randi_02_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/mana_randi_02_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/mana_salamando_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/mana_salamando_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/mana_sword_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/mana_sword_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm1_01_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm1_01_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm1_02_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm1_02_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm1_03_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm1_03_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm1_04_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm1_04_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm4_01_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm4_01_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm4_02_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm4_02_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm4_03_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm4_03_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sbm4_04_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sbm4_04_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sma_chest_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sma_chest_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sma_peach_01_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sma_peach_01_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sma_peach_02_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sma_peach_02_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/sma_toad_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/sma_toad_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smb_jump_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smb_jump_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw2_koopa_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw2_koopa_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw2_yoshi_01_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw2_yoshi_01_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw2_yoshi_02_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw2_yoshi_02_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_boo_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_boo_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_bowser_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_bowser_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_cape_mario_yoshi_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_cape_mario_yoshi_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_dolphin_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_dolphin_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_help_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_help_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_mario_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_mario_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_mario_yoshi_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_mario_yoshi_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_mushroom_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_mushroom_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/smw_yoshi_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/smw_yoshi_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/vikings_baelog_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/vikings_baelog_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/vikings_eric_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/vikings_eric_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/vikings_olaf_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/vikings_olaf_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/vista_cursor_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/vista_cursor_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/win31_386_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/win31_386_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/win31_control_panel_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/win31_control_panel_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/win31_cursor_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/win31_cursor_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/win31_fonts_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/win31_fonts_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/win31_keyboard_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/win31_keyboard_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/win31_ports_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/win31_ports_voronoi.png -------------------------------------------------------------------------------- /img/voronoi/win31_setup_voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/voronoi/win31_setup_voronoi.png -------------------------------------------------------------------------------- /img/win31_386.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/win31_386.png -------------------------------------------------------------------------------- /img/win31_control_panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/win31_control_panel.png -------------------------------------------------------------------------------- /img/win31_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/win31_cursor.png -------------------------------------------------------------------------------- /img/win31_fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/win31_fonts.png -------------------------------------------------------------------------------- /img/win31_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/win31_keyboard.png -------------------------------------------------------------------------------- /img/win31_ports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/win31_ports.png -------------------------------------------------------------------------------- /img/win31_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/img/win31_setup.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # usage: python main.py --render voronoi --image input_file.png --tests --centres --scale 1 --save output_file.png 4 | # optional arguments: --tests, --centres, --scale, --save 5 | 6 | import sys 7 | 8 | from classes import * 9 | from bsplines import * 10 | from helpers import * 11 | 12 | if sys.platform == "darwin": 13 | from PIL import Image 14 | else: 15 | import Image 16 | 17 | SCREEN_WIDTH = 800 18 | SCREEN_HEIGHT = 600 19 | 20 | '''rendering code''' 21 | 22 | # http://www.de-brauwer.be/wiki/wikka.php?wakka=PyOpenGLSierpinski 23 | window_id = -1 24 | 25 | # a global variable used for debugging 26 | # stores a list of point coordinate pairs 27 | point_list = [] 28 | 29 | def display_original(): 30 | global im, opengl_buffer 31 | w, h = im.size 32 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 33 | for x in xrange(w): 34 | for y in xrange(h): 35 | r, g, b = get_node(x, y, im, nodes).rgb 36 | y = h - y - 1 37 | glColor3ub(r, g, b) 38 | glBegin(GL_QUADS) 39 | glVertex2f(IMAGE_SCALE*x, IMAGE_SCALE*y) 40 | glVertex2f(IMAGE_SCALE*(x+1), IMAGE_SCALE*y) 41 | glVertex2f(IMAGE_SCALE*(x+1), IMAGE_SCALE*(y+1)) 42 | glVertex2f(IMAGE_SCALE*x, IMAGE_SCALE*(y+1)) 43 | glEnd() 44 | glReadPixels(0, 0, w*IMAGE_SCALE, h*IMAGE_SCALE, GL_RGBA, GL_UNSIGNED_BYTE, opengl_buffer) 45 | # glFlush() 46 | glutSwapBuffers() 47 | glutLeaveMainLoop() 48 | 49 | def display_voronoi(): 50 | global im, opengl_buffer, nodes 51 | w, h = im.size 52 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 53 | for x in xrange(w): 54 | for y in xrange(h): 55 | n = get_node(x, y, im, nodes) 56 | r, g, b = n.rgb 57 | glColor3ub(r, g, b) 58 | if '--lines' in sys.argv: 59 | glBegin(GL_LINE_LOOP) 60 | else: 61 | glBegin(GL_POLYGON) 62 | for pt in n.vor_pts: 63 | x_pt, y_pt = pt 64 | y_pt = h - y_pt 65 | glVertex2f(IMAGE_SCALE*x_pt, IMAGE_SCALE*y_pt) 66 | glEnd() 67 | if '--centres' in sys.argv: 68 | draw_pixel_centres(w, h, im, nodes, IMAGE_SCALE) 69 | display_point_list() 70 | glReadPixels(0, 0, w*IMAGE_SCALE, h*IMAGE_SCALE, GL_RGBA, GL_UNSIGNED_BYTE, opengl_buffer) 71 | # glFlush() 72 | glutSwapBuffers() 73 | glutLeaveMainLoop() 74 | 75 | def display_visible_edges(): 76 | global im, vedges, opengl_buffer, nodes 77 | w, h = im.size 78 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 79 | for v in vedges: 80 | set_random_color() 81 | glBegin(GL_LINE_STRIP) 82 | for p in v.points: 83 | x, y = p.get_xy() 84 | glVertex2f(IMAGE_SCALE*x, IMAGE_SCALE*(h-y)) 85 | glEnd() 86 | if '--centres' in sys.argv: 87 | draw_pixel_centres(w, h, im, nodes, IMAGE_SCALE) 88 | display_point_list() 89 | glReadPixels(0, 0, w*IMAGE_SCALE, h*IMAGE_SCALE, GL_RGBA, GL_UNSIGNED_BYTE, opengl_buffer) 90 | # glFlush() 91 | glutSwapBuffers() 92 | glutLeaveMainLoop() 93 | 94 | def display_point_list(): 95 | global point_list 96 | glColor3ub(0, 0, 255) 97 | glBegin(GL_POINTS) 98 | for p in point_list: 99 | x, y = p.get_xy() 100 | glVertex2f(IMAGE_SCALE*x, IMAGE_SCALE*(h-y)) 101 | glEnd() 102 | 103 | def display_similarity(): 104 | global im, opengl_buffer 105 | w, h = im.size 106 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 107 | glColor3ub(0, 0, 0) 108 | glBegin(GL_LINES) 109 | for x in xrange(w): 110 | for y in xrange(h): 111 | n = get_node(x, y, im, nodes) 112 | for ne in n.neighbours: 113 | nx, ny = n.get_xy() 114 | nex, ney = ne.get_xy() 115 | ny, ney = h-ny-1, h-ney-1 116 | glVertex2f(IMAGE_SCALE*(nx+0.5), IMAGE_SCALE*(ny+0.5)) 117 | glVertex2f(IMAGE_SCALE*(nex+0.5), IMAGE_SCALE*(ney+0.5)) 118 | glEnd() 119 | glReadPixels(0, 0, w*IMAGE_SCALE, h*IMAGE_SCALE, GL_RGBA, GL_UNSIGNED_BYTE, opengl_buffer) 120 | # glFlush() 121 | glutSwapBuffers() 122 | glutLeaveMainLoop() 123 | 124 | def display_bsplines(): 125 | global im, vedges, nodes 126 | w, h = im.size 127 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 128 | glColor3ub(0, 0, 0) 129 | for v in vedges: 130 | # set_random_color() 131 | glBegin(GL_LINE_STRIP) 132 | for x,y in v.bspline: 133 | y = h-y 134 | glVertex2f(IMAGE_SCALE*x, IMAGE_SCALE*y) 135 | glEnd() 136 | display_point_list() 137 | # color_pixels_bsplines(im, IMAGE_SCALE, nodes) 138 | glReadPixels(0, 0, w*IMAGE_SCALE, h*IMAGE_SCALE, GL_RGBA, GL_UNSIGNED_BYTE, opengl_buffer) 139 | # glFlush() 140 | glutSwapBuffers() 141 | glutLeaveMainLoop() 142 | 143 | def display_optimized(): 144 | pass 145 | 146 | def render(render_stage): 147 | global window_id, im, imagename 148 | w, h = im.size 149 | glutInit() 150 | glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS) 151 | glutInitWindowSize(w * IMAGE_SCALE, h * IMAGE_SCALE) 152 | window_id = glutCreateWindow(render_stage + ' - ' + imagename) 153 | glutHideWindow() 154 | glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_ALPHA) 155 | if render_stage == 'original': 156 | glutDisplayFunc(display_original) 157 | elif render_stage == 'similarity': 158 | glutDisplayFunc(display_similarity) 159 | elif render_stage == 'voronoi': 160 | glutDisplayFunc(display_voronoi) 161 | elif render_stage == 'vedges': 162 | glutDisplayFunc(display_visible_edges) 163 | elif render_stage == 'bsplines': 164 | glutDisplayFunc(display_bsplines) 165 | elif render_stage == 'optimized': 166 | glutDisplayFunc(display_optimized) 167 | else: 168 | glutDisplayFunc(display_original) 169 | glutKeyboardFunc(process_keyboard_input) 170 | init_scene(w, h, IMAGE_SCALE) 171 | glutMainLoop() 172 | 173 | ''' rendering over''' 174 | 175 | imagename = process_command_line_arg('--image', True, True, 'need an image to convert') 176 | 177 | im = Image.open(imagename) 178 | w, h = im.size 179 | 180 | # obtain the scale factor from on the image dimensions 181 | IMAGE_SCALE = process_command_line_arg('--scale', necessary=False, needs_arg=True) 182 | if IMAGE_SCALE is None: 183 | max_scale = min(SCREEN_WIDTH // w, SCREEN_HEIGHT // h) 184 | # find the largest multiple of 4 <= this value 185 | IMAGE_SCALE = (max_scale >> 2) << 2 if max_scale > 3 else max_scale 186 | else: 187 | IMAGE_SCALE = int(IMAGE_SCALE) 188 | 189 | opengl_buffer = (GLubyte*(4*w*h*IMAGE_SCALE*IMAGE_SCALE))(0) 190 | 191 | # credit - http://stackoverflow.com/a/4122290 192 | # and http://pyopengl.sourceforge.net/context/tutorials/shadow_2.html 193 | def save(pathname): 194 | global w, h, opengl_buffer 195 | 196 | image = Image.fromstring(mode='RGBA', size=(IMAGE_SCALE*w, IMAGE_SCALE*h), data=opengl_buffer) 197 | image = image.transpose(Image.FLIP_TOP_BOTTOM) 198 | image.save(pathname) 199 | 200 | return 201 | 202 | fbo = glGenFrameBuffers(1) 203 | glBindFrameBuffer(GL_FRAMEBUFFER, fbo) 204 | 205 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0) 206 | 207 | glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, opengl_buffer) 208 | image = Image.fromstring(mode='RGB', size=(w, h), data=opengl_buffer) 209 | image = image.transpose(Image.FLIP_TOP_BOTTOM) 210 | image.save(pathname) 211 | 212 | # declare the buffer that we will need to save the image to disk 213 | # if the --save command-line arg is passed 214 | 215 | nodes = [] 216 | 217 | # create nodes 218 | for row in xrange(h): 219 | for col in xrange(w): 220 | n = Node(image=im, x=col, y=row, rgb=im.getpixel((col, row))) 221 | nodes.append(n) 222 | 223 | # initialize similarity graph 224 | for row in xrange(h): 225 | for col in xrange(w): 226 | n = get_node(col, row, im, nodes) 227 | for x in [-1,0,1]: 228 | for y in [-1,0,1]: 229 | if x != 0 or y != 0: 230 | neighbour = get_node(col+x, row+y, im, nodes) 231 | n.make_conn(neighbour) 232 | 233 | def test_node_corresponds_to_image(im): 234 | for x in xrange(col): 235 | for y in xrange(row): 236 | assert get_node(x,y,im, nodes).rgb == im.getpixel((x,y)) 237 | 238 | def test_neighbours_are_mutual(im): 239 | for x in xrange(col): 240 | for y in xrange(row): 241 | n = get_node(x,y,im, nodes) 242 | for ne in n.neighbours: 243 | assert n in ne.neighbours 244 | 245 | def test_number_of_neighbours_is_correct(im): 246 | w, h = im.size 247 | # corner nodes have 3 neighbours 248 | assert len(get_node(0, 0, im, nodes).neighbours) == 3 249 | assert len(get_node(w-1, 0, im, nodes).neighbours) == 3 250 | assert len(get_node(0, h-1, im, nodes).neighbours) == 3 251 | assert len(get_node(w-1, h-1, im, nodes).neighbours) == 3 252 | # border nodes have 5 neighbours 253 | for x in xrange(1, w-1): 254 | assert len(get_node(x, 0, im, nodes).neighbours) == 5 255 | assert len(get_node(x, h-1, im, nodes).neighbours) == 5 256 | for y in xrange(1, h-1): 257 | assert len(get_node(0, y, im, nodes).neighbours) == 5 258 | assert len(get_node(w-1, y, im, nodes).neighbours) == 5 259 | # interior nodes have 8 neighbours 260 | for x in xrange(1, w-1): 261 | for y in xrange(1, h-1): 262 | assert len(get_node(x, y, im, nodes).neighbours) == 8 263 | 264 | if '--tests' in sys.argv: 265 | test_node_corresponds_to_image(im) 266 | test_number_of_neighbours_is_correct(im) 267 | test_neighbours_are_mutual(im) 268 | 269 | # remove dissimilar edges by yuv metric 270 | for x in xrange(w): 271 | for y in xrange(h): 272 | n = get_node(x, y, im, nodes) 273 | neighbours_to_remove = [ne for ne in n.neighbours if pixels_are_dissimilar(n.rgb, ne.rgb)] 274 | for ne in neighbours_to_remove: 275 | n.remove_conn(ne) 276 | 277 | # to measure the curve length that a diagonal is part of 278 | # start from one end of the diagonal and move away from its neighbour in the other direction 279 | # measure the length of that curve. similarly, measure the length of the other curve 280 | # then add the two half-curve lengths (plus 1) to get the length of the entire curve 281 | def overall_curve_len(node1, node2): 282 | assert node1 in node2.neighbours 283 | assert node2 in node1.neighbours 284 | curve_len = int(half_curve_len(node1, node2) + half_curve_len(node2, node1) + 1) 285 | return curve_len 286 | 287 | # node1 is the node we start exploring from 288 | # node2 is the other node 289 | def half_curve_len(node1, node2): 290 | assert node1 in node2.neighbours 291 | assert node2 in node1.neighbours 292 | # early exit - node1 does not have valence 2 293 | # so no point exploring further 294 | if len(node1.neighbours) != 2: 295 | return 0 296 | assert len(node1.neighbours) == 2 297 | current, previous = node1, node2 298 | # we store the nodes encountered thus far to detect cycles 299 | # otherwise, we would loop forever if we enter a cycle 300 | encountered = set([node2]) 301 | result = 0 302 | while len(current.neighbours) == 2: 303 | # get the neighbours of the current pixel 304 | neighb1, neighb2 = current.neighbours 305 | # and update current and previous 306 | old_current_x, old_current_y = current.get_xy() 307 | if neighb1 == previous: 308 | current = neighb2 309 | else: 310 | current = neighb1 311 | previous = get_node(old_current_x, old_current_y, im, nodes) 312 | result += 1 313 | if current not in encountered: 314 | encountered.add(current) 315 | else: 316 | # cycle detected, divide by half to avoid double-counting 317 | result /= 2.0 318 | break 319 | return result 320 | 321 | def largest_connected_components(topleft, topright, bottomleft, bottomright, window_edge_len, im): 322 | w, h = im.size 323 | half_window_minus_one = window_edge_len/2 - 1 324 | # any pixel we encounter should not exceed these bounds 325 | max_x = min(w-1, bottomright.x + half_window_minus_one) 326 | min_x = max(0, topleft.x - half_window_minus_one) 327 | max_y = min(h-1, bottomleft.y + half_window_minus_one) 328 | min_y = max(0, topleft.y - half_window_minus_one) 329 | # use depth-first search 330 | component1_size = dfs_connected_component_size(topleft, max_x, min_x, max_y, min_y) 331 | component2_size = dfs_connected_component_size(topright, max_x, min_x, max_y, min_y) 332 | return component1_size, component2_size 333 | 334 | def dfs_connected_component_size(node, max_x, min_x, max_y, min_y): 335 | encountered = set([node]) 336 | stack = [node] 337 | while len(stack) > 0: 338 | current = stack.pop() 339 | for ne in current.neighbours: 340 | if ne not in encountered and min_x <= ne.x <= max_x and min_y <= ne.y <= max_y: 341 | encountered.add(ne) 342 | stack.append(ne) 343 | return len(encountered) 344 | 345 | # apply heuristics to make graph planar 346 | for x in xrange(w-1): 347 | for y in xrange(h-1): 348 | n = get_node(x, y, im, nodes) 349 | right = get_node(x+1, y, im, nodes) # node to the right of the curr node 350 | down = get_node(x, y+1, im, nodes) # node directly below the curr node 351 | rightdown = get_node(x+1, y+1, im, nodes) # node directly below and to the right of the curr node 352 | 353 | # edges 354 | self_to_right = right in n.neighbours 355 | self_to_down = down in n.neighbours 356 | right_to_rightdown = right in rightdown.neighbours 357 | down_to_rightdown = down in rightdown.neighbours 358 | # diagonals 359 | diag1 = rightdown in n.neighbours 360 | diag2 = down in right.neighbours 361 | 362 | # check if fully connected 363 | vert_and_horiz_edges = self_to_right and self_to_down and right_to_rightdown and down_to_rightdown 364 | no_vert_horiz_edges = not (self_to_right or self_to_down or right_to_rightdown or down_to_rightdown) 365 | both_diagonals = diag1 and diag2 366 | 367 | fully_connected = vert_and_horiz_edges and both_diagonals # all 6 connections are present 368 | only_diagonals = no_vert_horiz_edges and both_diagonals # only the diagonals are present 369 | 370 | # we increase this each time a heuristic votes to keep diagonal 1 371 | # and decrease this each time a heuristic votes to keep diagonal 2 372 | keep_diag1 = 0.0 373 | # at the end of the three heuristics, if it is > 0, we keep diagonal 2 374 | # and if it is < 0, we keep diagonal 2 375 | # no clue what we should do if it equals 0, though 376 | 377 | if fully_connected: 378 | n.remove_conn(rightdown) 379 | right.remove_conn(down) 380 | 381 | if only_diagonals: 382 | # curves heuristic 383 | # the longer curve should be kept 384 | diag1_curve_len = overall_curve_len(n, rightdown) 385 | diag2_curve_len = overall_curve_len(right, down) 386 | curve_len_difference = abs(diag1_curve_len - diag2_curve_len) 387 | if diag1_curve_len > diag2_curve_len: 388 | keep_diag1 += curve_len_difference 389 | else: 390 | keep_diag1 -= curve_len_difference 391 | # sparse pixels heuristic 392 | # for each diagonal, find the length of the largest connected component 393 | # while making sure that we stay within a window of, say, 8 394 | window_edge_len = 8 395 | component1_size, component2_size = largest_connected_components(n, right, down, rightdown, window_edge_len, im) 396 | component_size_difference = abs(component1_size - component2_size) 397 | if component1_size < component2_size: 398 | keep_diag1 += component_size_difference 399 | else: 400 | keep_diag1 -= component_size_difference 401 | # islands heuristic 402 | if (len(n.neighbours) == 1 or len(rightdown.neighbours) == 1) and \ 403 | len(right.neighbours) != 1 and len(down.neighbours) != 1: 404 | keep_diag1 += 5 405 | elif len(n.neighbours) != 1 and len(rightdown.neighbours) != 1 and \ 406 | (len(right.neighbours) == 1 or len(down.neighbours) == 1): 407 | keep_diag1 -= 5 408 | 409 | if keep_diag1 >= 0: 410 | right.remove_conn(down) 411 | else: 412 | n.remove_conn(rightdown) 413 | 414 | # test that the graph is planar 415 | def test_graph_is_planar(im, nodes): 416 | for x in xrange(w-1): 417 | for y in xrange(h-1): 418 | n = get_node(x, y, im, nodes) 419 | right = get_node(x+1, y, im, nodes) 420 | down = get_node(x, y+1, im, nodes) 421 | rightdown = get_node(x+1, y+1, im, nodes) 422 | # if n in rightdown.neighbours and right in down.neighbours: 423 | # print n.get_xy() 424 | 425 | if '--tests' in sys.argv: 426 | test_graph_is_planar(im, nodes) 427 | 428 | def find_all_voronoi_points(x, y, im): 429 | # x, y = 0, 0 is the topleft pixel 430 | n = get_node(x, y, im, nodes) 431 | 432 | x_center = x + 0.5 433 | y_center = y + 0.5 434 | 435 | # for each of the eight directions, decide 436 | # where to put points, if at all 437 | # first, the up, down, left, right edges 438 | up = get_node(x, y-1, im, nodes) 439 | if up is not None: 440 | if up not in n.neighbours: 441 | n.vor_pts.append((x_center, y_center - 0.25)) 442 | else: 443 | n.vor_pts.append((x_center, y_center - 0.5)) 444 | 445 | dn = get_node(x, y+1, im, nodes) 446 | if dn is not None: 447 | if dn not in n.neighbours: 448 | n.vor_pts.append((x_center, y_center + 0.25)) 449 | else: 450 | n.vor_pts.append((x_center, y_center + 0.5)) 451 | 452 | lt = get_node(x-1, y, im, nodes) 453 | if lt is not None: 454 | if lt not in n.neighbours: 455 | n.vor_pts.append((x_center - 0.25, y_center)) 456 | else: 457 | n.vor_pts.append((x_center - 0.5, y_center)) 458 | 459 | rt = get_node(x+1, y, im, nodes) 460 | if rt is not None: 461 | if rt not in n.neighbours: 462 | n.vor_pts.append((x_center + 0.25, y_center)) 463 | else: 464 | n.vor_pts.append((x_center + 0.5, y_center)) 465 | 466 | # next, the diagonal neighbours 467 | up_in_neighbours = up is not None and up in n.neighbours 468 | dn_in_neighbours = dn is not None and dn in n.neighbours 469 | lt_in_neighbours = lt is not None and lt in n.neighbours 470 | rt_in_neighbours = rt is not None and rt in n.neighbours 471 | 472 | uplt = get_node(x-1, y-1, im, nodes) 473 | if uplt is not None: 474 | if uplt in n.neighbours: 475 | n.vor_pts.append((x_center - 0.75, y_center - 0.25)) 476 | n.vor_pts.append((x_center - 0.25, y_center - 0.75)) 477 | if (up_in_neighbours and not lt_in_neighbours) or \ 478 | (lt_in_neighbours and not up_in_neighbours): 479 | n.vor_pts.append((x_center - 0.5, y_center - 0.5)) 480 | else: 481 | if up in lt.neighbours: 482 | n.vor_pts.append((x_center - 0.25, y_center - 0.25)) 483 | else: 484 | n.vor_pts.append((x_center - 0.5, y_center - 0.5)) 485 | else: 486 | n.vor_pts.append((x_center - 0.5, y_center - 0.5)) 487 | 488 | dnlt = get_node(x-1, y+1, im, nodes) 489 | if dnlt is not None: 490 | if dnlt in n.neighbours: 491 | n.vor_pts.append((x_center - 0.75, y_center + 0.25)) 492 | n.vor_pts.append((x_center - 0.25, y_center + 0.75)) 493 | if (dn_in_neighbours and not lt_in_neighbours) or \ 494 | (lt_in_neighbours and not dn_in_neighbours): 495 | n.vor_pts.append((x_center - 0.5, y_center + 0.5)) 496 | else: 497 | if dn in lt.neighbours: 498 | n.vor_pts.append((x_center - 0.25, y_center + 0.25)) 499 | else: 500 | n.vor_pts.append((x_center - 0.5, y_center + 0.5)) 501 | else: 502 | n.vor_pts.append((x_center - 0.5, y_center + 0.5)) 503 | 504 | uprt = get_node(x+1, y-1, im, nodes) 505 | if uprt is not None: 506 | if uprt in n.neighbours: 507 | n.vor_pts.append((x_center + 0.75, y_center - 0.25)) 508 | n.vor_pts.append((x_center + 0.25, y_center - 0.75)) 509 | if (up_in_neighbours and not rt_in_neighbours) or \ 510 | (rt_in_neighbours and not up_in_neighbours): 511 | n.vor_pts.append((x_center + 0.5, y_center - 0.5)) 512 | else: 513 | if up in rt.neighbours: 514 | n.vor_pts.append((x_center + 0.25, y_center - 0.25)) 515 | else: 516 | n.vor_pts.append((x_center + 0.5, y_center - 0.5)) 517 | else: 518 | n.vor_pts.append((x_center + 0.5, y_center - 0.5)) 519 | 520 | dnrt = get_node(x+1, y+1, im, nodes) 521 | if dnrt is not None: 522 | if dnrt in n.neighbours: 523 | n.vor_pts.append((x_center + 0.75, y_center + 0.25)) 524 | n.vor_pts.append((x_center + 0.25, y_center + 0.75)) 525 | if (dn_in_neighbours and not rt_in_neighbours) or \ 526 | (rt_in_neighbours and not dn_in_neighbours): 527 | n.vor_pts.append((x_center + 0.5, y_center + 0.5)) 528 | else: 529 | if dn in rt.neighbours: 530 | n.vor_pts.append((x_center + 0.25, y_center + 0.25)) 531 | else: 532 | n.vor_pts.append((x_center + 0.5, y_center + 0.5)) 533 | else: 534 | n.vor_pts.append((x_center + 0.5, y_center + 0.5)) 535 | 536 | def find_potentially_useless_points(n): 537 | num_pts = len(n.vor_pts) 538 | result = set() 539 | for i in xrange(len(n.vor_pts)): 540 | p1 = n.vor_pts[i % num_pts] 541 | p2 = n.vor_pts[(i+1) % num_pts] 542 | p3 = n.vor_pts[(i+2) % num_pts] 543 | if (p1[0]-p2[0],p1[1]-p2[1]) == (p2[0]-p3[0],p2[1]-p3[1]): 544 | result.add(p2) 545 | return result 546 | 547 | # given a list of points (in our case, the polygon points for some pixel), 548 | # eliminate all points p such that p and its immediate neighbours 549 | # have an angle of 180 degrees 550 | def find_useless_pts(n): 551 | global points 552 | potentially_useless = find_potentially_useless_points(n) 553 | actually_useful = set() 554 | for coord_pair in potentially_useless: 555 | pt = points[coord_pair] 556 | for other_node in pt.nodes: 557 | if other_node != n and pt not in find_potentially_useless_points(other_node): 558 | actually_useful.add(pt.get_xy()) 559 | useless = potentially_useless - actually_useful 560 | return useless 561 | 562 | '''tests''' 563 | # remember, our system is left-handed 564 | # (0, 0) is the topleft pixel 565 | # and NOT the bottom-left pixel 566 | def test_is_to_the_left(): 567 | assert is_to_the_left((-1,1), (0,0), (1,1)) is False 568 | assert is_to_the_left((0,0), (0,0), (1,1)) is False 569 | assert is_to_the_left((2,2), (0,0), (1,1)) is False 570 | assert is_to_the_left((-0.6,-0.4), (0,0), (1,1)) is False 571 | 572 | def test_convex_hull(): 573 | pts1 = [(0,0), (0.5,0.25), (0.75,0.25), (1,0), (0.75,0.75), (0.5,0.75), (0,1), (0.25,0.5)] 574 | cvh1 = {(0, 1), (0.75, 0.75), (1, 0), (0, 0)} 575 | 576 | pt2 = [(0,0), (1,-1), (1,0), (1,1), (1.5, -0.5), (2,0)] 577 | cvh2 = [(0,0), (1,-1), (1.5,-0.5), (2,0), (1,1)] 578 | 579 | pt3 = [(0,0), (1,1), (1,0), (1,-1), (1.5, 0.5), (2,0)] 580 | cvh3 = [(0,0), (1,1), (1.5,0.5), (2,0), (1,-1)] 581 | 582 | # assert convex_hull(pt2) == cvh2 # FAILS 583 | assert convex_hull(pt3) == cvh3 584 | 585 | if '--tests' in sys.argv: 586 | test_is_to_the_left() 587 | test_convex_hull() 588 | 589 | points = {} 590 | # points is a dict mapping (x,y) to the Point present there. 591 | # We could use an array because the Point locations are quantized 592 | # to quarter-pixels, but there are 4wh possible point locations, 593 | # which would mean a very sparse array and a lot of wasted 594 | # memory. So the dict is a better way to store all the Points 595 | 596 | # populate the neighbours for each point belonging to node n 597 | # by treating n.vor_pts as a circular array 598 | def populate_neighbours(n): 599 | global points 600 | # initialize the list of neighbours (with respect to n) for each point 601 | for p in n.vor_pts: 602 | points[p].neighbours[n] = set() 603 | # base cases 604 | num_vor_pts = len(n.vor_pts) 605 | if num_vor_pts == 1: 606 | return 607 | if num_vor_pts == 2: 608 | p1, p2 = points[n.vor_pts[0]], points[n.vor_pts[1]] 609 | p1.neighbours[n].add(p2) 610 | p2.neighbours[n].add(p1) 611 | return 612 | # circular array 613 | for i in xrange(len(n.vor_pts)): 614 | p = points[n.vor_pts[i]] 615 | p.neighbours[n].add(points[n.vor_pts[(i+1)%num_vor_pts]]) 616 | p.neighbours[n].add(points[n.vor_pts[(i-1)%num_vor_pts]]) 617 | 618 | # now construct the simplified voronoi diagram 619 | # and in the process, fill up the global Points map 620 | for x in xrange(w): 621 | for y in xrange(h): 622 | find_all_voronoi_points(x, y, im) 623 | n = get_node(x, y, im, nodes) 624 | n.vor_pts = convex_hull(n.vor_pts) 625 | # populate the points dict 626 | for xx, yy in n.vor_pts: 627 | if (xx, yy) in points: 628 | p = points[(xx, yy)] 629 | p.nodes.add(n) 630 | else: 631 | p = Point(x=xx, y=yy) 632 | points[(xx, yy)] = p 633 | p.nodes.add(n) 634 | populate_neighbours(n) 635 | 636 | # remove all useless points 637 | for x in xrange(w): 638 | for y in xrange(h): 639 | n = get_node(x, y, im, nodes) 640 | useless_pts = find_useless_pts(n) 641 | n.vor_pts = filter(lambda p: p not in useless_pts, n.vor_pts) 642 | # also update the global points dict 643 | for p in useless_pts: 644 | # tell p's neighbours to forget p 645 | for ne in points[p].neighbours[n]: 646 | ne.neighbours[n].remove(points[p]) 647 | del points[p] 648 | 649 | def test_point_positions(): 650 | global points, imagename 651 | if imagename == 'img/smw_boo.png': 652 | assert (8.75, 11.75) in points 653 | assert (0, 0) in points 654 | 655 | def test_point_neighbours(): 656 | global points, imagename 657 | if imagename == 'img/smw_boo.png': 658 | assert (5.75, 0.75) in points 659 | # assert { (6,0), (5,1), (6.25,1.25) } == { pt.get_xy() for pt in points[(5.75, 0.75)].neighbours[get_node(, nodes5,1,im)] } 660 | 661 | def test_polygons_are_dissimilar(): 662 | global points, imagename 663 | if imagename == 'img/smw_boo.png': 664 | p1 = points[(3.25, 4.25)] 665 | p2 = points[(2.75, 3.75)] 666 | assert not polygons_are_dissimilar(p1, p2) 667 | 668 | if '--tests' in sys.argv: 669 | test_point_positions() 670 | test_point_neighbours() 671 | test_polygons_are_dissimilar() 672 | 673 | # render('voronoi') 674 | # save(process_command_line_arg('--save', False)) 675 | # exit(0) 676 | 677 | # global "list" of visible edge sequences 678 | vedges = set() 679 | 680 | # pt is a point at which three visible edges are meeting 681 | # this function merges them as per section 3.3 on page 5 682 | def merge_visible_edges(pt): 683 | # locate the neighbour of 'pt' in each one of these vedges 684 | # this can be done in constant time, since we know that pt 685 | # is an endpoint for each one of these vedges, and therefore 686 | # it is either at the head or the tail of the lists 687 | vedge1, vedge2, vedge3 = pt.vedges 688 | neighb1 = vedge1.points[1] if vedge1.points[0] == pt else vedge1.points[-2] 689 | neighb2 = vedge2.points[1] if vedge2.points[0] == pt else vedge2.points[-2] 690 | neighb3 = vedge3.points[1] if vedge3.points[0] == pt else vedge3.points[-2] 691 | # measure the angles - TODO 692 | pass 693 | 694 | def keep_closest_collinear_neighbours(p, neighbours): 695 | # filter all neighbours ne such that p has a neighbour ne', ne' has ne as its neighbour 696 | # and p, ne and ne' lie on a straight line with ne' in between p and ne 697 | to_remove = set() 698 | for i in xrange(len(neighbours)): 699 | for j in xrange(i+1, len(neighbours)): 700 | if is_straight_line(p, neighbours[i], neighbours[j]): 701 | # if p is not the middle point, remove 702 | # the neighbour which is farther away 703 | if p.x < neighbours[i].x and p.x < neighbours[j].x: 704 | if neighbours[i].x < neighbours[j].x: 705 | to_remove.add(neighbours[j]) 706 | else: 707 | to_remove.add(neighbours[i]) 708 | elif p.x > neighbours[i].x and p.x > neighbours[j].x: 709 | if neighbours[i].x > neighbours[j].x: 710 | to_remove.add(neighbours[j]) 711 | else: 712 | to_remove.add(neighbours[i]) 713 | 714 | return filter(lambda p: p not in to_remove, neighbours) 715 | 716 | # p is a point for which we want to find all 717 | # containing visible edge sequences 718 | def find_all_visible_edges(p): 719 | # keep only neighbours with which I have a single-length visible edge 720 | slve_neighbours = filter(lambda x: polygons_are_dissimilar(x, p), p.all_neighbours()) 721 | # keep only my closest neighbours along a line 722 | slve_neighbours = keep_closest_collinear_neighbours(p, slve_neighbours) 723 | # remove neighbours which already have a visible edge *sequence* with me 724 | for ve_object in p.vedges: 725 | slve_neighbours = filter(lambda ne: ne not in ve_object.points, slve_neighbours) 726 | 727 | # should we explore the visible edge with (p, ne) as a starting edge? 728 | # yes, if ne has not been explored before. if it has, 729 | # then surely it encountered the (ne, p) edge 730 | visible_edges = [find_visible_edge(p, ne) for ne in slve_neighbours] 731 | 732 | # check if two visible edge sequences are actually reversals of each other 733 | to_remove = set() 734 | for i in xrange(len(visible_edges)): 735 | for j in xrange(len(visible_edges)): 736 | if i != j: 737 | v, w = visible_edges[i], visible_edges[j] 738 | # we found a pair, and neither i nor j is marked for removal 739 | # first check for cycles 740 | vv = v[1:-1] if v[0] == v[-1] else v 741 | ww = w[1:-1] if w[0] == w[-1] else w 742 | if vv == list(reversed(ww)) and j not in to_remove and i not in to_remove: 743 | # add i to to_remove. we could add j too, either way works 744 | to_remove.add(i) 745 | # perform the removal 746 | visible_edges = [v for i,v in enumerate(visible_edges) if i not in to_remove] 747 | 748 | # if we only have two visible edge sequences, they are actually 749 | # two disjoint parts of one single visible edge sequence. so, we merge them 750 | if len(visible_edges) == 2 and len(p.vedges) == 0: # partial fix 751 | visible_edge1, visible_edge2 = visible_edges[0], visible_edges[1] 752 | 753 | # we need to check if either of the two sequences is a cycle 754 | is_cycle1 = visible_edge1[0] == visible_edge1[-1] 755 | is_cycle2 = visible_edge2[0] == visible_edge2[-1] 756 | 757 | if not is_cycle1 and not is_cycle2: 758 | visible_edges = [list(reversed(visible_edge1))[:-1] + visible_edge2] 759 | 760 | result = [] 761 | for v in visible_edges: 762 | ve_object = VisibleEdge(v) 763 | result.append(ve_object) 764 | for pt in v: 765 | pt.vedges.add(ve_object) 766 | 767 | return result 768 | 769 | # (p1, p2) is the first edge of the visible 770 | # edge with p1 as an endpoint. Examples: 771 | # 1 - 2 - 3 yields the list [1,2,3] 772 | # 1 - 2 - 3 - 1 yields the list [1,2,3,1] 773 | def find_visible_edge(p1, p2): 774 | assert p1 in p2.all_neighbours() 775 | assert p2 in p1.all_neighbours() 776 | 777 | prev, curr = p1, p2 778 | encountered = set([p1, p2]) 779 | result = [p1] 780 | while True: 781 | slve_neighbours = filter(lambda x: polygons_are_dissimilar(x, curr), curr.all_neighbours()) 782 | slve_neighbours = keep_closest_collinear_neighbours(curr, slve_neighbours) 783 | 784 | if len(slve_neighbours) != 2: 785 | result.append(curr) 786 | break 787 | 788 | result.append(curr) 789 | 790 | neighb1, neighb2 = slve_neighbours 791 | if neighb1 == prev: 792 | prev, curr = curr, neighb2 793 | else: 794 | prev, curr = curr, neighb1 795 | 796 | if curr not in encountered: 797 | encountered.add(curr) 798 | else: 799 | result.append(curr) 800 | break 801 | 802 | return result 803 | 804 | for p in points.values(): 805 | vedges |= set(find_all_visible_edges(p)) 806 | 807 | def merge_vedges(p, edge1, edge2, m): 808 | global vedges 809 | 810 | initial = len(vedges) 811 | 812 | e1, e2 = edge1.points, edge2.points 813 | 814 | if e1[0] != p: e1.reverse() # CHECK 815 | if e2[0] != p: e2.reverse() # THIS! 816 | 817 | is_cycle1 = e1[0] == e1[-1] 818 | is_cycle2 = e2[0] == e2[-1] 819 | if is_cycle1 and is_cycle2: 820 | e = e1[:-1] + e2 821 | elif is_cycle1 and not is_cycle2: 822 | e = list(reversed(e2))[:-1] + e1 823 | else: # CHECK [ABOVE] THIS 824 | e = list(reversed(e1))[:-1] + e2 825 | 826 | new_edge = VisibleEdge(e) 827 | 828 | vedges.remove(edge1) 829 | vedges.remove(edge2) 830 | 831 | for pt in new_edge.points: 832 | if edge1 in pt.vedges: 833 | pt.vedges.remove(edge1) 834 | if edge2 in pt.vedges: 835 | pt.vedges.remove(edge2) 836 | pt.vedges.add(new_edge) 837 | 838 | vedges.add(new_edge) 839 | 840 | if initial <= len(vedges): # less than not possible, right? 841 | print p, "merging by", m, "::", initial, ">", len(vedges) 842 | point_list.append(p) 843 | 844 | def resolve_juction(p): 845 | global vedges, count 846 | 847 | assert len(p.vedges) == 3 848 | v1, v2, v3 = p.vedges 849 | 850 | def corner(p, vedge): 851 | return True if p == vedge[0] or p == vedge[1] else False 852 | 853 | ne1 = v1[1] if v1[0] == p else v1[-2] 854 | ne2 = v2[1] if v2[0] == p else v2[-2] 855 | ne3 = v3[1] if v3[0] == p else v3[-2] 856 | 857 | # if p == ne1 or p == ne2 or p == ne3: 858 | # return 859 | 860 | e1, e2, e3 = map(lambda ne: is_contour_edge(p, ne), [ne1, ne2, ne3]) 861 | 862 | if e1 and e2 and not e3: 863 | merge_vedges(p, v1, v2, "contour edges") 864 | elif e2 and e3 and not e1: 865 | merge_vedges(p, v2, v3, "contour edges") 866 | elif e3 and e1 and not e2: 867 | merge_vedges(p, v3, v1, "contour edges") 868 | else: # connect edges 180 degrees apart 869 | a1, a2, a3 = angle(p, ne1, ne2), angle(p, ne2, ne3), angle(p, ne3, ne1) 870 | if a1 > a2 and a1 > a3: 871 | # point_list.append(ne3) 872 | merge_vedges(p, v1, v2, "closest angle") 873 | elif a2 > a3 and a2 > a1: 874 | # point_list.append(ne1) 875 | merge_vedges(p, v2, v3, "closest angle") 876 | elif a3 > a1 and a3 > a2: 877 | # point_list.append(ne2) 878 | merge_vedges(p, v3, v1, "closest angle") 879 | # else: # exclusive conditions? 880 | # print "#debug: couldn't merge ANY edges\n" 881 | 882 | for p in points.values(): 883 | if len(p.vedges) > 3: 884 | # print "unlikely case:", p, len(p.vedges) 885 | pass 886 | if len(p.vedges) == 3: 887 | # point_list.append(p) 888 | resolve_juction(p) 889 | 890 | def test_visible_edges(): 891 | global imagename 892 | 893 | num_vedges = { 'img/smw_boo.png' : 6, 894 | 'img/invaders_01.png' : 4, 895 | 'img/invaders_02.png' : 3, 896 | 'img/invaders_03.png' : 3, 897 | 'img/invaders_04.png' : 3, 898 | 'img/smw_help.png' : 8, 899 | } 900 | 901 | if imagename in num_vedges: 902 | assert len(vedges) == num_vedges[imagename] 903 | 904 | if imagename == 'img/smw_boo.png': 905 | v = points[(7,1)].vedges 906 | assert len(v) == 1 907 | assert len(v.pop().points) == 67 908 | 909 | v = points[(3.25, 4.25)].vedges 910 | assert len(v) == 1 911 | assert len(v.pop().points) == 59 912 | 913 | v = points[(4.25, 5.25)].vedges 914 | assert len(v) == 1 915 | assert len(v.pop().points) == 9 916 | 917 | v = points[(9.25, 6.25)].vedges 918 | assert len(v) == 1 919 | assert len(v.pop().points) == 23 920 | 921 | v = points[(3.25, 10.25)].vedges 922 | assert len(v) == 1 923 | assert len(v.pop().points) == 25 924 | 925 | if '--tests' in sys.argv: 926 | test_visible_edges() 927 | 928 | DEGREE, SMOOTHNESS = 3, 500 929 | for v in vedges: 930 | v.bspline = bspline([p.get_xy() for p in v.points], DEGREE, SMOOTHNESS) 931 | 932 | render_stage = process_command_line_arg('--render') 933 | if render_stage is not None: 934 | render(render_stage) 935 | 936 | save_image = process_command_line_arg('--save', False) 937 | if save_image is not None: 938 | if render_stage is not None: 939 | save(save_image) 940 | else: 941 | sys.stderr.write('cannot save image, nothing has been rendered\n') -------------------------------------------------------------------------------- /notes/114688_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/notes/114688_2.pdf -------------------------------------------------------------------------------- /notes/8x8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/notes/8x8.png -------------------------------------------------------------------------------- /notes/Depixelizing pixelArt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/notes/Depixelizing pixelArt.pdf -------------------------------------------------------------------------------- /notes/bsplines-outline: -------------------------------------------------------------------------------- 1 | *[what about a non-visible edge] 2 | visible edges (where significantly different colors meet) 3 | connected sequences (that contain only valence-2 nodes) 4 | count only visible edges to determine valence of node 5 | converted into B-spline curves 6 | control points of B-splines initialized to node locations 7 | 8 | if three splines end at single common node 9 | if "separated" cells have a YUV distance of at most 100/255 10 | categorize as shading edge; else contour edge 11 | if one shading edge and two contour edges 12 | connect the contour edges 13 | if this heuristic does not resolve the situation 14 | connect the pair angle between edges closest to 180 degrees [Fig. 5B] 15 | 16 | adjust endpoint of curve that ends at a T-junction to 17 | lie on the curve that continues through the T-junction -------------------------------------------------------------------------------- /notes/cython.txt: -------------------------------------------------------------------------------- 1 | cython main.py -> main.c 2 | gcc -O3 -c -fPIC main.c -L/usr/local/lib/python2.7/ -lpython2.7 -I/usr/include/python2.7/ -> main.o 3 | gcc -shared main.o -o main.so -> main.so 4 | python so.py -> output we need 5 | -------------------------------------------------------------------------------- /notes/inputs.txt: -------------------------------------------------------------------------------- 1 | all inputs are located at: http://research.microsoft.com/en-us/um/people/kopf/pixelart/supplementary/multi_comparison.html 2 | 3 | in smw_boo.png, the outline of the body has length 43 and there are 19 diagonals - curves heuristic 4 | in 8x8.png, the top left pink pixel in the two diagonals is (7,10). There are 41 white pixels and 5 pink pixels, however, the white pixels are connected to the gray pixels which in turn are connected to the dark gray pixels, so effectively the size of the white connected component is 56 (64 total pixels - 5 pink pixels - 3 black pixels) - sparse heuristic 5 | 6 | for direct simplified voronoi construction 7 | 8 | 1 1 9 | 2 4 (angle between the two can be 45 90 135 or 180) 10 | 3 11 | 8 1 -------------------------------------------------------------------------------- /notes/notespixel.txt: -------------------------------------------------------------------------------- 1 | pixel art characterists 2 | popular due to hardware constraints 3 | small palette of colors 4 | each pixel placed manually 5 | 6 | challenges to vectorizing pixel art 7 | typical techniques - segmentation, edge detection filtering 8 | group pixels into regions, convert the boundaries to smooth curves 9 | pixel art contains tiny features, maybe even just one pixel 10 | such algorithms will incur loss of detail 11 | specialized algorithms have staircasing artifacts due to their local nature 12 | staircasing - 13 | further, magnification is usually only 2x 3x or 4x 14 | 15 | input - pixel art image 16 | output - resolution-independent vector representation 17 | 18 | challenges 19 | every pixel matters 20 | pixel-wide 8-connected lines and curves 21 | at original scale, they appear connected 22 | upon magnification, they appear disconnected 23 | locally ambiguous configurations 24 | 2x2 checkerboard - which one to join? (foreground/background ambiguity) 25 | 26 | w+1 x h+1 mesh 27 | each square is a pixel 28 | vertical/horizontal neighbours have an edge in common 29 | diagonal neighbours have a pixel in common 30 | 31 | similarity graph for each pixel 32 | used to disconnect dissimilar pixels 33 | initally each pixel is connected to its eight neighbours 34 | remove edges between dissimilar pixels 35 | dual of the resulting graph shows similar neighbours, now we only consider the dual 36 | if a 2x2 block is fully connected, remove both diagonals as it is part of a continuously shaded region 37 | if a 2x2 block has both diagonals but no horizontal/vertical connections, we need to choose which diagonal to remove 38 | cannot decide locally 39 | 40 | use 3 heuristics 41 | each heuristic has a weight 42 | choose the connection with the greater aggregated weight 43 | 44 | curves heuristic 45 | a curve connects valence-2 nodes in the similarity graph 46 | no junctions 47 | the two ends are valence-1 nodes 48 | minimum length of a curve is 1 49 | heuristic: keep the longer curve 50 | 51 | sparse pixels heuristic 52 | sparser color perceived as foreground, other as background 53 | measure the size of the component connected to the diagonals 54 | consider an 8x8 window centred around the diagonals 55 | heuristic: connect the pixels with the smaller connected component 56 | weight of heuristic = difference in sizes of connected components 57 | doubt: what if there are many connected components in the image? 58 | 59 | islands heuristic 60 | want to avoid fragmenting the image too much 61 | if one of the two-diagonals has valence-1 node, if we cut the connection we would get single disconnected pixel 62 | we vote for keeping this connection with an empirical weight of 5 63 | 64 | after removing all crossing diagonals the graph is planar 65 | create a reshaped pixel graph 66 | cut each edge in the similarity graph into two halves, assign each half to the node it is connected to 67 | generalized voronoi - each voronoi cell contains the points that are closest to the union of a node and its half edges 68 | take this graph and collapse all valence 2 nodes to form the simplified voronoi graph 69 | 70 | optimisation - we directly compute the simplified voronoi graph instead of constructing the accurate one and then collapsing it 71 | shape of a voronoi cell is fully determined by its 8 neighbours 72 | walk in scanline order over the nodes of the similarity mesh 73 | match specific configurations in 3x3 blocks at a time 74 | paste together the corresponding cell templates 75 | 76 | planar graph - a graph that can be drawn such that edges do not cut each other (except of course when they meet at the nodes) 77 | 78 | the reshaped cell graph has resolved connectivity issues 79 | however, it may still look blocky 80 | identify visible edges - where significantly different colors meet 81 | connected sequences of visible edges that contain only valence-2 nodes are converted into quadratic B-spline curves 82 | 83 | when three edges meet at a node, we connect two splines into one, creating a T-junction 84 | which two out of three to connect? 85 | each edge is categorized either as a shading edge or a contour edge 86 | shading edge - separate cells with similar colours that were nevertheless different enough to be classified as visible to begin with 87 | contour edge - separate cells with strongly dissimilar colors - based on YUV distance metric - if distance > 100/255 88 | if at a 3-way junction we have one shading edge and two contour edges we connect the contour edges 89 | if this heuristic does not resolve the situation, simply connect the two edges with the angle closest to 180 degrees 90 | 91 | b-spline curve optimization 92 | we still have staircasing artifacts 93 | improve the smoothness of the curves by optimizing the locations of their control points 94 | minimize the sum of per-node energy terms - this gives us a new position for each pixel 95 | energy of a node depends on its position and its smoothness 96 | smoothness = absence of curvature 97 | smoothness = (integral along curve influenced by point i) (magnitude of curvature at point s on the curve) 98 | compute by numerical integration 99 | positional energy term is used to minimize movement of points 100 | positional energy = magnitude(new pos - old pos) ^ 4 101 | problem - what if we remove intentionally sharp features along with staircasing artifacts? this is oversmoothing and we need to avoid it 102 | detect sharp features in the model 103 | exclude the regions around these features from the integration 104 | now, the nodes in the reshaped cell graph are quantized to quarter-pixel locations 105 | so the sharp features can only take a finite number of forms - along with their reflections and rotations 106 | once we detect a pattern, we exclude the part of the spline curve between the nodes of the pattern from the integration 107 | 108 | optimizing the energy function 109 | at each iteration do a random walk of the nodes and optimize each one locally 110 | given a node, take a small radius and try several random new offset positions within a small radius around the current location 111 | then, keep the new location that minimizes the node's energy term 112 | 113 | note - this optimizes only the spline nodes 114 | 115 | DOUBTS 116 | 117 | how to identify visible edges? 118 | how to fit bsplines? - deboor 1978 -------------------------------------------------------------------------------- /notes/pixel.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/notes/pixel.pdf -------------------------------------------------------------------------------- /notes/smw_boo_nearest_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/notes/smw_boo_nearest_16x.png -------------------------------------------------------------------------------- /notes/spline_logic: -------------------------------------------------------------------------------- 1 | 1. make a class for polygon points. an object of this class should contain the following data members: 2 | a. a set of references to nodes whose voronoi polygons have this point as one of their vertices - this set never changes 3 | b. a set of references to VisibleEdges which this point is a part of. this set is initially empty and is filled up in step 3 4 | c. a set of neighbour polygon points. this set is initially empty. do we need this? 5 | 2. enumerate all the polygon points into a global dict 6 | 3. for each polygon point: 7 | a. if it is already part of a visible edge, do nothing 8 | b. if not, find the longest visible edge it is a part of, in the process marking all other polygon points on the edge as belonging to that visible edge. 9 | c. in this way, build up a global list of longest possible visible edges 10 | d. each edge is an object of the VisibleEdge class, which contains, among other things, references to polygon points 11 | 4. for each edge in the global list of VisibleEdges, obtain the b-spline 12 | 5. at each polygon point which belongs to exactly three VisibleEdges, merge two of the three VisibleEdges together using the heuristics described in section 3.3 of the paper 13 | 6. optimize the b-splines. while optimizing, make sure that we do not optimize the 24 corner cases as described in figure 7 of the paper 14 | 15 | step 3: 16 | 17 | for each polygon point, we try to find the longest visible edge it is a part of. a point is always part of exactly one visible edges, with the following exception: if a point is part of more than one visible edge, then it is an endpoint of each one of those visible edges. -------------------------------------------------------------------------------- /notes/voronoi_logic: -------------------------------------------------------------------------------- 1 | horizontal/vertical half edge 2 | exists: don't add anything 3 | else: node at 1/4 dist. along the HE 4 | diagonal half edge: 5 | with upper-left nbr. 6 | no edge: only 1 point; where? 7 | if upper and left are connected to each other: 8 | quarter distances away 9 | else: 10 | place it at upper-left corner of pixel 11 | else: 12 | (both self-up and self-left) or (neither): 13 | figure 4b 14 | else: 15 | figure 6 16 | ...three symmetric cases -------------------------------------------------------------------------------- /resize-frames: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | `javac ScaleDown.java` 4 | Dir.glob('vid/extracted/*.png') do |image_name| 5 | output_name = image_name.gsub('extracted/', 'resized/') 6 | puts "Resizing #{image_name} to #{output_name}" 7 | `java ScaleDown #{image_name} 4 #{output_name}` 8 | end -------------------------------------------------------------------------------- /run-all: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | Dir.glob('img/*.png') do |image| 4 | puts image 5 | `python main.py --tests --image #{image} --render voronoi` 6 | end -------------------------------------------------------------------------------- /scrape-images.py: -------------------------------------------------------------------------------- 1 | import urllib2 2 | 3 | images = ['smw2_yoshi_01_input.png', 'smw2_yoshi_02_input.png', 'smw_bowser_input.png', 'smw_boo_input.png', 'smw_dolphin_input.png', 'smw_help_input.png', 'smw_mario_input.png', 'smw_mario_yoshi_input.png', 'smw_mushroom_input.png', 'smw_yoshi_input.png', 'sma_chest_input.png', 'sma_peach_01_input.png', 'sma_peach_02_input.png', 'smb_jump_input.png', 'smw_cape_mario_yoshi_input.png', 'sma_toad_input.png', 'smw2_koopa_input.png', 'invaders_01_input.png', 'invaders_02_input.png', 'invaders_03_input.png', 'invaders_04_input.png', 'invaders_05_input.png', 'invaders_06_input.png', 'mana_granpa_input.png', 'mana_joch_input.png', 'mana_rabite_input.png', 'mana_randi_01_input.png', 'mana_randi_02_input.png', 'mana_salamando_input.png', 'mana_sword_input.png', 'sbm1_01_input.png', 'sbm1_02_input.png', 'sbm1_03_input.png', 'sbm1_04_input.png', 'sbm4_01_input.png', 'sbm4_02_input.png', 'sbm4_03_input.png', 'sbm4_04_input.png', 'gaxe2_axbattler_01_input.png', 'gaxe2_axbattler_02_input.png', 'gaxe_skeleton_input.png', 'icon_atari_bomb_input.png', 'icon_disk_input.png', 'vista_cursor_input.png', 'win31_cursor_input.png', 'win31_386_input.png', 'win31_control_panel_input.png', 'win31_fonts_input.png', 'win31_keyboard_input.png', 'win31_ports_input.png', 'win31_setup_input.png', 'vikings_baelog_input.png', 'vikings_eric_input.png', 'vikings_olaf_input.png'] 4 | 5 | base_url = 'http://research.microsoft.com/en-us/um/people/kopf/pixelart/supplementary/input_images/' 6 | 7 | for image_name in images: 8 | url = base_url + image_name 9 | image_data = urllib2.urlopen(url).read() 10 | path_name = 'img/' + image_name.replace('_input', '') 11 | with open(path_name, 'wb') as f: 12 | f.write(image_data) 13 | print 'Downloaded', image_name, 'to', path_name -------------------------------------------------------------------------------- /so.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | lib = ctypes.cdll.LoadLibrary('./main.so') 4 | -------------------------------------------------------------------------------- /tex/proposal/Makefile: -------------------------------------------------------------------------------- 1 | all: proposal 2 | 3 | proposal: proposal.tex 4 | xelatex proposal.tex 5 | # gs -sDEVICE=pdfwrite -dNOPAUSE -dQUIET -dBATCH -dFirstPage=2 -sOutputFile=first2.pdf first.pdf 6 | 7 | clean: 8 | rm -f *.log *.out *.dvi *toc -------------------------------------------------------------------------------- /tex/proposal/corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/tex/proposal/corner.png -------------------------------------------------------------------------------- /tex/proposal/islands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/tex/proposal/islands.png -------------------------------------------------------------------------------- /tex/proposal/jagged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajnirp/depixelizing/7cbedb250f413699285953a1667f45d6ce0602d2/tex/proposal/jagged.png -------------------------------------------------------------------------------- /tex/proposal/proposal.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper,9pt]{article} 2 | 3 | \usepackage[margin=0.95in]{geometry} 4 | \usepackage{inconsolata} 5 | \usepackage[normalem]{ulem} 6 | % \usepackage{fontspec} 7 | \usepackage{charter} 8 | % \setromanfont{Times New Roman} 9 | \usepackage[hidelinks,backref]{hyperref} % clickable links and citations with no green borders 10 | \usepackage{amsmath} 11 | \usepackage{listings} % add source code snippets 12 | \usepackage{csquotes} % block quotes 13 | \usepackage{color} % it's a must these days / for the colors are fading 14 | \usepackage[dvips]{graphicx} 15 | \DeclareGraphicsExtensions{.png,.jpg} 16 | \setlength{\parindent}{0pt} 17 | \definecolor{mygreen}{rgb}{0,0.6,0} 18 | \definecolor{mygray}{rgb}{0.5,0.5,0.5} 19 | \definecolor{mydarkgray}{rgb}{0.4,0.4,0.4} 20 | \definecolor{mymauve}{rgb}{0.58,0,0.82} 21 | \definecolor{myrust}{rgb}{0.77,0,0} 22 | \pagestyle{empty} 23 | 24 | \lstset{ % 25 | backgroundcolor=\color{white}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor} 26 | basicstyle=\footnotesize\ttfamily, % the size of the fonts that are used for the code 27 | breakatwhitespace=false, % sets if automatic breaks should only happen at whiterspace 28 | breaklines=true, % sets automatic line breaking 29 | captionpos=b, % sets the caption-position to bottom 30 | commentstyle=\color{mygreen}, % comment style 31 | deletekeywords={...}, % if you want to delete keywords from the given language 32 | escapeinside={\%*}{*)}, % if you want to add LaTeX within your code 33 | extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 34 | frame=single, % adds a frame around the code 35 | keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) 36 | keywordstyle=\color{blue}, % keyword style 37 | language=C++, % the language of the code 38 | morekeywords={*,...}, % if you want to add more keywords to the set 39 | numbers=none, % where to put the line-numbers; possible values are (none, left, right) 40 | numbersep=5pt, % how far the line-numbers are from the code 41 | numberstyle=\tiny\color{mygray}, % the style that is used for the line-numbers 42 | rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here)) 43 | showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' 44 | showstringspaces=false, % underline spaces within strings only 45 | showtabs=false, % show tabs within strings adding particular underscores 46 | stepnumber=2, % the step between two line-numbers. If it's 1, each line will be numbered 47 | stringstyle=\color{mymauve}, % string literal style 48 | tabsize=2, % sets default tabsize to 2 spaces 49 | % title=\lstname % show the filename of files included with \lstinputlisting; also try caption instead of title 50 | } 51 | 52 | \hypersetup{ 53 | colorlinks=true, 54 | linkcolor=red, 55 | urlcolor=blue, 56 | citecolor=green, 57 | linktoc=page 58 | } 59 | 60 | % \author{ 61 | % Kandarp Khandwala\\ 62 | % 110050005 63 | % \and 64 | % Rohan Prinja\\ 65 | % 110050011 66 | % } 67 | % \title{Project Proposal} 68 | 69 | \begin{document} 70 | 71 | % \vspace*{4.6cm} 72 | 73 | % \maketitle 74 | 75 | \textcolor{myrust}{\Huge{\centerline{Project Proposal}}} 76 | \textcolor{myrust}{\Large{\centerline{CS663}}} 77 | % \vspace{16pt} 78 | 79 | \textcolor{myrust}{\section{About}} 80 | 81 | In this document we propose a project for the Image Processing course, CS663, and outline some of its details. \textbf{Team members}: Kandarp Khandwala (110050005) and Rohan Prinja (110050011) 82 | 83 | \textcolor{myrust}{\section{Papers we will implement}} 84 | 85 | We plan to implement the paper \href{http://research.microsoft.com/en-us/um/people/kopf/pixelart/}{Depixelizing Pixel Art}. The paper outlines a method for converting pixel art (very-low-resolution raster images) to vector images, and obtains results superior to simple upsampling and even high-quality existing pixel art scaling algorithms like hqx. 86 | 87 | \textcolor{myrust}{\section{Datasets}} 88 | 89 | The authors of the paper have demonstrated their algorithm on around 54 inputs on \href{http://research.microsoft.com/en-us/um/people/kopf/pixelart/supplementary/comparison\_bicubic.html}{this page}. We shall use these images as our dataset. 90 | 91 | \textcolor{myrust}{\section{Validation strategy}} 92 | 93 | To evaluate any pixel art scaling algorithm we must use visual inspection, since the aim of the paper is to present an algorithm that creates aesthetic and good-looking vector art from pixel art. Some factors to consider while visually inspecting two vector art outputs are as follows:\\ 94 | 95 | \begin{enumerate} 96 | \itemsep-0.25em 97 | \item An image with less jagged edges and less blockiness is better. For example, the image on the left is better:\\ 98 | \includegraphics[height=5cm]{jagged} 99 | \item An image which is not overly smoothed is better. For example, the image on the right is better:\\ 100 | \includegraphics[height=3cm]{corner} 101 | \item An image which does not have ``incorrect islands" is better. An ``incorrect island" is when an output vector art image contains a small section cut-off from a larger body even though the intention of the pixel artist was to have a single continguous body. For example, the image on the left is better:\\ 102 | \includegraphics[height=3cm]{islands} 103 | \end{enumerate} 104 | 105 | We can sum up the above heuristics by saying that in general, our overall aim is to see how close the vector output is to the intention of the pixel artist. The closer it is, the better the vector output. 106 | 107 | \textcolor{myrust}{\section{Deliverables}} 108 | 109 | After we implement the algorithm as described in the paper, we will tune the parameters of the algorithm so as to obtain outputs better than hq4x on most or all of the input images. We do not expect to surpass the output of the paper itself, however, we will try to decrease the running time. 110 | 111 | \end{document} -------------------------------------------------------------------------------- /tex/report/Makefile: -------------------------------------------------------------------------------- 1 | all: report 2 | 3 | report: report.tex 4 | xelatex report.tex 5 | # gs -sDEVICE=pdfwrite -dNOPAUSE -dQUIET -dBATCH -dFirstPage=2 -sOutputFile=first2.pdf first.pdf 6 | 7 | clean: 8 | rm -f *.log *.out *.dvi *toc -------------------------------------------------------------------------------- /tex/report/report.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper,9pt]{article} 2 | 3 | \usepackage[margin=0.95in]{geometry} 4 | \usepackage{inconsolata} 5 | \usepackage[normalem]{ulem} 6 | % \usepackage{fontspec} 7 | \usepackage{charter} 8 | % \setromanfont{Times New Roman} 9 | \usepackage[hidelinks,backref]{hyperref} % clickable links and citations with no green borders 10 | \usepackage{amsmath} 11 | \usepackage{listings} % add source code snippets 12 | \usepackage{csquotes} % block quotes 13 | \usepackage{color} % it's a must these days / for the colors are fading 14 | \usepackage[dvips]{graphicx} 15 | \DeclareGraphicsExtensions{.png,.jpg} 16 | \setlength{\parindent}{0pt} 17 | \definecolor{mygreen}{rgb}{0,0.6,0} 18 | \definecolor{mygray}{rgb}{0.5,0.5,0.5} 19 | \definecolor{mydarkgray}{rgb}{0.4,0.4,0.4} 20 | \definecolor{mymauve}{rgb}{0.58,0,0.82} 21 | \definecolor{myrust}{rgb}{0.77,0,0} 22 | \pagestyle{empty} 23 | 24 | \lstset{ % 25 | backgroundcolor=\color{white}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor} 26 | basicstyle=\footnotesize\ttfamily, % the size of the fonts that are used for the code 27 | breakatwhitespace=false, % sets if automatic breaks should only happen at whiterspace 28 | breaklines=true, % sets automatic line breaking 29 | captionpos=b, % sets the caption-position to bottom 30 | commentstyle=\color{mygreen}, % comment style 31 | deletekeywords={...}, % if you want to delete keywords from the given language 32 | escapeinside={\%*}{*)}, % if you want to add LaTeX within your code 33 | extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 34 | frame=single, % adds a frame around the code 35 | keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) 36 | keywordstyle=\color{blue}, % keyword style 37 | language=C++, % the language of the code 38 | morekeywords={*,...}, % if you want to add more keywords to the set 39 | numbers=none, % where to put the line-numbers; possible values are (none, left, right) 40 | numbersep=5pt, % how far the line-numbers are from the code 41 | numberstyle=\tiny\color{mygray}, % the style that is used for the line-numbers 42 | rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here)) 43 | showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' 44 | showstringspaces=false, % underline spaces within strings only 45 | showtabs=false, % show tabs within strings adding particular underscores 46 | stepnumber=2, % the step between two line-numbers. If it's 1, each line will be numbered 47 | stringstyle=\color{mymauve}, % string literal style 48 | tabsize=2, % sets default tabsize to 2 spaces 49 | % title=\lstname % show the filename of files included with \lstinputlisting; also try caption instead of title 50 | } 51 | 52 | \hypersetup{ 53 | colorlinks=true, 54 | linkcolor=red, 55 | urlcolor=blue, 56 | citecolor=green, 57 | linktoc=page 58 | } 59 | 60 | \author{ 61 | Kandarp Khandwala\\ 62 | 110050005 63 | \and 64 | Rohan Prinja\\ 65 | 110050011 66 | } 67 | \title{Project Report} 68 | 69 | \begin{document} 70 | 71 | % \vspace*{4.6cm} 72 | 73 | \maketitle 74 | 75 | % \textcolor{myrust}{\Huge{\centerline{Project Proposal}}} 76 | % \textcolor{myrust}{\Large{\centerline{CS663}}} 77 | % \vspace{16pt} 78 | 79 | \textcolor{myrust}{\section{About this document}} 80 | 81 | In this document we summarise the results of our project. Our project was to implement Kopf and Lischinski's 2011 SIGGRAPH paper \href{http://research.microsoft.com/en-us/um/people/kopf/pixelart/}{Depixelizing Pixel Art}. The paper proposes a multi-stage procedure for converting pixel art to vector images. We have implemented all stages of this procedure except for the b-spline optimization step. We are getting a slower running time than the paper, and our output matches it almost perfectly. 82 | 83 | \textcolor{myrust}{\section{About the paper}} 84 | 85 | \textcolor{myrust}{\subsection{What is pixel art?}} 86 | 87 | Pixel art is the name given to a style of art that was popular in older game consoles of the 80s and 90s. It can be summed up as ``very-low-resolution raster art" The distinguishing feature of pixel art is that each pixel is placed by hand, by a skilled artist. This is in contrast to other forms of digital art, where pixel-level granularity is not common.\\ 88 | 89 | The features of pixel art which made it suitable for such devices were its low memory usage and ability to convey a lot of information through the use of very few pixels. Character sprites in games built for these consoles were typically 10-80 pixels tall and 10-40 pixels wide. Even at such a small size, the art looked very good.\\ 90 | 91 | \centerline{\includegraphics[scale=4]{../../img/smw_bowser}} 92 | 93 | The figure above is a sprite from Super Mario World, scaled 4x. Its actual size is 49 by 39 pixels. 94 | 95 | \textcolor{myrust}{\subsection{Contributions of the paper}} 96 | 97 | The paper proposes a method to obtain a vector representation of a given pixel art image. The biggest advantage of using a vector representation, (apart from preferring vector art for aesthetic reasons) is that vector representations are scale-independent. This is useful, because usually in a game using pixel art, the sprites and textures are scaled (almost always using nearest-neighbour interpolation) to 2x, 3x or 4x. With a vector representation, we can use arbitrary scales, like 2.5x, whereas nearest-neighbour tends to distort images when the scale value is not an integer. 98 | 99 | \textcolor{myrust}{\subsection{Competing algorithms}} 100 | 101 | There are two categories of algorithms that compete with the algorithm presented in this paper - pixel art upscaling algorithms, and raster-to-vector conversion algorithms.\\ 102 | 103 | There already exist algorithms to intelligently upscale pixel art sprites and textures (as opposed to nearest-neighbour, which results in very blocky upscaled images), such as \textbf{hqx}, which comes in three variants depending on the scale - \textbf{hq2x}, \textbf{hq3x} and \textbf{hq4x}. The output obtained by Kopf and Lischinski is superior to that of hqx, which anyway only has three variants.\\ 104 | 105 | Many raster-to-vector converters also exist. However, a quick look at the paper's \href{http://research.microsoft.com/en-us/um/people/kopf/pixelart/supplementary/multi_comparison.html}{results} page makes it clear that only the pixel art upscalers are a good contender to the paper's algorithm. Raster-to-vector converters achieve pretty terrible results on the vast majority of the input images, which is perhaps to be expected since these algorithms were not designed with very-low-resolution input images in mind. With pixel art upscaling, the challenge is really due to the minimalist nature of the art. For example, in pixel art, it is not uncommon to represent a character's eye by a single pixel! 106 | 107 | \textcolor{myrust}{\section{Outline of the algorithm}} 108 | 109 | The algorithm proceeds in three stages, each stage gradually approaching the final result.\\ 110 | 111 | \begin{enumerate} 112 | 113 | \item In the first stage, we view the square pixels of the image as a tiling of the image. We then reshape these pixels into polygons using a simplification of a modified Voronoi diagram. This is basically a re-tiling of the image. By doing this, we hope to approximate visual features of the image via appropriate polygons. This is the \textbf{Voronoi stage}. 114 | 115 | \item In the second stage, we identify edge sequences within the Voronoi diagram and using them as control polygons, fit quadratic b-splines. (In our implementation, we use cubic b-splines). This smoothens some of the blocky features of the Voronoi diagram. At the end of this stage, every visible edge has been fitted with a b-spline. This is the \textbf{spline extraction stage}. 116 | 117 | \item In the third stage, we optimize the b-splines to reduce staircasing artifacts. This step can lead to over-smoothing, so the authors correct that by using corner detection to avoid optimizing certain patterns of control points. This is the \textbf{spline optimization stage}. 118 | 119 | \end{enumerate} 120 | 121 | Finally, each b-spline is rendered by discrete sampling, and vector-rendering techniques are used to render the final image. Since b-splines are continuous curves, they are resolution-independent, which makes them a good vector representation. The only thing that needs to change while rendering at higher resolutions is the frequency with which we sample each spline.\\ 122 | 123 | Each of these stages consists of some sub-stages. 124 | 125 | \textcolor{myrust}{\subsection{Voronoi stage}} 126 | 127 | In this stage, each square pixel is converted into a polygon. To do so, we first create a graph called the \textbf{similarity graph}. It consists of nodes arranged in a $R \times C$ mesh, each node corresponding to a pixel in the input image. There is an edge between two nodes iff\\ 128 | 129 | \begin{itemize} 130 | 131 | \item They are either horizontally adjacent, vertically adjacent or diagonally adjacent, \textbf{and} 132 | \item The difference in Y, U or V values is less than $\frac{48}{255}$, $\frac{7}{255}$ or $\frac{6}{255}$ respectively. 133 | 134 | \end{itemize} 135 | 136 | We take this similarity graph and prune away edges until the resulting graph is planar. The exact details are mentioned in the paper. The interesting part of this step is when we have four nodes in a square configuration, say, A, B, C and D, with A joined to C and B joined to D, but no other connections exist among these four nodes. In order to make the graph planar, we must remove one of AC and BD. To do so, the authors propose three heuristics - the \textbf{curves} heuristic (implemented via a double unidirectional search), the \textbf{sparse} heuristic (implemented via a window-bounded depth first search) and the \textbf{islands} heuristic (implemented via a simple if-check that uses an empirically-determined constant).\\ 137 | 138 | Once we have a planar similarity graph, we could obtain a generalized Voronoi diagram in which each Voronoi cell contains the points that are closest to a node and the union of its half-edges. We would then consider all vertices of Voronoi cells that have exactly two outgoing edges in the Voronoi diagram, and remove all such vertices to obtain a simplified Voronoi diagram.\\ 139 | 140 | The authors observed, however, that the simplified Voronoi diagram can be computed in one step, by noting that there are only a finite number of locations where a point can exist in the simplified Voronoi diagram. In general, if the centre of a node has the coordinates $(x, y)$, then the vertices of the Voronoi cell must be located at $(a, b)$, where $a = x \pm 0.25$, $a = x \pm 0.5$ or $a = x \pm 0.75$ (and similarly for $b$).\\ 141 | 142 | At the end of this stage, we have obtained the \textbf{Voronoi re-tiling} of the original input image. It's worth noting that at this stage itself, the output looks pretty good. \href{https://www.youtube.com/watch?v=n4UP7V_Ev0g}{We obtained this result upon running the algorithm upto only the Voronoi stage on a scene from Super Mario World}. 143 | 144 | \textcolor{myrust}{\subsection{Spline extraction stage}} 145 | 146 | In this stage, we identify \textbf{visible edge sequences}, and replace each one with a b-spline for whom the control polygon is the edge sequence itself. A \textbf{visible edge} is defined as an edge in the Voronoi diagram which separates dissimilar pixels (dissimilar according to the YUV criterion above). A visible edge sequence of length $k$ is a sequence of points $p_1, p_2, ..., p_{k+1}$ such that $p_i$ and $p_{i+1}$ are connected by a visible edge for $1 \le i \le k$. Also, $p_i$ should have exactly two outgoing visible edges for $1 < i \le k$ while $p_1$ and $p_{k+1}$ should \textit{not} have valence 2. Note that $p_1 = p_{k+1}$ is possible, i.e. the visible edge sequence is a cycle.\\ 147 | 148 | Having identified visible edge sequences, we consider every point in the Voronoi diagram which is at the junction of three visible edge sequences. We then merge two of the incident visible edge sequences using heuristics described in the paper (this involves subclassifying visible edges as \textbf{shading} edges or \textbf{contour} edges based on how dissimilar the corresponding pixels are). 149 | 150 | \textcolor{myrust}{\subsection{Spline optimization stage}} 151 | 152 | In this stage, we optimize the control points of each b-spline curve by minimizing an associated energy function. The aim is to reduce staircasing artifacts. 153 | 154 | \textcolor{myrust}{\section{What we have implemented}} 155 | 156 | We have implemented all stages of the paper except spline optimization and vector rendering due to time constraints. All code was written from scratch, without using any external libraries, except for the b-spline fitting, which uses the \textbf{scipy} package. The paper was implemented purely in \textbf{Python}. For scripting, we used \textbf{Python}, \textbf{Ruby} and \textbf{Java}. For reading in images, we use Python's built-in image processing library \textbf{PIL}. 157 | 158 | \textcolor{myrust}{\section{Dataset}} 159 | 160 | The authors of the paper have demonstrated their algorithm on 54 pixel art sprites, as well as on each individual frame of a gameplay sequence from Super Mario World (as mentioned above) on \href{http://research.microsoft.com/en-us/um/people/kopf/pixelart/supplementary/comparison\_bicubic.html}{this page}. We have used these images as our dataset. 161 | 162 | \textcolor{myrust}{\section{Some Outputs}} 163 | 164 | % \centerline{\includegraphics{}} 165 | % \centerline{\includegraphics{}} 166 | % \centerline{\includegraphics{}} 167 | % \centerline{\includegraphics{}} 168 | 169 | \textcolor{myrust}{\section{Difficulties}} 170 | 171 | \textcolor{myrust}{\section{Future work}} 172 | 173 | \textcolor{myrust}{\subsection{Performance}} 174 | 175 | Although our Voronoi output matches the paper's final output to a large extent, there is a lot of scope for improvement. Our code is completely unoptimized. For each frame from the Mario video (there were 1397 in total), our code takes about 1 second to render the Voronoi output. This can be made much faster. Here are some things we can do to improve performance:\\ 176 | 177 | \begin{enumerate} 178 | \item Optimize some of the algorithms, both in terms of memory consumption and running-time. 179 | \item Rewrite the codebase in a faster language, like \textbf{C}\verb!++! or \textbf{Rust}. We have already ported part of the inital codebase to \textbf{C}\verb!++!. 180 | \item Currently, we are using a simple but very na{\"i}ve method for saving the output - we hide the display window after opening it, call \texttt{glReadPixels()} to capture the window data into a image data buffer, then use PIL functions to save the image data buffer to a \texttt{png} image. Essentially, we are rendering into the Default Backbuffer and then displaying it and hiding it, whereas we could instead render into an Framebuffer Object which could then be directly written to an image file. 181 | \end{enumerate} 182 | 183 | \textcolor{myrust}{\subsection{Extensions to the paper}} 184 | 185 | \textcolor{myrust}{\subsection{Emulator}} 186 | 187 | \textcolor{myrust}{\subsection{Future improvements}} 188 | 189 | %%%%%%%%%%%%%%%%%%%%%%5 190 | 191 | 192 | 193 | \textcolor{myrust}{\section{Validation strategy}} 194 | 195 | To evaluate any pixel art scaling algorithm we must use visual inspection, since the aim of the paper is to present an algorithm that creates aesthetic and good-looking vector art from pixel art. Some factors to consider while visually inspecting two vector art outputs are as follows:\\ 196 | 197 | \begin{enumerate} 198 | \itemsep-0.25em 199 | \item An image with less jagged edges and less blockiness is better. For example, the image on the left is better:\\ 200 | \item An image which is not overly smoothed is better. For example, the image on the right is better:\\ 201 | \item An image which does not have ``incorrect islands" is better. An ``incorrect island" is when an output vector art image contains a small section cut-off from a larger body even though the intention of the pixel artist was to have a single continguous body. For example, the image on the left is better:\\ 202 | \end{enumerate} 203 | 204 | We can sum up the above heuristics by saying that in general, our overall aim is to see how close the vector output is to the intention of the pixel artist. The closer it is, the better the vector output. 205 | 206 | \textcolor{myrust}{\section{Deliverables}} 207 | 208 | After we implement the algorithm as described in the paper, we will tune the parameters of the algorithm so as to obtain outputs better than hq4x on most or all of the input images. We do not expect to surpass the output of the paper itself, however, we will try to decrease the running time. 209 | 210 | \end{document} 211 | 212 | %% fronto parallel only lel 213 | %% emulators 214 | %% cpp or rust --------------------------------------------------------------------------------