├── LICENSE
├── README.md
└── main.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Kiriakos Gavras
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python-Image-Upscaler with OpenCV and Numpy
2 | A python image upscaler using different interpolation algorithms. I'm using OpenCV to read-write the image and Numpy to create an array of uint8 with the new image dimensions.
3 |
4 | ## Prerequisites
5 | * **Python**
6 | * **OpenCV**
7 | * **Numpy**
8 |
9 | ## How to use
10 | Give 3 arguments. A number to scale the image, the image's name and the interpolation technique.
11 | Here's an example:
12 |
13 | ```python main.py 2 coffe.jpg bilinear```
14 |
15 | This will save an image named *newcoffe.jpg* (you can change that in the code) two times larger with bilinear interpolation.
16 |
17 | ### Restraints:
18 | Scale must be: **2 to MAXINT** (don't go big here :P).
19 | Image name: You could also give a full path, though you should change the save path in the code if so.
20 | Interpolation technique: **bilinear** or **nearest**, will do bicubic in the future.
21 |
22 | If you get confused type ```python main.py -h``` for help.
23 |
24 | ## Examples
25 | There are three step in bilinear.
26 | 1. Map the pixels to your new image
27 | 2. Interpolate rows.
28 | 3. Interpolate columns.
29 | 
30 |
31 |
32 | **Below you can see the differences between nearest neighbor and bilinear interpolation. In the *nearest* you get the color of the closest pixel, and in *bilinear* a rate of a pixel's color depending on how far away it is, hence the smoothing effect.**
33 | 
34 |
35 |
36 | ## References
37 | [Resizing Images - Computerphile](https://www.youtube.com/watch?v=AqscP7rc8_M)
38 | [Wikipedia - Bilinear Interpolation](https://en.wikipedia.org/wiki/Bilinear_interpolation)
39 | [Closest Neighbor Interpolation](https://www.imageeprocessing.com/2017/11/nearest-neighbor-interpolation.html)
40 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import cv2
3 | import math
4 | import argparse
5 |
6 | def pixelMapping(img, scale):
7 | pixelX = 0
8 | pixelY = 0
9 | resizedHeight = height * scale
10 | resizedWidth = width * scale
11 | resized = np.zeros((resizedHeight, resizedWidth, 3), np.uint8)
12 |
13 | for x in range(resizedWidth):
14 | for y in range(resizedHeight):
15 | if pixelX * scale == x and pixelY * scale == y:
16 | resized[y, x] = img[pixelY, pixelX]
17 | pixelY += 1
18 | black = False
19 | else:
20 | if y == 0:
21 | black = True
22 | break
23 | resized[y, x] = [0, 0, 0]
24 | if not black:
25 | pixelX += 1
26 | pixelY = 0
27 | return resized
28 |
29 | def bilinearInterpolation(img, step):
30 | x1 = 0
31 | x2 = step
32 | left = img[0,0]
33 | lb,lg,lr = left
34 |
35 | right = img[0, x2]
36 | rb,rg,rr = right
37 |
38 | height, width, _ = img.shape
39 |
40 | # Interpolate rows
41 | for y in range(height):
42 | if y % step == 0:
43 | for x in range(1, width):
44 | if x % step != 0:
45 | b = ((x2 - x) / (x2 - x1)) * lb
46 | g = ((x2 - x) / (x2 - x1)) * lg
47 | r = ((x2 - x) / (x2 - x1)) * lr
48 |
49 | b += ((x - x1) / (x2 - x1)) * rb
50 | g += ((x - x1) / (x2 - x1)) * rg
51 | r += ((x - x1) / (x2 - x1)) * rr
52 |
53 | img[y, x] = [b, g, r]
54 | else:
55 | x1 = x2
56 | x2 += step
57 | if x2 == width:
58 | break
59 |
60 | left = img[y, x]
61 | lb,lg,lr = left
62 |
63 | right = img[y, x2]
64 | rb,rg,rr = right
65 | x1 = 0
66 | x2 = step
67 |
68 | left = img[0,0]
69 | lb,lg,lr = left
70 |
71 | right = img[x2, 0]
72 | rb,rg,rr = right
73 |
74 | #Interpolate columns
75 | for x in range(width):
76 | for y in range(1,height):
77 | if y % step != 0:
78 | b = ((x2 - y) / (x2 - x1)) * lb
79 | g = ((x2 - y) / (x2 - x1)) * lg
80 | r = ((x2 - y) / (x2 - x1)) * lr
81 |
82 | b += ((y - x1) / (x2 - x1)) * rb
83 | g += ((y - x1) / (x2 - x1)) * rg
84 | r += ((y - x1) / (x2 - x1)) * rr
85 |
86 | img[y, x] = [b, g, r]
87 | else:
88 | x1 = x2
89 | x2 += step
90 | if x2 == height:
91 | break
92 |
93 | left = img[y, x]
94 | lb,lg,lr = left
95 |
96 | right = img[x2, x]
97 | rb,rg,rr = right
98 | x1 = 0
99 | x2 = step
100 |
101 | return img
102 |
103 | def nearestNeighboor(img, scale):
104 | resizedHeight = height * scale
105 | resizedWidth = width * scale
106 | resized = np.zeros((resizedHeight, resizedWidth, 3), np.uint8)
107 |
108 | RatioCol = height / resizedHeight
109 | RatioRow = width / resizedWidth
110 |
111 | for x in range(resizedWidth):
112 | for y in range(resizedHeight):
113 | resized[y,x] = img[math.ceil((y+1) * RatioCol) - 1, math.ceil((x+1) * RatioRow) - 1]
114 | return resized
115 |
116 |
117 | parser = argparse.ArgumentParser(description="Give input for scale, image and interpolation technique.")
118 | parser.add_argument("scale", type=int, help="And integer to scale up the image.")
119 | parser.add_argument("imageName", type=str, help="A string for the image file.")
120 | parser.add_argument("technique", type=str, help="A string for the interpolation technique.")
121 | args = parser.parse_args()
122 |
123 | img = cv2.imread(args.imageName)
124 | height, width, _ = img.shape
125 |
126 | def main():
127 |
128 | if args.technique == "nearest":
129 | resized = nearestNeighboor(img, args.scale)
130 |
131 | elif args.technique == "bilinear":
132 | resized = pixelMapping(img, args.scale)
133 | resized = bilinearInterpolation(resized, args.scale)
134 |
135 | cv2.imwrite("new" + args.imageName, resized)
136 |
137 | #===================================================================#
138 | # Comment imwrite ^^^ and uncomment the below lines for live preview
139 | #===================================================================#
140 |
141 | # cv2.namedWindow("image", cv2.WINDOW_NORMAL)
142 | # cv2.imshow("image", resized)
143 | # cv2.waitKey(0)
144 | # cv2.destroyAllWindows()
145 |
146 | if __name__ == "__main__":
147 | main()
148 |
--------------------------------------------------------------------------------