├── 0_dbscan.py ├── 0_kmeans.py ├── 0_read.py ├── 0_save.py ├── 1_butterfly.jpg ├── 1_butterflySmaller.jpg ├── 2_butterfly2means.jpg ├── 2_butterfly5means.jpg ├── 2_butterfly_dbscan_100_100_e.jpg ├── 2_butterfly_dbscan_100_100_m.jpg ├── README.md └── log.txt /0_dbscan.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import math 3 | from ast import literal_eval 4 | from PIL import Image 5 | import time 6 | 7 | #Distance functions 8 | def EuclideanDistance(P,Q): 9 | intermediateValues = [] 10 | for i in range(len(P[2])): 11 | intermediateValues.append(math.pow(Q[2][i]-P[2][i],2)) 12 | return math.sqrt(sum(intermediateValues)) 13 | 14 | def MaximumDistance(P,Q): 15 | intermediateValues = [] 16 | for i in range(len(P[2])): 17 | intermediateValues.append(abs(Q[2][i]-P[2][i])) 18 | return max(intermediateValues) 19 | 20 | #Finds all neighbor points for a chosen point 21 | def FindNeighbours(Point, Points, distanceFunction, eps): 22 | tempNeighbours = [] 23 | for y in range(len(Points)): 24 | for x in range(len(Points[0])): 25 | if distanceFunction == "e": 26 | if EuclideanDistance(Point, Points[y][x]) <= eps: 27 | tempNeighbours.append(Points[y][x]) 28 | if distanceFunction == "m": 29 | if MaximumDistance(Point, Points[y][x]) <= eps: 30 | tempNeighbours.append(Points[y][x]) 31 | return tempNeighbours 32 | 33 | start = time.time() 34 | #Read parameters 35 | vectors = literal_eval(sys.stdin.read()) 36 | eps = int(sys.argv[1]) 37 | minPts = int(sys.argv[2]) 38 | distFunc = sys.argv[3] 39 | 40 | if distFunc != "e" and distFunc != "m": 41 | sys.exit(1) 42 | 43 | #prepare array 44 | pointsArray = [] 45 | for y in range(len(vectors)): 46 | pointsArray.append([]) 47 | for x in range(len(vectors[0])): 48 | pointsArray[y].append([y,x,vectors[y][x],"Undefined"]) 49 | 50 | #DBSCAN 51 | clusterCounter = 0 52 | progress = 0 53 | for y in range(len(vectors)): 54 | for x in range(len(vectors[0])): 55 | if pointsArray[y][x][-1] != "Undefined": 56 | continue 57 | 58 | Neighbours = FindNeighbours(pointsArray[y][x], pointsArray, distFunc, eps) 59 | if len(Neighbours) < minPts: 60 | pointsArray[y][x][-1] = "Noise" 61 | continue 62 | 63 | clusterCounter = clusterCounter + 1 64 | pointsArray[y][x][-1] = str(clusterCounter) 65 | if pointsArray[y][x] in Neighbours: 66 | Neighbours.remove(pointsArray[y][x]) 67 | 68 | for innerPoint in Neighbours: 69 | if innerPoint[-1] == "Noise": 70 | pointsArray[innerPoint[0]][innerPoint[1]][-1] = str(clusterCounter) 71 | if innerPoint[-1] != "Undefined": 72 | continue 73 | pointsArray[innerPoint[0]][innerPoint[1]][-1] = str(clusterCounter) 74 | NeighboursInner = FindNeighbours(innerPoint, pointsArray, distFunc, eps) 75 | if len(NeighboursInner) >= minPts: 76 | Neighbours.append(NeighboursInner) 77 | 78 | #Get distinct clusters 79 | clusterNumbers = [] 80 | for y in range(len(vectors)): 81 | for x in range(len(vectors[0])): 82 | if pointsArray[y][x][-1] not in clusterNumbers: 83 | clusterNumbers.append(pointsArray[y][x][-1]) 84 | 85 | #Map cluster's averages 86 | averagesForClusters = [] 87 | for item in clusterNumbers: 88 | n = 0 89 | vectorTemps = [0]*len(pointsArray[0][0][2]) 90 | for y in range(len(vectors)): 91 | for x in range(len(vectors[0])): 92 | if pointsArray[y][x][-1] == item: 93 | for i in range(len(pointsArray[y][x][2])): 94 | vectorTemps[i] = vectorTemps[i] + pointsArray[y][x][2][i] 95 | n = n + 1 96 | #Check 0 division 97 | for i in range(len(vectorTemps)): 98 | if vectorTemps[i] != 0: 99 | vectorTemps[i] = vectorTemps[i]/n 100 | averagesForClusters.append(vectorTemps) 101 | 102 | #Build clustered array and change cluster averages with initial values 103 | clusteredVectors = [] 104 | for y in range(len(pointsArray)): 105 | clusteredVectors.append([]) 106 | for x in range(len(pointsArray[0])): 107 | clusteredVectors[y].append(averagesForClusters[clusterNumbers.index(pointsArray[y][x][-1])]) 108 | 109 | print(clusteredVectors) 110 | end = time.time() 111 | 112 | #Log info 113 | with open('log.txt', 'a+') as out: 114 | out.write(f"""**DBSCAN RESULTS**\n""") 115 | out.write(f"""Array size: {len(vectors)}x{len(vectors[0])}x{len(vectors[0][0])}\n""") 116 | out.write(f"""Parameters: Eps: {eps}, minPts: {minPts}, distFunc: {distFunc}\n""") 117 | out.write(f"""Number of identified clusters: {len(clusterNumbers)}\n""") 118 | out.write(f"""Cluster averages: {str(averagesForClusters)}\n""") 119 | out.write(f"""Time taken: {round(end - start)} seconds\n""") 120 | -------------------------------------------------------------------------------- /0_kmeans.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | from ast import literal_eval 4 | from PIL import Image 5 | import time 6 | 7 | start = time.time() 8 | #Convert input to array and read kVal 9 | vectors = literal_eval(sys.stdin.read()) 10 | kVal = int(sys.argv[1]) 11 | 12 | #choose k initial random unequal points 13 | centerPoints = [] 14 | while len(centerPoints) != kVal: 15 | candidate = random.choice(random.choice(vectors)) 16 | if candidate not in centerPoints: 17 | centerPoints.append(candidate) 18 | 19 | #clustering 20 | testPrev = [] 21 | iterationCounter = 0 22 | while True: 23 | clusteringTable = [] 24 | for y in range(len(vectors)): 25 | clusteringTable.append([]) 26 | for x in range(len(vectors[0])): 27 | temp = [] 28 | temp.append(vectors[y][x]) 29 | for k in range(kVal): 30 | #distance from center point 31 | distance = 0 32 | for l in range(len(centerPoints[0])): 33 | distance = distance + abs(centerPoints[k][l]-vectors[y][x][l]) 34 | temp.append(distance) 35 | #assign cluster centroid with min distance 36 | temp.append(temp[1:].index(min(temp[1:]))) 37 | clusteringTable[y].append(temp) 38 | 39 | #check if cluster values changed, exit otherwise 40 | test = [] 41 | for k in range(kVal): 42 | test.append(0) 43 | for itemY in clusteringTable: 44 | for itemX in itemY: 45 | if itemX[-1] == k: 46 | test[k] = test[k] + 1 47 | if testPrev == test: 48 | break 49 | testPrev = test 50 | 51 | #update centroids 52 | for k in range(kVal): 53 | n = 0 54 | vectorTemps = [0]*len(clusteringTable[0][0][0]) 55 | for itemY in clusteringTable: 56 | for itemX in itemY: 57 | if itemX[-1] == k: 58 | for valCounter in range(len(itemX[0])): 59 | vectorTemps[valCounter] = vectorTemps[valCounter] + itemX[0][valCounter] 60 | n = n + 1 61 | #check for 0 division 62 | for valCounter in range(len(vectorTemps)): 63 | if vectorTemps[valCounter] != 0: 64 | vectorTemps[valCounter] = vectorTemps[valCounter]/n 65 | centerPoints[k] = vectorTemps 66 | iterationCounter = iterationCounter + 1 67 | 68 | #Build clustered array 69 | clusteredVectors = [] 70 | for y in range(len(clusteringTable)): 71 | clusteredVectors.append([]) 72 | for x in range(len(clusteringTable[0])): 73 | clusteredVectors[y].append(centerPoints[clusteringTable[y][x][-1]]) 74 | 75 | print(clusteredVectors) 76 | end = time.time() 77 | 78 | #Log info 79 | with open('log.txt', 'a+') as out: 80 | out.write(f"""**K-MEANS RESULTS**\n""") 81 | out.write(f"""Array size: {len(vectors)}x{len(vectors[0])}x{len(vectors[0][0])}\n""") 82 | out.write(f"""Parameters: kVal: {kVal}\n""") 83 | out.write(f"""Centroid averages: {str(centerPoints)}\n""") 84 | out.write(f"""Time taken: {round(end - start)} seconds\n""") -------------------------------------------------------------------------------- /0_read.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PIL import Image 3 | 4 | #Open and read img 5 | imageName = sys.argv[1] 6 | im = Image.open(imageName) 7 | pix = im.load() 8 | xVal,yVal = im.size 9 | pointsArray = [] 10 | 11 | #Map pixel values to array 12 | for y in range(yVal): 13 | pointsArray.append([]) 14 | for x in range(xVal): 15 | pointsArray[y].append(list(pix[x,y])) 16 | 17 | print(pointsArray) 18 | 19 | #Log 20 | with open('log.txt', 'a+') as out: 21 | out.write(f"""**READING IMAGE {imageName}**\n""") -------------------------------------------------------------------------------- /0_save.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from ast import literal_eval 3 | from PIL import Image 4 | 5 | #Convert input to array and read outputName parameter 6 | vectors = literal_eval(sys.stdin.read()) 7 | outputName = sys.argv[1] 8 | modelLen = len(vectors[0][0]) 9 | 10 | #Check supported model and initialize image 11 | if modelLen == 3: 12 | image = Image.new('RGB', (len(vectors[0]),len(vectors))) 13 | elif modelLen == 4: 14 | image = Image.new('RGBA', (len(vectors[0]),len(vectors))) 15 | else: 16 | print("Unsupported model") 17 | sys.exit(1) 18 | 19 | #Map array values to image and save 20 | pix = image.load() 21 | for y in range(len(vectors)): 22 | for x in range(len(vectors[0])): 23 | r = int(round(vectors[y][x][0])) 24 | g = int(round(vectors[y][x][1])) 25 | b = int(round(vectors[y][x][2])) 26 | if modelLen == 3: 27 | pix[x,y] = (r,g,b) 28 | elif modelLen == 4: 29 | a = int(round(vectors[y][x][3])) 30 | pix[x,y] = (r,g,b,a) 31 | 32 | image.save(outputName) 33 | 34 | #Log 35 | with open('log.txt', 'a+') as out: 36 | out.write(f"""**SAVING TO IMAGE {outputName}**\n\n""") -------------------------------------------------------------------------------- /1_butterfly.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomijerenko/clusteringAlgorithms/91f0a1516667d83c3eda6896b2ab79d19c9dacd1/1_butterfly.jpg -------------------------------------------------------------------------------- /1_butterflySmaller.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomijerenko/clusteringAlgorithms/91f0a1516667d83c3eda6896b2ab79d19c9dacd1/1_butterflySmaller.jpg -------------------------------------------------------------------------------- /2_butterfly2means.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomijerenko/clusteringAlgorithms/91f0a1516667d83c3eda6896b2ab79d19c9dacd1/2_butterfly2means.jpg -------------------------------------------------------------------------------- /2_butterfly5means.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomijerenko/clusteringAlgorithms/91f0a1516667d83c3eda6896b2ab79d19c9dacd1/2_butterfly5means.jpg -------------------------------------------------------------------------------- /2_butterfly_dbscan_100_100_e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomijerenko/clusteringAlgorithms/91f0a1516667d83c3eda6896b2ab79d19c9dacd1/2_butterfly_dbscan_100_100_e.jpg -------------------------------------------------------------------------------- /2_butterfly_dbscan_100_100_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomijerenko/clusteringAlgorithms/91f0a1516667d83c3eda6896b2ab79d19c9dacd1/2_butterfly_dbscan_100_100_m.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # K-means and DBSCAN 2 | K-means and DBSCAN are clustering algorithms, which we apply for color segmentation in images. 3 | 4 | K-means tries to find a color representatives for a number of classes given, i.e., most average color for each class, which is most similar to the colors within the class but as different as possible from colors in other classes. 5 | 6 | DBSCAN is so called density-based clustering algorithm, which tries to group similar colors into different classes based on how densely they are positioned together. 7 | 8 | # Files 9 | 0_read.py - reads image and outputs its pixel values as vector array 10 | 11 | 0_kmeans.py - reads vector array, performs k-means and outputs vector array 12 | 13 | 0_dbscan.py - reads vector array, performs dbscan and outputs vector array 14 | 15 | 0_save.py - reads vector array and generates an image 16 | 17 | log.txt - contains clustering information for pictures 18 | 19 | # Dependencies 20 | All packages/libraries should be included in Python 3.6.8 21 | 22 | # Limitations 23 | Image generation with 0_save.py works only for 3 or 4 channel models i.e. RGB (.jpg) or RGBA (.png) 24 | 25 | # Format 26 | k-means and dbscan use following format string (string of 3 dimensional array of size [x,y,n]): 27 | 28 | [[[n0,n1,n2,...],[n0,n1,n2,...]],[[n0,n1,n2,...],[n0,n1,n2,...]],...] 29 | 30 | # Usage 31 | ## k-means 32 | python3 0_read.py | python3 0_kmeans.py | python3 0_save.py 33 | 34 | ## dbscan 35 | Parameter "distanceFunction" uses only "e" for euclidean distance or "m" for maximum distance 36 | 37 | python3 0_read.py | python3 0_dbscan.py | python3 0_save.py 38 | 39 | # Examples 40 | ## k-means 41 | python3 0_read.py 1_butterfly.jpg | python3 0_kmeans.py 2 | python3 0_save.py 2_butterfly2means.jpg 42 | python3 0_read.py 1_butterfly.jpg | python3 0_kmeans.py 5 | python3 0_save.py 2_butterfly5means.jpg 43 | 44 | python3 0_read.py 1_pug.jpg | python3 0_kmeans.py 2 | python3 0_save.py 2_pug2means.jpg 45 | python3 0_read.py 1_pug.jpg | python3 0_kmeans.py 5 | python3 0_save.py 2_pug5means.jpg 46 | 47 | python3 0_read.py 1_cat.jpg | python3 0_kmeans.py 2 | python3 0_save.py 2_cat2means.jpg 48 | python3 0_read.py 1_cat.jpg | python3 0_kmeans.py 5 | python3 0_save.py 2_cat5means.jpg 49 | 50 | ## dbscan 51 | python3 0_read.py 1_butterflySmaller.jpg | python3 0_dbscan.py 100 100 e | python3 0_save.py 2_butterfly_dbscan_100_100_e.jpg 52 | python3 0_read.py 1_butterflySmaller.jpg | python3 0_dbscan.py 100 100 m | python3 0_save.py 2_butterfly_dbscan_100_100_m.jpg 53 | 54 | python3 0_read.py 1_pugSmaller.jpg | python3 0_dbscan.py 100 100 e | python3 0_save.py 2_pugSmaller_dbscan_100_100_e.jpg 55 | python3 0_read.py 1_pugSmaller.jpg | python3 0_dbscan.py 100 100 m | python3 0_save.py 2_pugSmaller_dbscan_100_100_m.jpg 56 | 57 | python3 0_read.py 1_catSmaller.jpg | python3 0_dbscan.py 100 100 e | python3 0_save.py 2_catSmaller_dbscan_100_100_e.jpg 58 | python3 0_read.py 1_catSmaller.jpg | python3 0_dbscan.py 100 100 m | python3 0_save.py 2_catSmaller_dbscan_100_100_m.jpg 59 | 60 | # Copyright 61 | All the images were taken from internet and therefore belong to their respective owners. 62 | 63 | Algorithms were not invented by me, I merely just implemented them. 64 | -------------------------------------------------------------------------------- /log.txt: -------------------------------------------------------------------------------- 1 | **READING IMAGE 1_butterfly.jpg** 2 | **K-MEANS RESULTS** 3 | Array size: 560x1000x3 4 | Parameters: kVal: 2 5 | Centroid averages: [[60.22743608480625, 53.08436481353548, 49.873992732460266], [138.77729990756984, 127.95455202811888, 121.05912386270035]] 6 | Time taken: 29 seconds 7 | **SAVING TO IMAGE 2_butterfly2means.jpg** 8 | 9 | **READING IMAGE 1_butterfly.jpg** 10 | **K-MEANS RESULTS** 11 | Array size: 560x1000x3 12 | Parameters: kVal: 5 13 | Centroid averages: [[60.48641329133019, 53.33849251731227, 51.317998442422244], [86.093229111651, 70.99537340234498, 77.24648427872258], [231.27213649943022, 207.71083187039946, 144.95337090763496], [116.38719346049047, 110.19939351322844, 117.00287421991737], [35.77095343852291, 36.1301895114798, 22.82069536551959]] 14 | Time taken: 144 seconds 15 | **SAVING TO IMAGE 2_butterfly5means.jpg** 16 | 17 | **READING IMAGE 1_pug.jpg** 18 | **K-MEANS RESULTS** 19 | Array size: 394x704x3 20 | Parameters: kVal: 2 21 | Centroid averages: [[84.4090315102273, 80.225333938423, 73.78535537172007], [188.385931860758, 197.59254045701343, 198.94992750018523]] 22 | Time taken: 20 seconds 23 | **SAVING TO IMAGE 2_pug2means.jpg** 24 | 25 | **READING IMAGE 1_pug.jpg** 26 | **K-MEANS RESULTS** 27 | Array size: 394x704x3 28 | Parameters: kVal: 5 29 | Centroid averages: [[38.60394299158936, 39.82297103604944, 35.34402466398031], [210.00599757673666, 224.57566639741518, 224.94297253634895], [121.10210480616166, 117.19150680966699, 110.882298081716], [80.12257141221592, 73.50714980823173, 66.24649378899765], [166.4215707616767, 170.2713397673125, 173.2805553539898]] 30 | Time taken: 87 seconds 31 | **SAVING TO IMAGE 2_pug5means.jpg** 32 | 33 | **READING IMAGE 1_cat.jpg** 34 | **K-MEANS RESULTS** 35 | Array size: 909x1280x3 36 | Parameters: kVal: 2 37 | Centroid averages: [[42.78752249164895, 23.76932257735801, 50.287447810301], [168.22167181416236, 179.56209534757338, 144.2222845935193]] 38 | Time taken: 88 seconds 39 | **SAVING TO IMAGE 2_cat2means.jpg** 40 | 41 | **READING IMAGE 1_cat.jpg** 42 | **K-MEANS RESULTS** 43 | Array size: 909x1280x3 44 | Parameters: kVal: 5 45 | Centroid averages: [[215.5043244399621, 180.85418385746158, 21.436692032639588], [27.54568326981912, 165.71006655574044, 118.11356744136117], [11.627620830192281, 11.088741742930397, 56.26498206865824], [190.5837944147117, 208.79830288519577, 217.80030984668625], [163.8423657768817, 10.231380714062045, 46.77674985853903]] 46 | Time taken: 224 seconds 47 | **SAVING TO IMAGE 2_cat5means.jpg** 48 | 49 | **READING IMAGE 1_butterflySmaller.jpg** 50 | **DBSCAN RESULTS** 51 | Array size: 112x200x3 52 | Parameters: Eps: 100, minPts: 100, distFunc: e 53 | Number of identified clusters: 4 54 | Cluster averages: [[89.48041441952881, 79.98651717286404, 84.22934998580754], [41.54557142857143, 39.176, 28.56642857142857], [222.4697357203751, 196.4381926683717, 134.86445012787723], [252.8962962962963, 248.74074074074073, 185.9185185185185]] 55 | Time taken: 802 seconds 56 | **SAVING TO IMAGE 2_butterfly_dbscan_100_100_e.jpg** 57 | 58 | **READING IMAGE 1_butterflySmaller.jpg** 59 | **DBSCAN RESULTS** 60 | Array size: 112x200x3 61 | Parameters: Eps: 100, minPts: 100, distFunc: m 62 | Number of identified clusters: 4 63 | Cluster averages: [[77.2144628308284, 69.6049289003593, 70.61135570062244], [39.532097948378556, 36.71608206485771, 8.16611515552614], [231.73155555555556, 208.16355555555555, 144.064], [203.0, 93.0, 152.66666666666666]] 64 | Time taken: 610 seconds 65 | **SAVING TO IMAGE 2_butterfly_dbscan_100_100_m.jpg** 66 | 67 | **READING IMAGE 1_pugSmaller.jpg** 68 | **DBSCAN RESULTS** 69 | Array size: 79x141x3 70 | Parameters: Eps: 100, minPts: 100, distFunc: e 71 | Number of identified clusters: 5 72 | Cluster averages: [[70.21237614507385, 66.56459151243223, 60.06318938119275], [129.75154601673336, 128.24772644598036, 122.85994907238997], [196.51323283082078, 207.02345058626466, 208.30854271356785], [246.3090909090909, 246.98181818181817, 246.38181818181818], [132.0, 234.0, 248.0]] 73 | Time taken: 184 seconds 74 | **SAVING TO IMAGE 2_pugSmaller_dbscan_100_100_e.jpg** 75 | 76 | **READING IMAGE 1_pugSmaller.jpg** 77 | **DBSCAN RESULTS** 78 | Array size: 79x141x3 79 | Parameters: Eps: 100, minPts: 100, distFunc: m 80 | Number of identified clusters: 4 81 | Cluster averages: [[77.10169757937756, 73.72571518390443, 67.49386985224773], [162.82958477508652, 163.9163783160323, 161.3892733564014], [212.37509607993852, 231.45734050730206, 233.38739431206764], [126.25, 219.875, 236.125]] 82 | Time taken: 152 seconds 83 | **SAVING TO IMAGE 2_pugSmaller_dbscan_100_100_m.jpg** 84 | 85 | **READING IMAGE 1_catSmaller.jpg** 86 | **DBSCAN RESULTS** 87 | Array size: 91x128x3 88 | Parameters: Eps: 100, minPts: 100, distFunc: e 89 | Number of identified clusters: 19 90 | Cluster averages: [[15.707242582897033, 14.249127399650959, 53.293411867364746], [59.64941569282137, 41.24040066777963, 149.95659432387313], [142.00729335494327, 23.103727714748786, 26.521069692058347], [222.91338582677164, 169.06861642294714, 21.985376827896513], [50.87800687285223, 157.84364261168386, 85.89604810996563], [141.4299674267101, 117.66449511400651, 163.1628664495114], [11.087719298245615, 156.093567251462, 183.46783625730995], [208.39669421487602, 190.9801652892562, 149.92727272727274], [157.88070539419087, 212.7188796680498, 215.899377593361], [132.77884615384616, 206.1778846153846, 35.71634615384615], [244.1056338028169, 220.35915492957747, 238.35915492957747], [190.41666666666666, 73.33333333333333, 21.083333333333332], [231.58823529411765, 13.222222222222221, 104.54575163398692], [243.5, 98.5, 196.75], [212.0121951219512, 0.6707317073170732, 0.6829268292682927], [248.04347826086956, 9.521739130434783, 186.30434782608697], [253.53846153846155, 251.46153846153845, 24.923076923076923], [1.2, 100.2, 171.0], [55.27272727272727, 230.54545454545453, 37.63636363636363]] 91 | Time taken: 209 seconds 92 | **SAVING TO IMAGE 2_catSmaller_dbscan_100_100_e.jpg** 93 | 94 | **READING IMAGE 1_catSmaller.jpg** 95 | **DBSCAN RESULTS** 96 | Array size: 91x128x3 97 | Parameters: Eps: 100, minPts: 100, distFunc: m 98 | Number of identified clusters: 14 99 | Cluster averages: [[25.261609620721554, 19.213506012950972, 59.6745605920444], [66.2258064516129, 34.21863799283154, 157.93548387096774], [167.85098039215686, 30.819607843137256, 33.31372549019608], [218.89866939611053, 177.62947799385876, 28.634595701125896], [57.63629893238434, 165.56725978647688, 93.9103202846975], [204.81975967957277, 188.9706275033378, 171.7343124165554], [148.19911012235818, 210.38820912124584, 217.25361512791991], [4.917808219178082, 157.43835616438355, 199.04794520547946], [246.61065573770492, 220.25819672131146, 240.10655737704917], [239.10471204188482, 12.157068062827225, 131.7539267015707], [137.8, 65.76, 188.96], [249.44444444444446, 23.333333333333332, 199.0], [88.95348837209302, 244.93023255813952, 51.55813953488372], [255.0, 251.0, 11.0]] 100 | Time taken: 153 seconds 101 | **SAVING TO IMAGE 2_catSmaller_dbscan_100_100_m.jpg** 102 | --------------------------------------------------------------------------------