├── PatchBasedSynthesis.py ├── README.md ├── bubbles.jpg ├── corn.jpg ├── mesh.jpg ├── mesh2.jpg ├── pattern.jpg ├── results ├── bubblesSynthesis.jpg ├── mesh2.jpg ├── mesh2Synthesis.jpg ├── meshSynthesis.jpg ├── patternSynthesis.jpg ├── progressbas.jpg └── roofSynthesis.jpg └── roof.jpg /PatchBasedSynthesis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import cv2 4 | import sys 5 | import numpy as np 6 | from random import randint 7 | 8 | #Image Loading and initializations 9 | InputName = str(sys.argv[1]) 10 | img_sample = cv2.imread(InputName) 11 | img_height = 250 12 | img_width = 200 13 | sample_width = img_sample.shape[1] 14 | sample_height = img_sample.shape[0] 15 | img = np.zeros((img_height,img_width,3), np.uint8) 16 | PatchSize = int(sys.argv[2]) 17 | OverlapWidth = int(sys.argv[3]) 18 | InitialThresConstant = float(sys.argv[4]) 19 | 20 | #Picking random patch to begin 21 | randomPatchHeight = randint(0, sample_height - PatchSize) 22 | randomPatchWidth = randint(0, sample_width - PatchSize) 23 | for i in range(PatchSize): 24 | for j in range(PatchSize): 25 | img[i, j] = img_sample[randomPatchHeight + i, randomPatchWidth + j] 26 | #initializating next 27 | GrowPatchLocation = (0,PatchSize) 28 | #---------------------------------------------------------------------------------------# 29 | #| Best Fit Patch and related functions |# 30 | #---------------------------------------------------------------------------------------# 31 | def OverlapErrorVertical( imgPx, samplePx ): 32 | iLeft,jLeft = imgPx 33 | iRight,jRight = samplePx 34 | OverlapErr = 0 35 | diff = np.zeros((3)) 36 | for i in range( PatchSize ): 37 | for j in range( OverlapWidth ): 38 | diff[0] = int(img[i + iLeft, j+ jLeft][0]) - int(img_sample[i + iRight, j + jRight][0]) 39 | diff[1] = int(img[i + iLeft, j+ jLeft][1]) - int(img_sample[i + iRight, j + jRight][1]) 40 | diff[2] = int(img[i + iLeft, j+ jLeft][2]) - int(img_sample[i + iRight, j + jRight][2]) 41 | OverlapErr += (diff[0]**2 + diff[1]**2 + diff[2]**2)**0.5 42 | return OverlapErr 43 | 44 | def OverlapErrorHorizntl( leftPx, rightPx ): 45 | iLeft,jLeft = leftPx 46 | iRight,jRight = rightPx 47 | OverlapErr = 0 48 | diff = np.zeros((3)) 49 | for i in range( OverlapWidth ): 50 | for j in range( PatchSize ): 51 | diff[0] = int(img[i + iLeft, j+ jLeft][0]) - int(img_sample[i + iRight, j + jRight][0]) 52 | diff[1] = int(img[i + iLeft, j+ jLeft][1]) - int(img_sample[i + iRight, j + jRight][1]) 53 | diff[2] = int(img[i + iLeft, j+ jLeft][2]) - int(img_sample[i + iRight, j + jRight][2]) 54 | OverlapErr += (diff[0]**2 + diff[1]**2 + diff[2]**2)**0.5 55 | return OverlapErr 56 | 57 | def GetBestPatches( px ):#Will get called in GrowImage 58 | PixelList = [] 59 | #check for top layer 60 | if px[0] == 0: 61 | for i in range(sample_height - PatchSize): 62 | for j in range(OverlapWidth, sample_width - PatchSize ): 63 | error = OverlapErrorVertical( (px[0], px[1] - OverlapWidth), (i, j - OverlapWidth) ) 64 | if error < ThresholdOverlapError: 65 | PixelList.append((i,j)) 66 | elif error < ThresholdOverlapError/2: 67 | return [(i,j)] 68 | #check for leftmost layer 69 | elif px[1] == 0: 70 | for i in range(OverlapWidth, sample_height - PatchSize ): 71 | for j in range(sample_width - PatchSize): 72 | error = OverlapErrorHorizntl( (px[0] - OverlapWidth, px[1]), (i - OverlapWidth, j) ) 73 | if error < ThresholdOverlapError: 74 | PixelList.append((i,j)) 75 | elif error < ThresholdOverlapError/2: 76 | return [(i,j)] 77 | #for pixel placed inside 78 | else: 79 | for i in range(OverlapWidth, sample_height - PatchSize): 80 | for j in range(OverlapWidth, sample_width - PatchSize): 81 | error_Vertical = OverlapErrorVertical( (px[0], px[1] - OverlapWidth), (i,j - OverlapWidth) ) 82 | error_Horizntl = OverlapErrorHorizntl( (px[0] - OverlapWidth, px[1]), (i - OverlapWidth,j) ) 83 | if error_Vertical < ThresholdOverlapError and error_Horizntl < ThresholdOverlapError: 84 | PixelList.append((i,j)) 85 | elif error_Vertical < ThresholdOverlapError/2 and error_Horizntl < ThresholdOverlapError/2: 86 | return [(i,j)] 87 | return PixelList 88 | 89 | #-----------------------------------------------------------------------------------------------# 90 | #| Quilting and related Functions |# 91 | #-----------------------------------------------------------------------------------------------# 92 | 93 | def SSD_Error( offset, imgPx, samplePx ): 94 | err_r = int(img[imgPx[0] + offset[0], imgPx[1] + offset[1]][0]) -int(img_sample[samplePx[0] + offset[0], samplePx[1] + offset[1]][0]) 95 | err_g = int(img[imgPx[0] + offset[0], imgPx[1] + offset[1]][1]) - int(img_sample[samplePx[0] + offset[0], samplePx[1] + offset[1]][1]) 96 | err_b = int(img[imgPx[0] + offset[0], imgPx[1] + offset[1]][2]) - int(img_sample[samplePx[0] + offset[0], samplePx[1] + offset[1]][2]) 97 | return (err_r**2 + err_g**2 + err_b**2)/3.0 98 | 99 | #---------------------------------------------------------------# 100 | #| Calculating Cost |# 101 | #---------------------------------------------------------------# 102 | 103 | def GetCostVertical(imgPx, samplePx): 104 | Cost = np.zeros((PatchSize, OverlapWidth)) 105 | for j in range(OverlapWidth): 106 | for i in range(PatchSize): 107 | if i == PatchSize - 1: 108 | Cost[i,j] = SSD_Error((i ,j - OverlapWidth), imgPx, samplePx) 109 | else: 110 | if j == 0 : 111 | Cost[i,j] = SSD_Error((i , j - OverlapWidth), imgPx, samplePx) + min( SSD_Error((i + 1, j - OverlapWidth), imgPx, samplePx),SSD_Error((i + 1,j + 1 - OverlapWidth), imgPx, samplePx) ) 112 | elif j == OverlapWidth - 1: 113 | Cost[i,j] = SSD_Error((i, j - OverlapWidth), imgPx, samplePx) + min( SSD_Error((i + 1, j - OverlapWidth), imgPx, samplePx), SSD_Error((i + 1, j - 1 - OverlapWidth), imgPx, samplePx) ) 114 | else: 115 | Cost[i,j] = SSD_Error((i, j -OverlapWidth), imgPx, samplePx) + min(SSD_Error((i + 1, j - OverlapWidth), imgPx, samplePx),SSD_Error((i + 1, j + 1 - OverlapWidth), imgPx, samplePx),SSD_Error((i + 1, j - 1 - OverlapWidth), imgPx, samplePx)) 116 | return Cost 117 | 118 | def GetCostHorizntl(imgPx, samplePx): 119 | Cost = np.zeros((OverlapWidth, PatchSize)) 120 | for i in range( OverlapWidth ): 121 | for j in range( PatchSize ): 122 | if j == PatchSize - 1: 123 | Cost[i,j] = SSD_Error((i - OverlapWidth, j), imgPx, samplePx) 124 | elif i == 0: 125 | Cost[i,j] = SSD_Error((i - OverlapWidth, j), imgPx, samplePx) + min(SSD_Error((i - OverlapWidth, j + 1), imgPx, samplePx), SSD_Error((i + 1 - OverlapWidth, j + 1), imgPx, samplePx)) 126 | elif i == OverlapWidth - 1: 127 | Cost[i,j] = SSD_Error((i - OverlapWidth, j), imgPx, samplePx) + min(SSD_Error((i - OverlapWidth, j + 1), imgPx, samplePx), SSD_Error((i - 1 - OverlapWidth, j + 1), imgPx, samplePx)) 128 | else: 129 | Cost[i,j] = SSD_Error((i - OverlapWidth, j), imgPx, samplePx) + min(SSD_Error((i - OverlapWidth, j + 1), imgPx, samplePx), SSD_Error((i + 1 - OverlapWidth, j + 1), imgPx, samplePx), SSD_Error((i - 1 - OverlapWidth, j + 1), imgPx, samplePx)) 130 | return Cost 131 | 132 | #---------------------------------------------------------------# 133 | #| Finding Minimum Cost Path |# 134 | #---------------------------------------------------------------# 135 | 136 | def FindMinCostPathVertical(Cost): 137 | Boundary = np.zeros((PatchSize),np.int) 138 | ParentMatrix = np.zeros((PatchSize, OverlapWidth),np.int) 139 | for i in range(1, PatchSize): 140 | for j in range(OverlapWidth): 141 | if j == 0: 142 | ParentMatrix[i,j] = j if Cost[i-1,j] < Cost[i-1,j+1] else j+1 143 | elif j == OverlapWidth - 1: 144 | ParentMatrix[i,j] = j if Cost[i-1,j] < Cost[i-1,j-1] else j-1 145 | else: 146 | curr_min = j if Cost[i-1,j] < Cost[i-1,j-1] else j-1 147 | ParentMatrix[i,j] = curr_min if Cost[i-1,curr_min] < Cost[i-1,j+1] else j+1 148 | Cost[i,j] += Cost[i-1, ParentMatrix[i,j]] 149 | minIndex = 0 150 | for j in range(1,OverlapWidth): 151 | minIndex = minIndex if Cost[PatchSize - 1, minIndex] < Cost[PatchSize - 1, j] else j 152 | Boundary[PatchSize-1] = minIndex 153 | for i in range(PatchSize - 1,0,-1): 154 | Boundary[i - 1] = ParentMatrix[i,Boundary[i]] 155 | return Boundary 156 | 157 | def FindMinCostPathHorizntl(Cost): 158 | Boundary = np.zeros(( PatchSize),np.int) 159 | ParentMatrix = np.zeros((OverlapWidth, PatchSize),np.int) 160 | for j in range(1, PatchSize): 161 | for i in range(OverlapWidth): 162 | if i == 0: 163 | ParentMatrix[i,j] = i if Cost[i,j-1] < Cost[i+1,j-1] else i + 1 164 | elif i == OverlapWidth - 1: 165 | ParentMatrix[i,j] = i if Cost[i,j-1] < Cost[i-1,j-1] else i - 1 166 | else: 167 | curr_min = i if Cost[i,j-1] < Cost[i-1,j-1] else i - 1 168 | ParentMatrix[i,j] = curr_min if Cost[curr_min,j-1] < Cost[i-1,j-1] else i + 1 169 | Cost[i,j] += Cost[ParentMatrix[i,j], j-1] 170 | minIndex = 0 171 | for i in range(1,OverlapWidth): 172 | minIndex = minIndex if Cost[minIndex, PatchSize - 1] < Cost[i, PatchSize - 1] else i 173 | Boundary[PatchSize-1] = minIndex 174 | for j in range(PatchSize - 1,0,-1): 175 | Boundary[j - 1] = ParentMatrix[Boundary[j],j] 176 | return Boundary 177 | 178 | #---------------------------------------------------------------# 179 | #| Quilting |# 180 | #---------------------------------------------------------------# 181 | 182 | def QuiltVertical(Boundary, imgPx, samplePx): 183 | for i in range(PatchSize): 184 | for j in range(Boundary[i], 0, -1): 185 | img[imgPx[0] + i, imgPx[1] - j] = img_sample[ samplePx[0] + i, samplePx[1] - j ] 186 | def QuiltHorizntl(Boundary, imgPx, samplePx): 187 | for j in range(PatchSize): 188 | for i in range(Boundary[j], 0, -1): 189 | img[imgPx[0] - i, imgPx[1] + j] = img_sample[samplePx[0] - i, samplePx[1] + j] 190 | 191 | def QuiltPatches( imgPx, samplePx ): 192 | #check for top layer 193 | if imgPx[0] == 0: 194 | Cost = GetCostVertical(imgPx, samplePx) 195 | # Getting boundary to stitch 196 | Boundary = FindMinCostPathVertical(Cost) 197 | #Quilting Patches 198 | QuiltVertical(Boundary, imgPx, samplePx) 199 | #check for leftmost layer 200 | elif imgPx[1] == 0: 201 | Cost = GetCostHorizntl(imgPx, samplePx) 202 | #Boundary to stitch 203 | Boundary = FindMinCostPathHorizntl(Cost) 204 | #Quilting Patches 205 | QuiltHorizntl(Boundary, imgPx, samplePx) 206 | #for pixel placed inside 207 | else: 208 | CostVertical = GetCostVertical(imgPx, samplePx) 209 | CostHorizntl = GetCostHorizntl(imgPx, samplePx) 210 | BoundaryVertical = FindMinCostPathVertical(CostVertical) 211 | BoundaryHorizntl = FindMinCostPathHorizntl(CostHorizntl) 212 | QuiltVertical(BoundaryVertical, imgPx, samplePx) 213 | QuiltHorizntl(BoundaryHorizntl, imgPx, samplePx) 214 | 215 | #--------------------------------------------------------------------------------------------------------# 216 | # Growing Image Patch-by-patch |# 217 | #--------------------------------------------------------------------------------------------------------# 218 | 219 | def FillImage( imgPx, samplePx ): 220 | for i in range(PatchSize): 221 | for j in range(PatchSize): 222 | img[ imgPx[0] + i, imgPx[1] + j ] = img_sample[ samplePx[0] + i, samplePx[1] + j ] 223 | 224 | pixelsCompleted = 0 225 | TotalPatches = ( (img_height - 1 )/ PatchSize )*((img_width)/ PatchSize) - 1 226 | sys.stdout.write("Progress : [%-20s] %d%% | PixelsCompleted: %d | ThresholdConstant: --.------" % ('='*(pixelsCompleted*20/TotalPatches), (100*pixelsCompleted)/TotalPatches, pixelsCompleted)) 227 | sys.stdout.flush() 228 | while GrowPatchLocation[0] + PatchSize < img_height: 229 | pixelsCompleted += 1 230 | ThresholdConstant = InitialThresConstant 231 | #set progress to zer0 232 | progress = 0 233 | while progress == 0: 234 | ThresholdOverlapError = ThresholdConstant * PatchSize * OverlapWidth 235 | #Get Best matches for current pixel 236 | List = GetBestPatches(GrowPatchLocation) 237 | if len(List) > 0: 238 | progress = 1 239 | #Make A random selection from best fit pxls 240 | sampleMatch = List[ randint(0, len(List) - 1) ] 241 | FillImage( GrowPatchLocation, sampleMatch ) 242 | #Quilt this with in curr location 243 | QuiltPatches( GrowPatchLocation, sampleMatch ) 244 | #upadate cur pixel location 245 | GrowPatchLocation = (GrowPatchLocation[0], GrowPatchLocation[1] + PatchSize) 246 | if GrowPatchLocation[1] + PatchSize > img_width: 247 | GrowPatchLocation = (GrowPatchLocation[0] + PatchSize, 0) 248 | #if not progressed, increse threshold 249 | else: 250 | ThresholdConstant *= 1.1 251 | # print pixelsCompleted, ThresholdConstant 252 | sys.stdout.write('\r') 253 | sys.stdout.write("Progress : [%-20s] %d%% | PixelsCompleted: %d | ThresholdConstant: %f" % ('='*(pixelsCompleted*20/TotalPatches), (100*pixelsCompleted)/TotalPatches, pixelsCompleted, ThresholdConstant)) 254 | sys.stdout.flush() 255 | 256 | # Displaying Images 257 | cv2.imshow('Sample Texture',img_sample) 258 | cv2.waitKey(0) 259 | cv2.destroyAllWindows() 260 | cv2.imshow('Generated Image',img) 261 | cv2.waitKey(0) 262 | cv2.destroyAllWindows() 263 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Patch-Based-Texture-Synthesis 2 | It's an implementation of Efros and Freeman's "Image Quilting and Texture Synthesis" 2001 3 | 4 | The output depends on two factors : PatchSize and OverlapWidth 5 | The running time depends on Sample Image dimensions, Desired Image dimensions, ThresholdConstant and PatchSize 6 | 7 | ## To run the code, copy the following into your command line 8 | `python PatchBasedSynthesis.py /image/source.jpg Patch_Size Overlap_Width Initial_Threshold_error` 9 | 10 | for example 11 | `python PatchBasedSynthesis.py /home/afroz/textures/corn.jpg 30 5 78.0` 12 | -------------------------------------------------------------------------------- /bubbles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/bubbles.jpg -------------------------------------------------------------------------------- /corn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/corn.jpg -------------------------------------------------------------------------------- /mesh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/mesh.jpg -------------------------------------------------------------------------------- /mesh2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/mesh2.jpg -------------------------------------------------------------------------------- /pattern.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/pattern.jpg -------------------------------------------------------------------------------- /results/bubblesSynthesis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/results/bubblesSynthesis.jpg -------------------------------------------------------------------------------- /results/mesh2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/results/mesh2.jpg -------------------------------------------------------------------------------- /results/mesh2Synthesis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/results/mesh2Synthesis.jpg -------------------------------------------------------------------------------- /results/meshSynthesis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/results/meshSynthesis.jpg -------------------------------------------------------------------------------- /results/patternSynthesis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/results/patternSynthesis.jpg -------------------------------------------------------------------------------- /results/progressbas.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/results/progressbas.jpg -------------------------------------------------------------------------------- /results/roofSynthesis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/results/roofSynthesis.jpg -------------------------------------------------------------------------------- /roof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrozalm/Patch-Based-Texture-Synthesis/ce2e73816e623456a216955d4723e56bfec75738/roof.jpg --------------------------------------------------------------------------------