@bhugueney/

rotate ppm in python

Python

No description

fork
loading
Files
  • main.py
  • Aerial.512-15.ppm
  • Aerial.512.ppm
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import math
import time
import sys

def readPPM(file):
    """ -> nested list of lists of tuples 
    [nRows][nCols](r,g,b)
    """
    header= file.readline()
    if header != b'P6\n' :
        raise RuntimeError('bad file format :'+header)
    # TODO should skip comments
    line = file.readline().decode()
    (w, h)= line.split()
    (w, h)= (int(w), int(h))
    max_col_val= file.readline() # we don't use max_col_val, assuming 255
    img=[]
    for r in range(h):
        row= []
        for c in range(w):
            row.append(tuple(file.read(3)))
        img.append(row)
    return img

def writePPM(file, img):
    """
    """
    file.write(bytearray('P6\n'+
                         str(len(img[0]))+
                         ' '+str(len(img))
                         +'\n255\n'
                         , 'ascii'))
    for row in img:
        for rgb in row:
            file.write(bytes(rgb))
    return None

def rotate(angle, xy):
    (x, y)= xy
    return (x*math.cos(angle) - y*math.sin(angle)
            , y*math.cos(angle) + x*math.sin(angle))

def add(xy_1, xy_2):
    (x_1, y_1)= xy_1
    (x_2, y_2)= xy_2
    return (x_1+x_2, y_1+y_2)

def scale(k, xy):
    (x, y)= xy
    return (k*x, k*y)

def rotate_around(angle, xy_c, xy):
    return add(xy_c, rotate(angle, add(scale(-1, xy_c), xy)))

def update_min_max(xmin_xmax_ymin_y_max, xy):
    (x, y)= xy
    return (min(xmin_xmax_ymin_y_max[0], x),
            max(xmin_xmax_ymin_y_max[1], x),
            min(xmin_xmax_ymin_y_max[2], y),
            max(xmin_xmax_ymin_y_max[3], y))

def rotate_img(angle, src):
    start= time.perf_counter()
    w_src= len(src[0])
    h_src= len(src)
    xy_c_src= scale(0.5, (w_src, h_src))
    src_to_dest= lambda xy : rotate_around(-angle, xy_c_src, xy)
    (x_min_dest, x_max_dest, y_min_dest, y_max_dest)= update_min_max((math.inf, -math.inf, math.inf, -math.inf)
                                                                     ,src_to_dest((0,0)))
    (x_min_dest, x_max_dest, y_min_dest, y_max_dest)= update_min_max((x_min_dest, x_max_dest, y_min_dest, y_max_dest)
                                                                     ,src_to_dest((w_src,0)))
    (x_min_dest, x_max_dest, y_min_dest, y_max_dest)= update_min_max((x_min_dest, x_max_dest, y_min_dest, y_max_dest)
                                                                     ,src_to_dest((w_src,h_src)))
    (x_min_dest, x_max_dest, y_min_dest, y_max_dest)= update_min_max((x_min_dest, x_max_dest, y_min_dest, y_max_dest)
                                                                     ,src_to_dest((0, h_src)))
    w_dest= int(x_max_dest - x_min_dest)
    h_dest= int(y_max_dest - y_min_dest)
    is_in_src= lambda r, c : (0 <= r < h_src) and (0 <= c < w_src)
    default_color= (0, 0, 0)
    dest=[]
    for r_dest in range(h_dest):
        dest.append([])
        for c_dest in range(w_dest):
            (c_src, r_src)= rotate_around(angle, xy_c_src, (c_dest+x_min_dest, r_dest + y_min_dest))
            (c_src, r_src)= (int(c_src), int(r_src))
            dest[-1].append(src[r_src][c_src] if is_in_src(r_src, c_src) else default_color)
    end= time.perf_counter()
    print("rotation of "+str(w_src)+"x"+str(h_src)+
          " to "+str(w_dest)+"x"+str(h_dest)+" in "+str(end-start)+"s.",
          file= sys.stderr)
    return dest

def process(argv):
    try:
        angle= math.pi*float(argv[1])/180 if len(argv) > 1 else math.pi/4
        input_file= argv[2] if len(argv) > 2 else sys.stdin.fileno()
        output_file= argv[3] if len(argv) > 3 else sys.stdout.fileno()
        with open(input_file, 'rb') as input, open(output_file, 'wb') as output:
            writePPM(output, rotate_img(angle, readPPM(input)))  
    except Exception as e:
        print("exception "+ str(type(e)) +" caught: "+str(e.args),
              file=sys.stderr)
    
if __name__ == '__main__':
    process(["main.py", "15", "Aerial.512.ppm","Aerial.512-15.ppm"]) #process(sys.argv)