cross-stitch

Interactively turn images into patterns for cross stitching
git clone git://src.adamsgaard.dk/cross-stitch
Log | Files | Refs

commit d37e43fe9fd4de97dbbe3164bb52d209bb210ab4
parent b3fae888664cc0e16207fce99a9db032c92454d3
Author: Anders Damsgaard <anders.damsgaard@geo.au.dk>
Date:   Fri, 14 Feb 2014 20:14:07 +0100

Begun implementing GUI

Diffstat:
DREADME.rst | 64----------------------------------------------------------------
Mcross-stitch.py | 161++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mfisker-pattern.png | 0
3 files changed, 115 insertions(+), 110 deletions(-)

diff --git a/README.rst b/README.rst @@ -1,64 +0,0 @@ -cross-stitch -============ - -A Python application to turn your images into patterns for cross stitching. - -https://github.com/anders-dc/cross-stitch - -Requirements ------------- -Python 2 or 3, Numpy, Scipy, and Matplotlib. - -To install these dependencies in Debian and its derivatives, run: - - $ sudo apt-get install python python-numpy python-scipy python-matplotlib - -License -------- -GNU Public License version 3 or newer. See LICENSE.txt for details. - -Author ------- -Anders Damsgaard (andersd@riseup.net) - -Todo ----- -Add color processing functions to enhance colors and limit the number of colors. -Show product names of needed yarn colors. - -Usage ------ - - usage: cross-stitch.py [-h] --infile file --outfile file [--width WIDTH] - [--ncolors NCOLORS] - -Downsamples and modifies an image in order to create a pattern for cross -stitching. - -optional arguments: - -h, --help show this help message and exit - --infile file, -i file - input image to process - --outfile file, -o file - save processed image as file - --width WIDTH, -w WIDTH - canvas width, default value = 20 - --ncolors NCOLORS, -c NCOLORS - number of colors in output image, default value = 16 - -Example -------- - - $ ./cross-stitch.py -i fiskeren.jpg -o fisker-pattern.py -w 80 -c 16 - -.. image:: fiskeren.jpg - :scale: 50 % - :alt: Original image - :align: center - -.. image:: fisker-pattern.png - :scale: 60 % - :alt: Cross stitching pattern - :align: center - - diff --git a/cross-stitch.py b/cross-stitch.py @@ -6,52 +6,121 @@ import scipy.ndimage import scipy.misc import scipy.cluster import numpy as np +import matplotlib import matplotlib.pyplot as plt +import wx +class CrossStitch: -## Process input arguments ##################################################### -program_description = \ - '''Downsamples and modifies an image in order to create a pattern for - cross stitching.''' -parser = argparse.ArgumentParser(description = program_description) -parser.add_argument('--infile', '-i', metavar='file', type=str, nargs=1, - required=True, help='input image to process') -parser.add_argument('--outfile', '-o', metavar='file', type=str, nargs=1, - required=True, help='save processed image as file') -parser.add_argument('--width', '-w', type=int, nargs=1, default=20, - help='canvas width, default value = 20') -parser.add_argument('--ncolors', '-c', type=int, nargs=1, default=16, - help='number of colors in output image, default value = 16') -args = parser.parse_args() -infile = args.infile[0] -outfile = args.outfile[0] -width = args.width[0] -ncolors = args.ncolors - -## Read image ################################################################## -try: - im = scipy.ndimage.imread(infile) -except IOError: - sys.stderr.write('could not open input file "' + infile + '"\n') - sys.exit(1) - -## Downsampling ################################################################ -hw_ratio = float(im.shape[0])/im.shape[1] -new_size = (int(round(hw_ratio*width)), width) -im_small = scipy.misc.imresize(im, new_size) - -## Process image colors ######################################################## -ar = im_small.reshape(scipy.product(im_small.shape[:2]), im_small.shape[2]) -colors, dist = scipy.cluster.vq.kmeans(ar, ncolors) -c = ar.copy() -vecs, dist = scipy.cluster.vq.vq(ar, colors) -for i, color in enumerate(colors): - c[scipy.r_[scipy.where(vecs==i)],:] = color -im_small_reduced = c.reshape(new_size[0], new_size[1], 3) - -## Generate output plot ######################################################## -fig = plt.figure() -imgplot = plt.imshow(im_small_reduced) -imgplot.set_interpolation('nearest') -plt.grid() -plt.savefig(outfile) + def __init__(self): + pass + + def read_image(self, infile): + try: + self.img = scipy.ndimage.imread(infile) + except IOError: + sys.stderr.write('could not open input file "' + infile + '"\n') + + def down_sample(self, width): + hw_ratio = float(self.img.shape[0])/self.img.shape[1] + size = (int(round(hw_ratio*width)), width) + self.img = scipy.misc.imresize(self.img, size) + self.orig_img = self.img.copy() + + def limit_colors(self, ncolors): + ar = self.img.reshape(scipy.product(self.img.shape[:2]),\ + self.img.shape[2]) + self.colors, dist = scipy.cluster.vq.kmeans(ar, ncolors) + tmp = ar.copy() + vecs, dist = scipy.cluster.vq.vq(ar, self.colors) + for i, color in enumerate(self.colors): + tmp[scipy.r_[scipy.where(vecs == i)],:] = color + self.img = tmp.reshape(self.img.shape[0], self.img.shape[1], 3) + + def save_image(self, filename): + fig = plt.figure() + imgplot = plt.imshow(self.img) + imgplot.set_interpolation('nearest') + plt.grid() + plt.savefig(filename) + + +class MainScreen(wx.Frame): + + def __init__(self, *args, **kwargs): + super(MainScreen, self).__init__(*args, **kwargs) + self.InitUI() + self.cs = CrossStitch() + + def InitUI(self): + + self.InitMenu() + #self.InitToolbar() + + self.SetSize((600, 400)) + self.SetTitle('Main menu') + self.Centre() + self.Show(True) + + def InitMenu(self): + + menubar = wx.MenuBar() + + fileMenu = wx.Menu() + fitem = fileMenu.Append(wx.ID_OPEN, 'Open', 'Open image') + self.Bind(wx.EVT_MENU, self.OnOpen, fitem) + fitem = fileMenu.Append(wx.ID_SAVE, 'Save', 'Save image') + self.Bind(wx.EVT_MENU, self.OnSave, fitem) + fileMenu.AppendSeparator() + fitem = fileMenu.Append(wx.ID_EXIT, 'Quit', 'Quit application') + self.Bind(wx.EVT_MENU, self.OnQuit, fitem) + menubar.Append(fileMenu, '&File') + + processingMenu = wx.Menu() + fitem = processingMenu.Append(wx.ID_ANY, 'Down sample', + 'Down sample image') + self.Bind(wx.EVT_MENU, self.OnDownSample, fitem) + fitem = processingMenu.Append(wx.ID_ANY, 'Reduce number of colors', + 'Reduce number of colors in image') + self.Bind(wx.EVT_MENU, self.OnLimitColors, fitem) + + menubar.Append(processingMenu, '&Image processing') + + + self.SetMenuBar(menubar) + + def InitToolbar(self): + + toolbar = self.CreateToolBar() + qtool = toolbar.AddLabelTool(wx.ID_EXIT, 'Quit', wx.Bitmap('textit.png')) + self.Bind(wx.EVT_TOOL, self.OnQuit, qtool) + + toolbar.Realize() + + + + def OnQuit(self, e): + self.Close() + + def OnOpen(self, e): + self.cs.read_image("fiskeren.jpg") + + def OnSave(self, e): + self.cs.save_image("fisker-pattern.png") + + def OnDownSample(self, e): + self.cs.down_sample(80) + + def OnLimitColors(self, e): + self.cs.limit_colors(16) + + + + +def main(): + app = wx.App() + MainScreen(None, title='Cross Stitch') + app.MainLoop() + +if __name__ == '__main__': + main() diff --git a/fisker-pattern.png b/fisker-pattern.png Binary files differ.