File:Arnold's Cat Map animation (74px).gif

Arnold's_Cat_Map_animation_(74px).gif(74 × 74 pixels, file size: 2.29 MB, MIME type: image/gif, looped, 232 frames, 1 min 33 s)

Captions

Captions

Add a one-line explanation of what this file represents
Description
English: Animation of Arnold's cat map, using an image of cherries as the starting image. The image is 74 pixels square, and repeats after 114 iterations. Notice how the image sometimes contains superimposed cherries, many tiny cherries or a single perfectly reproduced but upside-down image.
Date
Source

Own work

Used File:Cherry Stella444.jpg as the starting image
Author Inductiveload
Permission
(Reusing this file)
w:en:Creative Commons
attribution share alike
This file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
You are free:
  • to share – to copy, distribute and transmit the work
  • to remix – to adapt the work
Under the following conditions:
  • attribution – You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
  • share alike – If you remix, transform, or build upon the material, you must distribute your contributions under the same or compatible license as the original.
Other versions

Python source code edit

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import Image #needs PIL for image handling
import ImageChops
import ImageFont
import ImageDraw

# checks if two images are equal
def equal(im1, im2):
    return ImageChops.difference(im1, im2).getbbox() is None

# add a text caption to an image (adds space at the bottom)
def addCaption(im, text, pointSize):
    size = im.size
    draw = ImageDraw.Draw(im)

    textFontLocation = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"
    topBottomMargin = 5
    
    textFont = ImageFont.truetype(textFontLocation ,pointSize)
    textSize = draw.textsize(text, font=textFont) # the size of the text box!
    
    newImage = Image.new(im.mode,\
            (im.size[0], im.size[1]+textSize[1]+2*topBottomMargin), "white" )
    newImage.paste( im, (0,0) )
    draw = ImageDraw.Draw(newImage)
    
    textX = (newImage.size[0] / 2.0) - (textSize[0] / 2.0)
    textY = newImage.size[1] - textSize[1] - topBottomMargin
    
    draw.text((textX, textY), text, fill="black", font=textFont)
    return newImage

# add a solid border to an image
def addBorder(im, color, thickness):
    newImage = Image.new(im.mode,\
            (im.size[0]+thickness*2, im.size[1]+thickness*2), color)
    newImage.paste( im, (1,1) )
    return newImage
    
# scale an image up or down (doesn't resample, so the pixels are clear)
def scaleImage(im, factor):
    newImage = Image.new(im.mode,\
            (im.size[0]*factor, im.size[1]*factor), "white" )
    newImage.paste( im.transform((im.size[0]*factor, im.size[1]*factor),\
            Image.AFFINE,\
            (1/float(factor),0,0,0,1/float(factor),0) ),\
            (0,0) )
    return newImage
    
def saveImages(im, iteration):
    im.save("Catmap%04d.png"%iteration) #save the simple image
    
    #resize image, add border and caption
    imageLarge = scaleImage(im, 3)
    imageLarge = addBorder(imageLarge, "black", 1)
    imageLarge = addCaption(imageLarge, str(iteration), 25)
    imageLarge.save("CatmapLargeNumbered%04d.png"%iteration)

# CONTROL STARTS HERE

inFile = "aa-catmap-orig.resized.jpg" #input image

image0 = Image.open(inFile, 'r')

#crop to square if required
if image0.size[0] != image0.size[1]:
    n = min(image0.size[0], image0.size[1] )
    image0 = image0.crop((0,0,n,n))
else:
    n = image0.size[0]

imageOrig = image0.copy() # keep to original image to see when we return to it

image1 = Image.new(image0.mode, (n,n)) # temp image to copy pixels to
pixels1 = image1.load()

# BEGIN THE CAT MAPPING PROCESS

iteration = 0
saveImages(image0, iteration) # save the first image
while True:
    pixels0 = image0.load() #reload the iterated image
    
    for x in range(n):# perform the mapping
        for y in range(n):
            newX = (2*x + y) % n #find new location
            newY = (x + y) % n
            pixels1[newX, newY] = pixels0[x, y] # copy the pixel over
    
    image0 = image1.copy() #transfer back to image0 for the next iteration
    iteration += 1
    saveImages(image0, iteration) # save this iteration's image

    if equal(image0, imageOrig):
        break

File history

Click on a date/time to view the file as it appeared at that time.

Date/TimeThumbnailDimensionsUserComment
current19:20, 15 September 2010Thumbnail for version as of 19:20, 15 September 201074 × 74 (2.29 MB)Inductiveload (talk | contribs){{Information |Description={{en|Animation of en:Arnold's cat map, using an image of cherries as the starting image. The image is 74 pixels square, and repeats after 114 iterations. Notice how the image sometimes contains superimposed cherries, many