├── 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 | ![example](https://user-images.githubusercontent.com/49881189/95275496-4d77c480-0851-11eb-8871-a75ffc8254ca.png) 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 | ![example2](https://user-images.githubusercontent.com/49881189/95272140-4ef0bf00-0848-11eb-8f5a-83d89df3c4ef.png) 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 | --------------------------------------------------------------------------------