import sys from array import array import os import time import pickle import cv import numpy import gameduino import gameduino.prep as gdprep import gameduino.sim as gdsim import Image def pil2cv(pilim): cvrgb = cv.CreateMatHeader(pilim.size[1], pilim.size[0], cv.CV_8UC3) cvim = cv.CreateMat(pilim.size[1], pilim.size[0], cv.CV_8UC3) cv.SetData(cvrgb, pilim.tostring()) cv.CvtColor(cvrgb, cvim, cv.CV_RGB2BGR) return cvim def cv2pil(cvim): cvrgb = cv.CreateMat(cvim.rows, cvim.cols, cv.CV_8UC3) cv.CvtColor(cvim, cvrgb, cv.CV_BGR2RGB) return Image.fromstring("RGB", (cvim.cols, cvim.rows), cvrgb.tostring()) class Gameduino512(gdsim.Gameduino): def im512(self): im = Image.new("RGB", (800, 600)) spr = array('I', self.rdstr(gameduino.RAM_SPR, 256 * 4)) def setspr(xoff, yoff): spr2 = [] for i in range(256): cc = spr[i] x = (cc + xoff) & 511 y = ((cc >> 16) + yoff) & 511 spr2.append((cc & ~0x1ff01ff) | x | (y << 16)) self.wrstr(gameduino.RAM_SPR, array('I', spr2).tostring()) for x in [0, 400]: self.wr16(gameduino.SCROLL_X, x) for y in [0, 300]: self.wr16(gameduino.SCROLL_Y, y) setspr(-x, -y) im.paste(self.im(), (x, y)) setspr(0, 0) return im.crop((0, 0, 512, 512)) def scolorq(im, pal): if 0: open("rgb", "w").write(im.convert("RGB").tostring()) os.system("/u/jamesb/juiceboss/gameduino/tools/scolorq/spatial_color_quant rgb %d %d %d q.rgb" % (im.size[0], im.size[1], pal)) data = open("q.rgb").read() im = Image.fromstring("RGB", im.size, data) return gdprep.palettize(im, pal) def none(pilsrc, tgt = 255): im = pil2cv(pilsrc) xys = [(x,y) for y in range(0, im.rows, 8) for x in range(0, im.cols, 8)] sprites = [] nsprs = 0 sprpos = [] sprs = Image.new("RGB", (16, 16 * nsprs)) alldcts = {} for (x,y) in xys: t = cv.GetSubRect(im, (x, y, 8, 8)) yrb = cv.CreateMat(8, 8, cv.CV_8UC3) cv.CvtColor(t, yrb, cv.CV_BGR2YCrCb) yy = cv.CreateMat(8, 8, cv.CV_8UC1) cr = cv.CreateMat(8, 8, cv.CV_8UC1) cb = cv.CreateMat(8, 8, cv.CV_8UC1) cv.Split(yrb, yy, cr, cb, None) def dctl(im, w): dct = cv.CreateMat(8, 8, cv.CV_32FC1) cv.DCT((numpy.array(im) - 128.).astype(numpy.float32), dct, cv.CV_DXT_FORWARD) wdct = numpy.array(dct)[:,:] * w # * numpy.array([[1,2],[2,4]]) return list(wdct.flatten()) alldcts[(x,y)] = dctl(yy, 1) + dctl(cr, 0.5) + dctl(cb, 0.5) def selencode(): # encode the chars in list xys, return the GD and the Gameduino's image as a cvmat gd = Gameduino512() tiles = [cv.GetSubRect(im, (x, y, 8, 8)) for (x,y) in xys] #try: # pilim = Image.new("RGB", (im.cols, im.rows)) # for ((x,y), t) in zip(xys, tiles): # pilim.paste(cv2pil(t), (x,y)) # (picd,chrd,pald) = gdprep.encode(pilim) #except OverflowError: if 1: samples = numpy.array([alldcts[xy] for xy in xys]) labels = cv.CreateMat(len(xys), 1, cv.CV_32SC1) tc = (cv.CV_TERMCRIT_EPS, 0, 0.1) print 'kmeans', cv.KMeans2(samples, tgt, labels, tc, flags = cv.KMEANS_PP_CENTERS) labels = [int(labels[i,0]) for i in range(len(xys))] assert len(set(labels)) == tgt def haslabel(l): # which tiles got label l return [t for (t, i) in zip(tiles, labels) if (l == i)] def tilemean(tiles): s = 0 for t in tiles: s += numpy.array(t).astype(numpy.float32) s /= len(tiles) s = s.astype(numpy.uint8) s = pil2cv(scolorq(cv2pil(cv.fromarray(s)), 4).convert("RGB")) return s merged = [tilemean(haslabel(i)) for i in range(tgt)] out = cv.CreateMat(im.rows, im.cols, im.type) cv.SetZero(out) for i,(x,y) in enumerate(xys): cv.Copy(merged[labels[i]], cv.GetSubRect(out, (x,y,8,8))) pilim = cv2pil(out) (picd,chrd,pald) = gdprep.encode(pilim) print len(chrd) / 16, 'chars' gd.fill(gameduino.RAM_PIC, 0xff, 64 * 64) ls = im.cols / 8 for y in range(im.rows / 8): gd.wrstr(64 * y + gameduino.RAM_PIC, picd[ls*y:ls*y+ls]) gd.wrstr(gameduino.RAM_CHR, chrd) gd.wrstr(gameduino.RAM_PAL, pald) actual = pil2cv(gd.im().crop((0, 0, im.cols, im.rows))) return (gd, actual) while len(sprpos) < nsprs: print len(sprpos) (_, actual) = selencode() # Find the 16x16 with the biggest error def errorat(x, y): sig = cv.GetSubRect(im, (x, y, 16, 16)) act = cv.GetSubRect(actual, (x, y, 16, 16)) ad = cv.CreateMat(16, 16, cv.CV_8UC3) cv.AbsDiff(sig, act, ad) return sum(cv.Sum(ad)) for i in range(8): sprxys = [(x,y) for x in range(0, im.cols, 16) for y in range(0, im.rows, 16) if (x,y) in xys] (_, x, y) = max([(errorat(x, y), x, y) for (x,y) in sprxys]) del xys[xys.index((x,y))] del xys[xys.index((x+8,y))] del xys[xys.index((x,y+8))] del xys[xys.index((x+8,y+8))] sprs.paste(pilsrc.crop((x, y, x+16, y+16)), (0, len(sprpos) * 16)) sprpos.append((x,y)) gd,_ = selencode() ir = gdprep.ImageRAM(open("hdr.h", "w")) if 1: if nsprs: sprs = scolorq(sprs, 16) ir.addsprites("spr", (16, 16), sprs, gdprep.PALETTE16A) gd.wrstr(gameduino.PALETTE16A, gdprep.getpal(sprs)) gd.wrstr(gameduino.RAM_SPRIMG, ir.used()) # gd.fill(0, 0, 4096 + 4096 + 2048) for i,(x,y) in enumerate(sprpos): gd.sprite(i, x, y, i / 2, 4 + ((i & 1) << 1), 0) else: sprs = gdprep.palettize(sprs, 256) ir.addsprites("spr", (16, 16), sprs, gdprep.PALETTE256A) gd.wrstr(gameduino.RAM_SPRPAL, gdprep.getpal(sprs)) gd.wrstr(gameduino.RAM_SPRIMG, ir.used()) for i,(x,y) in enumerate(sprpos): gd.sprite(i, x, y, i, 0, 0) return gd if 0: for filename in ["lena288.png"]: # ["closeup.png", "pitch.png", "clone400.png", "crowd.png", "Kickstarter0.png", "photo-small.png", "gd.png"]: print filename gd = none(Image.open(filename)) open("%s-memdump" % filename, "w").write(gd.mem.tostring()) gd.im().save("out.png") KMEANS_PP_CENTERS = 4 def simple(pilsrc, tgt = 255): im = pil2cv(pilsrc) xys = [(x,y) for y in range(0, im.rows, 8) for x in range(0, im.cols, 8)] alldcts = {} for (x,y) in xys: t = cv.GetSubRect(im, (x, y, 8, 8)) yrb = cv.CreateMat(8, 8, cv.CV_8UC3) cv.CvtColor(t, yrb, cv.CV_BGR2YCrCb) yy = cv.CreateMat(8, 8, cv.CV_8UC1) cr = cv.CreateMat(8, 8, cv.CV_8UC1) cb = cv.CreateMat(8, 8, cv.CV_8UC1) cv.Split(yrb, yy, cr, cb, None) def dctl(im, w): dct = cv.CreateMat(8, 8, cv.CV_32FC1) src = (numpy.array(im) - 128.).astype(numpy.float32) cv.DCT(cv.fromarray(src), dct, cv.CV_DXT_FORWARD) wdct = numpy.array(dct)[:,:] * w # * numpy.array([[1,2],[2,4]]) return list(wdct.flatten()) alldcts[(x,y)] = dctl(yy, 1) + dctl(cr, 0.5) + dctl(cb, 0.5) gd = Gameduino512() tiles = [cv.GetSubRect(im, (x, y, 8, 8)) for (x,y) in xys] if 1: samples = numpy.array([alldcts[xy] for xy in xys]) labels = cv.CreateMat(len(xys), 1, cv.CV_32SC1) tc = (cv.CV_TERMCRIT_ITER, 10, 0.0) print 'kmeans', cv.KMeans2(cv.fromarray(samples), tgt, labels, tc, flags = 2) labels = [int(labels[i,0]) for i in range(len(xys))] # assert len(set(labels)) == tgt def haslabel(l): # which tiles got label l return [t for (t, i) in zip(tiles, labels) if (l == i)] def tilemean(tiles): if len(tiles): s = 0 for t in tiles: s += numpy.array(t).astype(numpy.float32) s /= len(tiles) s = s.astype(numpy.uint8) s = pil2cv(scolorq(cv2pil(cv.fromarray(s)), 4).convert("RGB")) return s else: return pil2cv(Image.new("RGB", (8,8))) print len(set(labels)), "unique tiles" merged = [tilemean(haslabel(i)) for i in range(tgt)] out = cv.CreateMat(im.rows, im.cols, im.type) cv.SetZero(out) for i,(x,y) in enumerate(xys): cv.Copy(merged[labels[i]], cv.GetSubRect(out, (x,y,8,8))) pilim = cv2pil(out) (picd,chrd,pald) = gdprep.encode(pilim) return (picd,chrd,pald) def preview(imsz,picd,chrd,pald): gd = gdsim.Gameduino() gd.fill(gameduino.RAM_PIC, 0xff, 64 * 64) ls = im.size[0] / 8 for y in range(im.size[1] / 8): gd.wrstr(64 * y + gameduino.RAM_PIC, picd[ls*y:ls*y+ls]) gd.wrstr(gameduino.RAM_CHR, chrd) gd.wrstr(gameduino.RAM_PAL, pald) return gd.im() if __name__ == "__main__": im = Image.open("titanic.png") # im = im.resize((40 * 8,33 * 8), Image.LINEAR) t0 = time.time() (picd,chrd,pald) = simple(im, 255) # pickle.dump((picd,chrd,pald), open("p%04d.pickle" % i, "w")) print 'took', time.time() - t0 # open("%s-memdump" % filename, "w").write(gd.mem.tostring()) preview(im.size, picd,chrd,pald).crop((0,0,im.size[0],im.size[1])).save("out.png")