forked from Gil-Mor/iFish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfish.py
115 lines (89 loc) · 4.26 KB
/
fish.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
107
108
109
110
111
112
113
114
115
import imageio
import numpy as np
from math import sqrt
import sys
import argparse
import os
def get_fish_xn_yn(source_x, source_y, radius, distortion):
"""
Get normalized x, y pixel coordinates from the original image and return normalized
x, y pixel coordinates in the destination fished image.
:param distortion: Amount in which to move pixels from/to center.
As distortion grows, pixels will be moved further from the center, and vice versa.
"""
if 1 - distortion*(radius**2) == 0:
return source_x, source_y
return source_x / (1 - (distortion*(radius**2))), source_y / (1 - (distortion*(radius**2)))
def fish(img, distortion_coefficient):
"""
:type img: numpy.ndarray
:param distortion_coefficient: The amount of distortion to apply.
:return: numpy.ndarray - the image with applied effect.
"""
# If input image is only BW or RGB convert it to RGBA
# So that output 'frame' can be transparent.
w, h = img.shape[0], img.shape[1]
if len(img.shape) == 2:
# Duplicate the one BW channel twice to create Black and White
# RGB image (For each pixel, the 3 channels have the same value)
bw_channel = np.copy(img)
img = np.dstack((img, bw_channel))
img = np.dstack((img, bw_channel))
if len(img.shape) == 3 and img.shape[2] == 3:
print("RGB to RGBA")
img = np.dstack((img, np.full((w, h), 255)))
# prepare array for dst image
dstimg = np.zeros_like(img)
# floats for calcultions
w, h = float(w), float(h)
# easier calcultion if we traverse x, y in dst image
for x in range(len(dstimg)):
for y in range(len(dstimg[x])):
# normalize x and y to be in interval of [-1, 1]
xnd, ynd = float((2*x - w)/w), float((2*y - h)/h)
# get xn and yn distance from normalized center
rd = sqrt(xnd**2 + ynd**2)
# new normalized pixel coordinates
xdu, ydu = get_fish_xn_yn(xnd, ynd, rd, distortion_coefficient)
# convert the normalized distorted xdn and ydn back to image pixels
xu, yu = int(((xdu + 1)*w)/2), int(((ydu + 1)*h)/2)
# if new pixel is in bounds copy from source pixel to destination pixel
if 0 <= xu and xu < img.shape[0] and 0 <= yu and yu < img.shape[1]:
dstimg[x][y] = img[xu][yu]
return dstimg.astype(np.uint8)
def parse_args(args=sys.argv[1:]):
"""Parse arguments."""
parser = argparse.ArgumentParser(
description="Apply fish-eye effect to images.",
prog='python3 fish.py')
parser.add_argument("-i", "--image", help="path to image file."
" If no input is given, the supplied example 'grid.jpg' will be used.",
type=str, default="grid.jpg")
parser.add_argument("-o", "--outpath", help="file path to write output to."
" format: <path>.<format(jpg,png,etc..)>",
type=str, default="fish.png")
parser.add_argument("-d", "--distortion",
help="The distoration coefficient. How much the move pixels from/to the center."
" Recommended values are between -1 and 1."
" The bigger the distortion, the further pixels will be moved outwars from the center (fisheye)."
" The Smaller the distortion, the closer pixels will be move inwards toward the center (rectilinear)."
" For example, to reverse the fisheye effect with --distoration 0.5,"
" You can run with --distortion -0.3."
" Note that due to double processing the result will be somewhat distorted.",
type=float, default=0.5)
return parser.parse_args(args)
if __name__ == "__main__":
args = parse_args()
try:
imgobj = imageio.imread(args.image)
except Exception as e:
print(e)
sys.exit(1)
if os.path.exists(args.outpath):
ans = input(
args.outpath + " exists. File will be overridden. Continue? y/n: ")
if ans.lower() != 'y':
print("exiting")
sys.exit(0)
output_img = fish(imgobj, args.distortion)
imageio.imwrite(args.outpath, output_img, format='png')