├── .gitignore ├── README.md ├── samples ├── 106_NIKON-D3400-35MM_F.JPG ├── 106_NIKON-D3400-35MM_M.JPG ├── 106_NIKON-D3400-35MM_S.JPG ├── 176_HONOR-7X_F.jpg ├── 176_HONOR-7X_M.jpg ├── 176_HONOR-7X_S.jpg ├── 180_HONOR-10_F.jpg ├── 180_HONOR-10_M.jpg └── 180_HONOR-10_S.jpg └── scripts ├── generate_kernels.py ├── generate_motion_PSF.py ├── generate_trajectory.py └── postprocessing.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blur Dataset 2 | ## Description 3 | This dataset contains 1050 blurred and sharp images (350 triplets), each image triplet is a set of three photos of the same scene: sharp, defocused-blurred and motion-blurred images. 4 | 5 | The dataset was created to validate the blur detection algorithm. The dataset can also be used for testing image deblurring, hovewer, the triplets are not "pixel-to-pixel" images, so, one cannot compare blurred and sharp images on the basis of PSNR or SSIM but sharp images can be used for visual comparison. 6 | 7 | ## Dataset structure 8 | 9 | The dataset contains three folders: sharp, defocused-blurred and motion-blurred images. 10 | 11 | The filename structure is as follows: id_device_type.extension where 12 | - ID - a number from 0 to 349; 13 | - device - the image capture device; 14 | - type - one of [S, F, M]. S stands for Sharp image, F - deFocused-blurred image and M - Motion-blurred image. 15 | 16 | The dataset contains 66 devices, these are typically smartphones, but several cameras are also provided. 17 |
18 | List of devices: 19 |

20 | 21 | |Device | Amount| 22 | |-----------|-------| 23 | |HONOR-7X | 37 | 24 | |NIKON-D3400-18-55MM | 37 | 25 | |HONOR-8X | 30 | 26 | |IPHONE-SE | 30 | 27 | |NIKON-D3400-35MM | 25 | 28 | |XIAOMI-PROCOFONE-F1 | 23 | 29 | |IPHONE-7 | 13 | 30 | |IPHONE-6S | 11 | 31 | |XIAOMI-MI8-SE | 9 | 32 | |HONOR-10 | 8 | 33 | |ASUS-ZENFONE-LIVE-ZB501KL | 6 | 34 | |HONOR-7C | 6 | 35 | |HUAWEI-P20-LITE | 6 | 36 | |SONY-NEX-5T | 6 | 37 | |XIAOMI-REDMI-7 | 6 | 38 | |HUAWEI-P20 | 5 | 39 | |IPHONE-8-PLUS | 5 | 40 | |SAMSUNG-GALAXY-J3 | 5 | 41 | |HUAWEI-MATE20 | 4 | 42 | |HUAWEI-Y9 | 4 | 43 | |IPHONE-8 | 4 | 44 | |CANON-6D-100MM | 3 | 45 | |HONOR-9 | 3 | 46 | |HUAWEI-NOVA-LITE | 3 | 47 | |IPHONE-7-PLUS | 3 | 48 | |SAMSUNG-GALAXY-A8 | 3 | 49 | |SAMSUNG-GALAXY-J5 | 3 | 50 | |WILEYFOX-SWIFT-2-PLUS | 3 | 51 | |XIAOMI-REDMI-3S | 3 | 52 | |XIAOMI-REDMI-NOTE-7 | 3 | 53 | |HONOR-6X | 2 | 54 | |HUAWEI-P30-PRO | 2 | 55 | |ONEPLUS-3T | 2 | 56 | |SAMSUNG-GALAXY-A5 | 2 | 57 | |SAMSUNG-GALAXY-A6 | 2 | 58 | |SAMSUNG-GALAXY-J7 | 2 | 59 | |XIAOMI-REDMI-5-PLUS | 2 | 60 | |ASUS-ZE500KL | 1 | 61 | |BQ-5512L | 1 | 62 | |CANON-6D-70-200MM | 1 | 63 | |HONOR-4C | 1 | 64 | |HONOR-8 | 1 | 65 | |HONOR-9-LITE | 1 | 66 | |HUAWEI-P-SMART | 1 | 67 | |HUAWEI-P30 | 1 | 68 | |HUAWEI-P30-LITE | 1 | 69 | |IPHONE-5S | 1 | 70 | |IPHONE-6 | 1 | 71 | |IPHONE-XR | 1 | 72 | |LG-Q6 | 1 | 73 | |NOKIA-21 | 1 | 74 | |PANASONIC-DMC-TZ35 | 1 | 75 | |PRESTIGIO-MULTI-PHONE | 1 | 76 | |SAMSUNG-EDGE-7C | 1 | 77 | |SAMSUNG-GALAXY-7-NEO | 1 | 78 | |SAMSUNG-GALAXY-A3 | 1 | 79 | |SAMSUNG-GALAXY-GRAND-PRIME | 1 | 80 | |SAMSUNG-GALAXY-GRAND-PRIME-PLUS | 1 | 81 | |SAMSUNG-GALAXY-S5 | 1 | 82 | |SONY-XPERIA-E5 | 1 | 83 | |XIAOMI-MI8-LITE | 1 | 84 | |XIAOMI-REDMI-4 | 1 | 85 | |XIAOMI-REDMI-4A | 1 | 86 | |XIAOMI-REDMI-4X | 1 | 87 | |XIAOMI-REDMI-NOTE-4X | 1 | 88 | |XIAOMI-REDMI-NOTE-5A-PRIME | 1 | 89 |

90 |
91 | 92 | ## Download 93 | Kaggle dataset (images were scaled to 2048 pixels by the widest side): https://www.kaggle.com/kwentar/blur-dataset 94 | 95 | Google drive (source images): https://drive.google.com/open?id=1RObmCDPeQ1Lg-V6u7dT02Pf0qH-QMcTp 96 | 97 | Mirror: https://yadi.sk/d/_Wli3wSSScnzCg 98 | 99 | ## Samples 100 | 101 | |Sharp | Defocused-blurred | Motion-blurred | 102 | |-----------|-------|-------| 103 | |![106 Sharp](samples/106_NIKON-D3400-35MM_S.JPG)| ![106 Defocused](samples/106_NIKON-D3400-35MM_F.JPG)| ![106 Motion](samples/106_NIKON-D3400-35MM_M.JPG)| 104 | |![176 Sharp](samples/176_HONOR-7X_S.jpg)| ![176 Defocused](samples/176_HONOR-7X_F.jpg)| ![176 Motion](samples/176_HONOR-7X_M.jpg)| 105 | |![180 Sharp](samples/180_HONOR-10_S.jpg)| ![180 Defocused](samples/180_HONOR-10_F.jpg)| ![180 Motion](samples/180_HONOR-10_M.jpg)| 106 | 107 | ## Licence 108 | CC0: Public Domain 109 | 110 | ## How to cite 111 | will be soon 112 | 113 | -------------------------------------------------------------------------------- /samples/106_NIKON-D3400-35MM_F.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/106_NIKON-D3400-35MM_F.JPG -------------------------------------------------------------------------------- /samples/106_NIKON-D3400-35MM_M.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/106_NIKON-D3400-35MM_M.JPG -------------------------------------------------------------------------------- /samples/106_NIKON-D3400-35MM_S.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/106_NIKON-D3400-35MM_S.JPG -------------------------------------------------------------------------------- /samples/176_HONOR-7X_F.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/176_HONOR-7X_F.jpg -------------------------------------------------------------------------------- /samples/176_HONOR-7X_M.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/176_HONOR-7X_M.jpg -------------------------------------------------------------------------------- /samples/176_HONOR-7X_S.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/176_HONOR-7X_S.jpg -------------------------------------------------------------------------------- /samples/180_HONOR-10_F.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/180_HONOR-10_F.jpg -------------------------------------------------------------------------------- /samples/180_HONOR-10_M.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/180_HONOR-10_M.jpg -------------------------------------------------------------------------------- /samples/180_HONOR-10_S.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kwentar/blur_dataset/4ab4fa2dbf9a69b25b232b2d6f9fded96981b29b/samples/180_HONOR-10_S.jpg -------------------------------------------------------------------------------- /scripts/generate_kernels.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | 5 | def generate_defocused_kernel(kernel_size: int, n: int, kernel_value=250): 6 | """ 7 | Generate kernel (PSF) for defocused-blurred images generation 8 | :param kernel_size: Size of result kernel, usually <= 128 9 | :param n: -1 for circle, >2 means amount of angles for regular polygon, 10 | 4 for square and so on, 0, 1 and 2 not allowed 11 | :param kernel_value: default pixel value on kernel 12 | :return: PSF as image with size kernel_size x kernel_size and 1 channel 13 | """ 14 | kernel = np.zeros((kernel_size, kernel_size)) 15 | r = kernel_size*0.4 16 | if n == -1: 17 | cv2.circle(kernel, (kernel_size//2, kernel_size//2), int(r), 18 | kernel_value, -1) 19 | else: 20 | points = np.zeros((n, 2), dtype=np.int32) 21 | for i in range(n): 22 | x = int(r * np.cos(2*np.pi * i / n))+kernel_size//2 23 | y = int(r * np.sin(2*np.pi * i / n))+kernel_size//2 24 | points[i] = [x, y] 25 | cv2.fillPoly(kernel, pts=[points], color=kernel_value) 26 | kernel = cv2.blur(kernel, (3, 3)) 27 | noise = np.random.normal(0, 2, (kernel_size, kernel_size)) 28 | print(np.max(noise), np.min(noise)) 29 | mask = kernel.copy()*255/kernel_value 30 | 31 | return cv2.bitwise_and((kernel + noise), mask) 32 | 33 | def 34 | if __name__ == '__main__': 35 | img = generate_defocused_kernel(400, 16) 36 | cv2.imshow('img', img.astype(np.uint8)) 37 | cv2.waitKey() 38 | -------------------------------------------------------------------------------- /scripts/generate_motion_PSF.py: -------------------------------------------------------------------------------- 1 | # from https://github.com/KupynOrest/DeblurGAN 2 | import random 3 | 4 | import numpy as np 5 | from math import ceil 6 | import matplotlib.pyplot as plt 7 | import cv2 8 | from generate_trajectory import Trajectory 9 | 10 | 11 | class PSF(object): 12 | def __init__(self, canvas=None, trajectory=None, fraction=None, 13 | path_to_save=None, max_len=60): 14 | if canvas is None: 15 | self.canvas = (canvas, canvas) 16 | else: 17 | self.canvas = (canvas, canvas) 18 | if trajectory is None: 19 | self.trajectory = Trajectory(canvas=canvas, expl=0.005, max_len=max_len).fit(show=False, save=False) 20 | else: 21 | self.trajectory = trajectory.x 22 | if fraction is None: 23 | self.fraction = [1/2, 1] 24 | else: 25 | self.fraction = fraction 26 | self.path_to_save = path_to_save 27 | self.PSFnumber = len(self.fraction) 28 | self.iters = len(self.trajectory) 29 | self.PSFs = [] 30 | 31 | def fit(self, show=False, save=False): 32 | PSF = np.zeros(self.canvas) 33 | 34 | triangle_fun = lambda x: np.maximum(0, (1 - np.abs(x))) 35 | triangle_fun_prod = lambda x, y: np.multiply(triangle_fun(x), triangle_fun(y)) 36 | for j in range(self.PSFnumber): 37 | if j == 0: 38 | prevT = 0 39 | else: 40 | prevT = self.fraction[j - 1] 41 | 42 | for t in range(len(self.trajectory)): 43 | # print(j, t) 44 | if (self.fraction[j] * self.iters >= t) and (prevT * self.iters < t - 1): 45 | t_proportion = 1 46 | elif (self.fraction[j] * self.iters >= t - 1) and (prevT * self.iters < t - 1): 47 | t_proportion = self.fraction[j] * self.iters - (t - 1) 48 | elif (self.fraction[j] * self.iters >= t) and (prevT * self.iters < t): 49 | t_proportion = t - (prevT * self.iters) 50 | elif (self.fraction[j] * self.iters >= t - 1) and (prevT * self.iters < t): 51 | t_proportion = (self.fraction[j] - prevT) * self.iters 52 | else: 53 | t_proportion = 0 54 | 55 | m2 = int(np.minimum(self.canvas[1] - 1, np.maximum(1, np.math.floor(self.trajectory[t].real)))) 56 | M2 = int(m2 + 1) 57 | m1 = int(np.minimum(self.canvas[0] - 1, np.maximum(1, np.math.floor(self.trajectory[t].imag)))) 58 | M1 = int(m1 + 1) 59 | 60 | PSF[m1, m2] += t_proportion * triangle_fun_prod( 61 | self.trajectory[t].real - m2, self.trajectory[t].imag - m1 62 | ) 63 | PSF[m1, M2] += t_proportion * triangle_fun_prod( 64 | self.trajectory[t].real - M2, self.trajectory[t].imag - m1 65 | ) 66 | PSF[M1, m2] += t_proportion * triangle_fun_prod( 67 | self.trajectory[t].real - m2, self.trajectory[t].imag - M1 68 | ) 69 | PSF[M1, M2] += t_proportion * triangle_fun_prod( 70 | self.trajectory[t].real - M2, self.trajectory[t].imag - M1 71 | ) 72 | 73 | self.PSFs.append(PSF / (self.iters)) 74 | if show or save: 75 | self.__plot_canvas(show, save) 76 | 77 | return self.PSFs 78 | 79 | def __plot_canvas(self, show, save): 80 | if len(self.PSFs) == 0: 81 | raise Exception("Please run fit() method first.") 82 | else: 83 | plt.close() 84 | fig, axes = plt.subplots(1, self.PSFnumber, figsize=(10, 10)) 85 | for i in range(self.PSFnumber): 86 | cv2.imshow(str(i), self.PSFs[i]/np.max(self.PSFs[i])) 87 | if show and save: 88 | if self.path_to_save is None: 89 | raise Exception('Please create Trajectory instance with path_to_save') 90 | cv2.imwrite(self.path_to_save, self.PSFs[0]/np.max(self.PSFs[i])) 91 | cv2.waitKey() 92 | 93 | elif save: 94 | if self.path_to_save is None: 95 | raise Exception('Please create Trajectory instance with path_to_save') 96 | plt.savefig(self.path_to_save) 97 | elif show: 98 | cv2.waitKey() 99 | 100 | 101 | if __name__ == '__main__': 102 | kernel_size = 400 103 | psf = PSF(canvas=kernel_size, 104 | max_len=random.randint(kernel_size//2, kernel_size*2), 105 | path_to_save='test_psf.png') 106 | psf.fit(show=True, save=True) -------------------------------------------------------------------------------- /scripts/generate_trajectory.py: -------------------------------------------------------------------------------- 1 | # from https://github.com/KupynOrest/DeblurGAN 2 | 3 | 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | from math import ceil 7 | 8 | 9 | class Trajectory(object): 10 | def __init__(self, canvas=64, iters=2000, max_len=60, expl=None, path_to_save=None): 11 | """ 12 | Generates a variety of random motion trajectories in continuous domain as in [Boracchi and Foi 2012]. Each 13 | trajectory consists of a complex-valued vector determining the discrete positions of a particle following a 14 | 2-D random motion in continuous domain. The particle has an initial velocity vector which, at each iteration, 15 | is affected by a Gaussian perturbation and by a deterministic inertial component, directed toward the 16 | previous particle position. In addition, with a small probability, an impulsive (abrupt) perturbation aiming 17 | at inverting the particle velocity may arises, mimicking a sudden movement that occurs when the user presses 18 | the camera button or tries to compensate the camera shake. At each step, the velocity is normalized to 19 | guarantee that trajectories corresponding to equal exposures have the same length. Each perturbation ( 20 | Gaussian, inertial, and impulsive) is ruled by its own parameter. Rectilinear Blur as in [Boracchi and Foi 21 | 2011] can be obtained by setting anxiety to 0 (when no impulsive changes occurs 22 | :param canvas: size of domain where our trajectory os defined. 23 | :param iters: number of iterations for definition of our trajectory. 24 | :param max_len: maximum length of our trajectory. 25 | :param expl: this param helps to define probability of big shake. Recommended expl = 0.005. 26 | :param path_to_save: where to save if you need. 27 | """ 28 | self.canvas = canvas 29 | self.iters = iters 30 | self.max_len = max_len 31 | if expl is None: 32 | self.expl = 0.1 * np.random.uniform(0, 1) 33 | else: 34 | self.expl = expl 35 | if path_to_save is None: 36 | pass 37 | else: 38 | self.path_to_save = path_to_save 39 | self.tot_length = None 40 | self.big_expl_count = None 41 | self.x = None 42 | 43 | def fit(self, show=False, save=False): 44 | """ 45 | Generate motion, you can save or plot, coordinates of motion you can find in x property. 46 | Also you can fin properties tot_length, big_expl_count. 47 | :param show: default False. 48 | :param save: default False. 49 | :return: x (vector of motion). 50 | """ 51 | tot_length = 0 52 | big_expl_count = 0 53 | # how to be near the previous position 54 | # TODO: I can change this paramether for 0.1 and make kernel at all image 55 | centripetal = 0.7 * np.random.uniform(0, 1) 56 | # probability of big shake 57 | prob_big_shake = 0.2 * np.random.uniform(0, 1) 58 | # term determining, at each sample, the random component of the new direction 59 | gaussian_shake = 10 * np.random.uniform(0, 1) 60 | init_angle = 360 * np.random.uniform(0, 1) 61 | 62 | img_v0 = np.sin(np.deg2rad(init_angle)) 63 | real_v0 = np.cos(np.deg2rad(init_angle)) 64 | 65 | v0 = complex(real=real_v0, imag=img_v0) 66 | v = v0 * self.max_len / (self.iters - 1) 67 | 68 | if self.expl > 0: 69 | v = v0 * self.expl 70 | 71 | x = np.array([complex(real=0, imag=0)] * (self.iters)) 72 | 73 | for t in range(0, self.iters - 1): 74 | if np.random.uniform() < prob_big_shake * self.expl: 75 | next_direction = 2 * v * (np.exp(complex(real=0, imag=np.pi + (np.random.uniform() - 0.5)))) 76 | big_expl_count += 1 77 | else: 78 | next_direction = 0 79 | 80 | dv = next_direction + self.expl * ( 81 | gaussian_shake * complex(real=np.random.randn(), imag=np.random.randn()) - centripetal * x[t]) * ( 82 | self.max_len / (self.iters - 1)) 83 | 84 | v += dv 85 | v = (v / float(np.abs(v))) * (self.max_len / float((self.iters - 1))) 86 | x[t + 1] = x[t] + v 87 | tot_length = tot_length + abs(x[t + 1] - x[t]) 88 | 89 | # centere the motion 90 | x += complex(real=-np.min(x.real), imag=-np.min(x.imag)) 91 | x = x - complex(real=x[0].real % 1., imag=x[0].imag % 1.) + complex(1, 1) 92 | x += complex(real=ceil((self.canvas - max(x.real)) / 2), imag=ceil((self.canvas - max(x.imag)) / 2)) 93 | 94 | self.tot_length = tot_length 95 | self.big_expl_count = big_expl_count 96 | self.x = x 97 | 98 | if show or save: 99 | self.__plot_canvas(show, save) 100 | return self 101 | 102 | def __len__(self): 103 | return self.iters 104 | 105 | def __getitem__(self, item): 106 | return self.x[item] 107 | def __plot_canvas(self, show, save): 108 | if self.x is None: 109 | raise Exception("Please run fit() method first") 110 | else: 111 | plt.close() 112 | plt.plot(self.x.real, self.x.imag, '-', color='blue') 113 | 114 | plt.xlim((0, self.canvas)) 115 | plt.ylim((0, self.canvas)) 116 | if show and save: 117 | plt.savefig(self.path_to_save) 118 | plt.show() 119 | elif save: 120 | if self.path_to_save is None: 121 | raise Exception('Please create Trajectory instance with path_to_save') 122 | plt.savefig(self.path_to_save) 123 | elif show: 124 | plt.show() 125 | 126 | 127 | if __name__ == '__main__': 128 | trajectory = Trajectory(expl=0.005, 129 | path_to_save='/Users/mykolam/PycharmProjects/University/RandomMotionBlur/main.png') 130 | trajectory.fit(True, False) -------------------------------------------------------------------------------- /scripts/postprocessing.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import shutil 3 | 4 | import ospa 5 | import os 6 | from collections import Counter 7 | 8 | from tqdm import tqdm 9 | 10 | 11 | def check_id(*args): 12 | lists = [] 13 | for item in args: 14 | lists.append([]) 15 | for file_ in item: 16 | lists[-1].append(int(os.path.split(file_)[-1].split('_')[0])) 17 | for item in lists: 18 | item.sort() 19 | for item in lists: 20 | for item2 in lists: 21 | assert item == item2, 'incorrect id lists' 22 | 23 | 24 | def check_type(files): 25 | current_set = set() 26 | for file_ in files: 27 | filename = os.path.split(file_)[-1] 28 | current_set.add(filename.split('_')[2].split('.')[0]) 29 | assert len(current_set) == 1, f'wrong set {current_set}' 30 | print(current_set) 31 | 32 | 33 | def check_devices(*args): 34 | lists = [] 35 | for item in args: 36 | lists.append([]) 37 | for file_ in item: 38 | lists[-1].append(os.path.split(file_)[-1].split('_')[1]) 39 | for item in lists: 40 | item.sort() 41 | 42 | for item in lists: 43 | 44 | for item2 in lists: 45 | assert item == item2, 'incorrect device lists' 46 | cnt = Counter(lists[-1]) 47 | dev = [] 48 | for item in cnt.most_common(100): 49 | print(f'|{item[0]}\t| {item[1]}\t|') 50 | dev.append(item[0]) 51 | for item in sorted(dev): 52 | print(item) 53 | print(len(dev)) 54 | 55 | 56 | def replace_device_names(files): 57 | replace_dict = { 58 | 'NIKON-3400-18-55mm': 'NIKON-D3400-18-55MM', 59 | 'NIKON-3400-35mm': 'NIKON-D3400-35MM', 60 | 'HONOR7X': 'HONOR-7X', 61 | 'HONOR10': 'HONOR-10', 62 | 'HONOR9': 'HONOR-9', 63 | 'IPHONE7': 'IPHONE-7', 64 | 'REDMI-3S': 'XIAOMI-REDMI-3S', 65 | 'PRESIGIO-MULTI-PHONE': 'PRESTIGIO-MULTI-PHONE', 66 | 'SAMSUNG-A3': 'SAMSUNG-GALAXY-A3', 67 | 'SAMSUNG-A5': 'SAMSUNG-GALAXY-A5', 68 | 'SAMSUNG-A6': 'SAMSUNG-GALAXY-A6', 69 | 'SAMSUNG-A8': 'SAMSUNG-GALAXY-A8', 70 | 'SAMSUNG-J7': 'SAMSUNG-GALAXY-J7', 71 | 'XT1710-09': 'MOTOROLA-MOTO-Z2-PLAY', 72 | 'XIAOMI-REDMI-NOT-4X': 'XIAOMI-REDMI-NOTE-4X', 73 | 'XIAOMI-MI-8-LITE': 'XIAOMI-MI8-LITE', 74 | 'XIAOMI-MI8SE': 'XIAOMI-MI8-SE', 75 | 'XIAOMI-MI-8SE': 'XIAOMI-MI8-SE', 76 | 'SAMSUNG-GALAXY-A8+': 'SAMSUNG-GALAXY-A8-PLUS', 77 | 'SAMSUMG-A700F': 'SAMSUNG-GALAXY-A8', 78 | 'HUAWEIP20': 'HUAWEI-P20', 79 | 'HUASWEI-P20-LITE': 'HUAWEI-P20-LITE', 80 | 'XIAOMI-REDMI-NOTE7': 'XIAOMI-REDMI-NOTE-7', 81 | 'NIKON-3400-35MM': 'NIKON-D3400-35MM', 82 | 'NIKON-3400-18-55MM': 'NIKON-D3400-18-55MM' 83 | } 84 | for file_ in files: 85 | dir_, filename = os.path.split(file_) 86 | id_, device, end = filename.split('_') 87 | if device in replace_dict: 88 | print(f'I\'ll replace {filename} with device {device} to {replace_dict[device]}') 89 | new_filename = f'{id_}_{replace_dict[device]}_{end}' 90 | new_full_path = os.path.join(dir_, new_filename) 91 | shutil.move(file_, new_full_path) 92 | 93 | 94 | def parse_folder(src_folder, 95 | sharp_folder='sharp', 96 | defocused_folder='defocused_blurred', 97 | motion_folder='motion_blurred'): 98 | sharp_images = ospa.listdir(os.path.join(src_folder, sharp_folder)) 99 | defocused_images = ospa.listdir(os.path.join(src_folder, defocused_folder)) 100 | motion_images = ospa.listdir(os.path.join(src_folder, motion_folder)) 101 | check_id(sharp_images, defocused_images, motion_images) 102 | check_type(sharp_images) 103 | check_type(defocused_images) 104 | check_type(motion_images) 105 | check_devices(sharp_images, defocused_images, motion_images) 106 | replace_device_names(sharp_images) 107 | replace_device_names(defocused_images) 108 | replace_device_names(motion_images) 109 | 110 | 111 | def check_folders_exist(dst_folder, sharp_folder, defocused_folder, motion_folder): 112 | if not os.path.exists(dst_folder): 113 | os.mkdir(dst_folder) 114 | if not os.path.exists(os.path.join(dst_folder, sharp_folder)): 115 | os.mkdir(os.path.join(dst_folder, sharp_folder)) 116 | if not os.path.exists(os.path.join(dst_folder, defocused_folder)): 117 | os.mkdir(os.path.join(dst_folder, defocused_folder)) 118 | if not os.path.exists(os.path.join(dst_folder, motion_folder)): 119 | os.mkdir(os.path.join(dst_folder, motion_folder)) 120 | 121 | 122 | def move_bad_ids(src_folder, 123 | dst_folder, 124 | bad_ids, 125 | sharp_folder='sharp', 126 | defocused_folder='defocused_blurred', 127 | motion_folder='motion_blurred'): 128 | sharp_images = ospa.listdir(os.path.join(src_folder, sharp_folder), full_path=False) 129 | defocused_images = ospa.listdir(os.path.join(src_folder, defocused_folder), full_path=False) 130 | motion_images = ospa.listdir(os.path.join(src_folder, motion_folder), full_path=False) 131 | check_folders_exist(dst_folder, sharp_folder, defocused_folder, motion_folder) 132 | 133 | for images, folder in zip([sharp_images, defocused_images, motion_images], 134 | [sharp_folder, defocused_folder, motion_folder]): 135 | for file_ in images: 136 | id_ = int(file_.split('_')[0]) 137 | if id_ in bad_ids: 138 | print(f'I\'ll move {file_}') 139 | shutil.move(os.path.join(src_folder, folder, file_), 140 | os.path.join(dst_folder, folder, file_)) 141 | 142 | 143 | def fix_id(src_folder, 144 | dst_folder, 145 | sharp_folder='sharp', 146 | defocused_folder='defocused_blurred', 147 | motion_folder='motion_blurred'): 148 | id_dict = {} 149 | check_folders_exist(dst_folder, sharp_folder, defocused_folder, motion_folder) 150 | sharp_images = ospa.listdir(os.path.join(src_folder, sharp_folder), full_path=False) 151 | defocused_images = ospa.listdir(os.path.join(src_folder, defocused_folder), full_path=False) 152 | motion_images = ospa.listdir(os.path.join(src_folder, motion_folder), full_path=False) 153 | for image in sharp_images: 154 | id_ = int(image.split('_')[0]) 155 | if id_ not in id_dict: 156 | id_dict[id_] = [-1, -1, -1] 157 | id_dict[id_][0] = '_'.join(image.split('_')[1:]) 158 | for image in defocused_images: 159 | id_ = int(image.split('_')[0]) 160 | if id_ not in id_dict: 161 | id_dict[id_] = [-1, -1, -1] 162 | id_dict[id_][1] = '_'.join(image.split('_')[1:]) 163 | for image in motion_images: 164 | id_ = int(image.split('_')[0]) 165 | if id_ not in id_dict: 166 | id_dict[id_] = [-1, -1, -1] 167 | id_dict[id_][2] = '_'.join(image.split('_')[1:]) 168 | 169 | print(id_dict) 170 | print(len(id_dict)) 171 | 172 | for index, (key, item) in enumerate(id_dict.items()): 173 | sharp_image_new = os.path.join(dst_folder, sharp_folder, f'{index}_{item[0]}') 174 | defocused_image_new = os.path.join(dst_folder, defocused_folder, f'{index}_{item[1]}') 175 | motion_image_new = os.path.join(dst_folder, motion_folder, f'{index}_{item[2]}') 176 | sharp_image_old = os.path.join(src_folder, sharp_folder, f'{key}_{item[0]}') 177 | defocused_image_old = os.path.join(src_folder, defocused_folder, f'{key}_{item[1]}') 178 | motion_image_old = os.path.join(src_folder, motion_folder, f'{key}_{item[2]}') 179 | 180 | shutil.move(sharp_image_old, sharp_image_new) 181 | shutil.move(defocused_image_old, defocused_image_new) 182 | shutil.move(motion_image_old, motion_image_new) 183 | 184 | 185 | def check_sizes(src_folder, 186 | sharp_folder='sharp', 187 | defocused_folder='defocused_blurred', 188 | motion_folder='motion_blurred'): 189 | sharp_images = ospa.listdir(os.path.join(src_folder, sharp_folder)) 190 | defocused_images = ospa.listdir(os.path.join(src_folder, defocused_folder)) 191 | motion_images = ospa.listdir(os.path.join(src_folder, motion_folder)) 192 | print(len(sharp_images), len(defocused_images), len(motion_images)) 193 | for sharp, defocused, motion in tqdm(zip(sharp_images, defocused_images, motion_images), 194 | total=len(sharp_images)): 195 | sharp_image = cv2.imread(sharp) 196 | defocused_image = cv2.imread(defocused) 197 | motion_image = cv2.imread(motion) 198 | if sharp_image.shape != defocused_image.shape or sharp_image.shape != motion_image.shape: 199 | print('before', sharp, sharp_image.shape, defocused_image.shape, motion_image.shape) 200 | min_y = min(sharp_image.shape[0], defocused_image.shape[0], motion_image.shape[0])-1 201 | min_x = min(sharp_image.shape[1], defocused_image.shape[1], motion_image.shape[1])-1 202 | sharp_image = sharp_image[:min_y, :min_x] 203 | defocused_image = defocused_image[:min_y, :min_x] 204 | motion_image = motion_image[:min_y, :min_x] 205 | print('after', sharp, sharp_image.shape, defocused_image.shape, motion_image.shape) 206 | cv2.imwrite(sharp, sharp_image) 207 | cv2.imwrite(defocused, defocused_image) 208 | cv2.imwrite(motion, motion_image) 209 | 210 | 211 | def scale_base_to(src_folder, 212 | dst_folder, 213 | dst_size=2048, 214 | sharp_folder='sharp', 215 | defocused_folder='defocused_blurred', 216 | motion_folder='motion_blurred'): 217 | 218 | check_folders_exist(dst_folder, sharp_folder, defocused_folder, motion_folder) 219 | 220 | sharp_images = ospa.listdir(os.path.join(src_folder, sharp_folder)) 221 | defocused_images = ospa.listdir(os.path.join(src_folder, defocused_folder)) 222 | motion_images = ospa.listdir(os.path.join(src_folder, motion_folder)) 223 | print(len(sharp_images), len(defocused_images), len(motion_images)) 224 | for sharp, defocused, motion in tqdm(zip(sharp_images, defocused_images, motion_images), 225 | total=len(sharp_images)): 226 | sharp_image = cv2.imread(sharp) 227 | defocused_image = cv2.imread(defocused) 228 | motion_image = cv2.imread(motion) 229 | scale = dst_size/max(sharp_image.shape) 230 | sharp_image = cv2.resize(sharp_image, (0, 0), fx=scale, fy=scale) 231 | defocused_image = cv2.resize(defocused_image, (0, 0), fx=scale, fy=scale) 232 | motion_image = cv2.resize(motion_image, (0, 0), fx=scale, fy=scale) 233 | cv2.imwrite(os.path.join(dst_folder, sharp_folder, os.path.split(sharp)[-1]), 234 | sharp_image) 235 | cv2.imwrite(os.path.join(dst_folder, defocused_folder, os.path.split(defocused)[-1]), 236 | defocused_image) 237 | cv2.imwrite(os.path.join(dst_folder, motion_folder, os.path.split(motion)[-1]), 238 | motion_image) 239 | 240 | 241 | if __name__ == '__main__': 242 | parse_folder(r'G:\_datasets\blur_dataset') 243 | # bad_id = [10, 86, 121, 164, 192, 195, 17, 36, 47, 53, 89, 106, 154, 44, 46, 244 | # 48, 87, 88, 94, 97, 104, 110, 128, 136, 139, 143, 170, 175, 205, 245 | # 218, 255, 298, 301, 306, 317, 335, 347, 379, 206, 222, 234, 295, 296, 309] 246 | # 247 | # bad_id = [10, 13, 18, 102, 144, 185, 205, 211, 244, 277, 282, 304, 322] 248 | # move_bad_ids(r'G:\_datasets\blur_dataset', r'G:\_datasets\blur_dataset_tmp', bad_id) 249 | # parse_folder(r'G:\_datasets\blur_dataset') 250 | # fix_id(r'G:\_datasets\blur_dataset', r'G:\_datasets\blur_dataset_fix_id') 251 | check_sizes(r'G:\_datasets\blur_dataset_fix_id') 252 | scale_base_to(r'G:\_datasets\blur_dataset_fix_id', r'G:\_datasets\blur_dataset_scaled') 253 | 254 | 255 | --------------------------------------------------------------------------------