├── .gitignore ├── 27ab22bdff8437f2dab2f16849abf938.jpg ├── DislaySuperpixel.py ├── DisplayLabel.py ├── DoSuperpixel.py ├── EnforceConnectivity.py ├── Initialize.py ├── LSC.py ├── LSC_demo.py ├── LSC_mex.py ├── README.md ├── Seeds.py ├── __init__.py ├── my_fspecial.py ├── my_imread.py ├── myrgb2lab.py ├── point.py ├── preEnforceConnetivity.py └── test_utils ├── __init__.py ├── compare_matrix.py └── gen_label.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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | # Pycharm settings 92 | .idea/ 93 | 94 | # my settings 95 | test_matlab_data/ 96 | -------------------------------------------------------------------------------- /27ab22bdff8437f2dab2f16849abf938.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shifvb/Finished_Senior_LSC_Python/477f0fd98d8a3f7e565ceef16ea4444ae5a8be05/27ab22bdff8437f2dab2f16849abf938.jpg -------------------------------------------------------------------------------- /DislaySuperpixel.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PIL import Image 3 | 4 | 5 | def DisplaySuperpixel(label_2D: np.ndarray, img: np.ndarray, name): 6 | img = img.copy() 7 | nRows, nCols = label_2D.shape 8 | for i in range(nRows): 9 | for j in range(nCols): 10 | minX = 0 if i - 1 < 0 else i - 1 11 | minY = 0 if j - 1 < 0 else j - 1 12 | maxX = nRows - 1 if i + 1 >= nRows else i + 1 13 | maxY = nCols - 1 if j + 1 >= nCols else j + 1 14 | count = (label_2D[minX:maxX + 1, minY:maxY + 1] != label_2D[i][j]).sum() 15 | if count >= 2: 16 | img[i][j] = [0, 0, 0] 17 | PIL_image = Image.fromarray(img, 'RGB') 18 | PIL_image.show() 19 | PIL_image.save(name.split(".")[0] + "_result" + ".jpg") 20 | -------------------------------------------------------------------------------- /DisplayLabel.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PIL import Image 3 | 4 | 5 | def DisplayLabel(label_2D: np.ndarray, name): 6 | nRows, nCols = label_2D.shape 7 | img = np.zeros([nRows, nCols, 3], dtype=np.uint8) 8 | for i in range(nRows): 9 | for j in range(nCols): 10 | minX = 0 if i - 1 < 0 else i - 1 11 | minY = 0 if j - 1 < 0 else j - 1 12 | maxX = nRows - 1 if i + 1 >= nRows else i + 1 13 | maxY = nCols - 1 if j + 1 >= nCols else j + 1 14 | count = (label_2D[minX:maxX + 1, minY:maxY + 1] != label_2D[i][j]).sum() 15 | if count >= 2: 16 | img[i][j] = [255, 255, 255] 17 | PIL_image = Image.fromarray(img, 'RGB') 18 | PIL_image.show() 19 | PIL_image.save(name.split(".")[0] + "_label" + ".jpg") 20 | -------------------------------------------------------------------------------- /DoSuperpixel.py: -------------------------------------------------------------------------------- 1 | from point import Point 2 | from EnforceConnectivity import EnforceConnectivity 3 | from preEnforceConnetivity import preEnforceConnectivity 4 | import numpy as np 5 | import sys 6 | import time 7 | from scipy.io import loadmat 8 | from test_utils import compare_matrix 9 | import math 10 | from math import pow 11 | import pickle 12 | 13 | DBL_MAX = sys.float_info[0] # max float value 14 | TEST_INITIALIZATION = False 15 | TEST_KMEANS_LABEL = False 16 | FAKE_KMEANS_LABEL = False 17 | TEST_PEC_LABEL = False 18 | FAKE_EC_LABEL = False 19 | 20 | 21 | # Perform weighted kmeans iteratively in the ten dimensional feature space. 22 | def DoSuperpixel(L1: np.ndarray, L2: np.ndarray, a1: np.ndarray, a2: np.ndarray, b1: np.ndarray, b2: np.ndarray, 23 | x1: np.ndarray, x2: np.ndarray, y1: np.ndarray, y2: np.ndarray, 24 | W: np.ndarray, label: np.ndarray, seedArray: list, seedNum: int, nRows: int, nCols: int, StepX: int, 25 | StepY: int, iterationNum: int, thresholdCoef: int, new_label: np.ndarray): 26 | if FAKE_KMEANS_LABEL: 27 | iterationNum = -1 28 | print("\t[{}] [DoSuperpixel.py]: Pre-treatment".format(time.ctime()[11:19])) 29 | dist = np.empty([nRows, nCols], dtype=np.float64) 30 | centerL1 = np.empty([seedNum], dtype=np.float64) 31 | centerL2 = np.empty([seedNum], dtype=np.float64) 32 | centera1 = np.empty([seedNum], dtype=np.float64) 33 | centera2 = np.empty([seedNum], dtype=np.float64) 34 | centerb1 = np.empty([seedNum], dtype=np.float64) 35 | centerb2 = np.empty([seedNum], dtype=np.float64) 36 | centerx1 = np.empty([seedNum], dtype=np.float64) 37 | centerx2 = np.empty([seedNum], dtype=np.float64) 38 | centery1 = np.empty([seedNum], dtype=np.float64) 39 | centery2 = np.empty([seedNum], dtype=np.float64) 40 | WSum = np.empty([seedNum], dtype=np.float64) 41 | clusterSize = np.empty([seedNum], dtype=np.int32) 42 | 43 | print("\t[{}] [DoSuperpixel.py]: Initialization".format(time.ctime()[11:19])) 44 | for i in range(seedNum): 45 | centerL1[i] = 0 46 | centerL2[i] = 0 47 | centera1[i] = 0 48 | centera2[i] = 0 49 | centerb1[i] = 0 50 | centerb2[i] = 0 51 | centerx1[i] = 0 52 | centerx2[i] = 0 53 | centery1[i] = 0 54 | centery2[i] = 0 55 | x = seedArray[i].x 56 | y = seedArray[i].y 57 | minX = int(0 if x - StepX // 4 <= 0 else x - StepX // 4) 58 | minY = int(0 if y - StepY // 4 <= 0 else y - StepY // 4) 59 | maxX = int(nRows - 1 if x + StepX // 4 >= nRows - 1 else x + StepX // 4) 60 | maxY = int(nCols - 1 if y + StepY // 4 >= nCols - 1 else y + StepY // 4) 61 | Count = 0 62 | for j in range(minX, maxX + 1): 63 | for k in range(minY, maxY + 1): 64 | Count += 1 65 | centerL1[i] += L1[j][k] 66 | centerL2[i] += L2[j][k] 67 | centera1[i] += a1[j][k] 68 | centera2[i] += a2[j][k] 69 | centerb1[i] += b1[j][k] 70 | centerb2[i] += b2[j][k] 71 | centerx1[i] += x1[j][k] 72 | centerx2[i] += x2[j][k] 73 | centery1[i] += y1[j][k] 74 | centery2[i] += y2[j][k] 75 | centerL1[i] /= Count 76 | centerL2[i] /= Count 77 | centera1[i] /= Count 78 | centera2[i] /= Count 79 | centerb1[i] /= Count 80 | centerb2[i] /= Count 81 | centerx1[i] /= Count 82 | centerx2[i] /= Count 83 | centery1[i] /= Count 84 | centery2[i] /= Count 85 | 86 | if TEST_INITIALIZATION: 87 | data = loadmat("test_27_DOS_Initialization_centers.mat") 88 | print(compare_matrix.compare_1D_array(centerL1, data["tCenterL1"].reshape([200]), 10, 1e-8), end="", flush=True) 89 | print(compare_matrix.compare_1D_array(centerL2, data["tCenterL2"].reshape([200]), 10, 1e-8), end="", flush=True) 90 | print(compare_matrix.compare_1D_array(centera1, data["tCentera1"].reshape([200]), 10, 1e-8), end="", flush=True) 91 | print(compare_matrix.compare_1D_array(centera2, data["tCentera2"].reshape([200]), 10, 1e-8), end="", flush=True) 92 | print(compare_matrix.compare_1D_array(centerb1, data["tCenterb1"].reshape([200]), 10, 1e-8), end="", flush=True) 93 | print(compare_matrix.compare_1D_array(centerb2, data["tCenterb2"].reshape([200]), 10, 1e-8), end="", flush=True) 94 | print(compare_matrix.compare_1D_array(centerx1, data["tCenterx1"].reshape([200]), 10, 1e-8), end="", flush=True) 95 | print(compare_matrix.compare_1D_array(centerx2, data["tCenterx2"].reshape([200]), 10, 1e-8), end="", flush=True) 96 | print(compare_matrix.compare_1D_array(centery1, data["tCentery1"].reshape([200]), 10, 1e-8), end="", flush=True) 97 | print(compare_matrix.compare_1D_array(centery2, data["tCentery2"].reshape([200]), 10, 1e-8), end="", flush=True) 98 | # exit() 99 | 100 | print("\t[{}] [DoSuperpixel.py]: K-means".format(time.ctime()[11:19])) 101 | for iteration in range(iterationNum + 1): 102 | print("\t\t[{}] [DoSuperpixel.py]: K-means_iter_{}_step_1".format(time.ctime()[11:19], iteration)) 103 | for i in range(nRows): 104 | for j in range(nCols): 105 | dist[i][j] = DBL_MAX 106 | for i in range(seedNum): 107 | # print("\t\t[{}] [DoSuperpixel.py]: K-means_iter_{}_seed{}".format(time.ctime()[11:19], iteration, i)) 108 | x = seedArray[i].x 109 | y = seedArray[i].y 110 | minX = int(0 if x - StepX <= 0 else x - StepX) 111 | minY = int(0 if y - StepY <= 0 else y - StepY) 112 | maxX = int(nRows - 1 if x + StepX >= nRows - 1 else x + StepX) 113 | maxY = int(nCols - 1 if y + StepY >= nCols - 1 else y + StepY) 114 | 115 | # my implementation start 116 | step1_min_x = minX 117 | step1_max_x = maxX + 1 118 | step1_min_y = minY 119 | step1_max_y = maxY + 1 120 | step1_vpow = np.vectorize(lambda _: _ * _) 121 | step1_L1_pow = step1_vpow(L1[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centerL1[i]) 122 | step1_L2_pow = step1_vpow(L2[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centerL2[i]) 123 | step1_a1_pow = step1_vpow(a1[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centera1[i]) 124 | step1_a2_pow = step1_vpow(a2[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centera2[i]) 125 | step1_b1_pow = step1_vpow(b1[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centerb1[i]) 126 | step1_b2_pow = step1_vpow(b2[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centerb2[i]) 127 | step1_x1_pow = step1_vpow(x1[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centerx1[i]) 128 | step1_x2_pow = step1_vpow(x2[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centerx2[i]) 129 | step1_y1_pow = step1_vpow(y1[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centery1[i]) 130 | step1_y2_pow = step1_vpow(y2[step1_min_x:step1_max_x, step1_min_y: step1_max_y] - centery2[i]) 131 | step1_D = step1_L1_pow + step1_L2_pow + step1_a1_pow + step1_a2_pow + step1_b1_pow + step1_b2_pow + \ 132 | step1_x1_pow + step1_x2_pow + step1_y1_pow + step1_y2_pow 133 | 134 | step1_if = (step1_D - dist[step1_min_x: step1_max_x, step1_min_y: step1_max_y] < 0).astype(np.uint16) 135 | step1_neg_if = 1 - step1_if 136 | new_label[step1_min_x: step1_max_x, step1_min_y: step1_max_y] *= step1_neg_if 137 | new_label[step1_min_x: step1_max_x, step1_min_y: step1_max_y] += (step1_if * i) 138 | 139 | dist[step1_min_x: step1_max_x, step1_min_y: step1_max_y] *= step1_neg_if 140 | step1_D_to_plus = step1_D * step1_if 141 | dist[step1_min_x: step1_max_x, step1_min_y: step1_max_y] += step1_D_to_plus 142 | # my implementation end 143 | 144 | # previous implementation start 145 | # for m in range(minX, maxX + 1): 146 | # for n in range(minY, maxY + 1): 147 | # D = (L1[m][n] - centerL1[i]) * (L1[m][n] - centerL1[i]) + \ 148 | # (L2[m][n] - centerL2[i]) * (L2[m][n] - centerL2[i]) + \ 149 | # (a1[m][n] - centera1[i]) * (a1[m][n] - centera1[i]) + \ 150 | # (a2[m][n] - centera2[i]) * (a2[m][n] - centera2[i]) + \ 151 | # (b1[m][n] - centerb1[i]) * (b1[m][n] - centerb1[i]) + \ 152 | # (b2[m][n] - centerb2[i]) * (b2[m][n] - centerb2[i]) + \ 153 | # (x1[m][n] - centerx1[i]) * (x1[m][n] - centerx1[i]) + \ 154 | # (x2[m][n] - centerx2[i]) * (x2[m][n] - centerx2[i]) + \ 155 | # (y1[m][n] - centery1[i]) * (y1[m][n] - centery1[i]) + \ 156 | # (y2[m][n] - centery2[i]) * (y2[m][n] - centery2[i]) 157 | # if D < dist[m][n]: 158 | # label[m * nCols + n] = i 159 | # dist[m][n] = D 160 | # previous implementation end 161 | 162 | print("\t\t[{}] [DoSuperpixel.py]: K-means_iter_{}_step_2".format(time.ctime()[11:19], iteration)) 163 | for i in range(seedNum): 164 | centerL1[i] = 0 165 | centerL2[i] = 0 166 | centera1[i] = 0 167 | centera2[i] = 0 168 | centerb1[i] = 0 169 | centerb2[i] = 0 170 | centerx1[i] = 0 171 | centerx2[i] = 0 172 | centery1[i] = 0 173 | centery2[i] = 0 174 | WSum[i] = 0 175 | clusterSize[i] = 0 176 | seedArray[i].x = 0 177 | seedArray[i].y = 0 178 | 179 | print("\t\t[{}] [DoSuperpixel.py]: K-means_iter_{}_step_3".format(time.ctime()[11:19], iteration)) 180 | label = new_label.copy().reshape([nRows * nCols]) 181 | # my implementation start : tested but slow (~= 17s) 182 | # step3_WL1 = W * L1 183 | # step3_WL2 = W * L2 184 | # step3_Wa1 = W * a1 185 | # step3_Wa2 = W * a2 186 | # step3_Wb1 = W * b1 187 | # step3_Wb2 = W * b2 188 | # step3_Wx1 = W * x1 189 | # step3_Wx2 = W * x2 190 | # step3_Wy1 = W * y1 191 | # step3_Wy2 = W * y2 192 | # for L in range(seedNum): 193 | # if L % 50 == 0: 194 | # print("\t\t\t\t[{}] [DEBUG]: seedNum{}".format(time.ctime()[11:19], L)) 195 | # add_range_matrix = (new_label == L) 196 | # centerL1[L] += (step3_WL1 * add_range_matrix).sum() 197 | # centerL2[L] += (step3_WL2 * add_range_matrix).sum() 198 | # centera1[L] += (step3_Wa1 * add_range_matrix).sum() 199 | # centera2[L] += (step3_Wa2 * add_range_matrix).sum() 200 | # centerb1[L] += (step3_Wb1 * add_range_matrix).sum() 201 | # centerb2[L] += (step3_Wb2 * add_range_matrix).sum() 202 | # centerx1[L] += (step3_Wx1 * add_range_matrix).sum() 203 | # centerx2[L] += (step3_Wx2 * add_range_matrix).sum() 204 | # centery1[L] += (step3_Wy1 * add_range_matrix).sum() 205 | # centery2[L] += (step3_Wy2 * add_range_matrix).sum() 206 | # clusterSize[L] += add_range_matrix.sum() 207 | # WSum[L] += (W * add_range_matrix).sum() 208 | # seedArray[L].x, seedArray[L].y = np.sum(a=np.argwhere(add_range_matrix), axis=0) 209 | # my implementation end 210 | 211 | # previous implementation start (~= 10s) 212 | for i in range(nRows): 213 | for j in range(nCols): 214 | L = label[i * nCols + j] # int 215 | Weight = W[i][j] # double 216 | centerL1[L] += Weight * L1[i][j] 217 | centerL2[L] += Weight * L2[i][j] 218 | centera1[L] += Weight * a1[i][j] 219 | centera2[L] += Weight * a2[i][j] 220 | centerb1[L] += Weight * b1[i][j] 221 | centerb2[L] += Weight * b2[i][j] 222 | centerx1[L] += Weight * x1[i][j] 223 | centerx2[L] += Weight * x2[i][j] 224 | centery1[L] += Weight * y1[i][j] 225 | centery2[L] += Weight * y2[i][j] 226 | clusterSize[L] += 1 227 | WSum[L] += Weight 228 | seedArray[L].x += i 229 | seedArray[L].y += j 230 | # previous implementation end 231 | 232 | print("\t\t[{}] [DoSuperpixel.py]: K-means_iter_{}_step_4".format(time.ctime()[11:19], iteration)) 233 | for i in range(seedNum): 234 | WSum[i] = 1 if WSum[i] == 0 else WSum[i] 235 | clusterSize[i] = 1 if clusterSize[i] == 0 else clusterSize[i] 236 | 237 | print("\t\t[{}] [DoSuperpixel.py]: K-means_iter_{}_step_5".format(time.ctime()[11:19], iteration)) 238 | for i in range(seedNum): 239 | centerL1[i] /= WSum[i] 240 | centerL2[i] /= WSum[i] 241 | centera1[i] /= WSum[i] 242 | centera2[i] /= WSum[i] 243 | centerb1[i] /= WSum[i] 244 | centerb2[i] /= WSum[i] 245 | centerx1[i] /= WSum[i] 246 | centerx2[i] /= WSum[i] 247 | centery1[i] /= WSum[i] 248 | centery2[i] /= WSum[i] 249 | seedArray[i].x /= clusterSize[i] 250 | seedArray[i].y /= clusterSize[i] 251 | 252 | if FAKE_KMEANS_LABEL: 253 | label = pickle.load(open("test_dump_data\\test_27_DOS_label_iter20.pydump", 'rb')) 254 | if TEST_KMEANS_LABEL: 255 | data = loadmat("test_matlab_data\\test_27_DOS_label_after_KMEANS.mat") 256 | print(compare_matrix.compare_2D_matrix(label.reshape([nRows, nCols]).transpose([1, 0]), data["tLabel"])) 257 | exit() 258 | 259 | threshold = int((nRows * nCols) / (seedNum * thresholdCoef)) 260 | preEnforceConnectivity(label, nRows, nCols) 261 | 262 | if TEST_PEC_LABEL: 263 | data = loadmat("test_matlab_data\\test_27_DOS_label_after_PEC.mat") 264 | print(compare_matrix.compare_2D_matrix(label.reshape([nRows, nCols]).transpose([1, 0]), data["tLabel"])) 265 | exit() 266 | 267 | if FAKE_EC_LABEL: 268 | label = loadmat("test_matlab_data\\FINAL.mat")["label"].transpose([1, 0]).reshape([nRows * nCols]) 269 | else: 270 | label = EnforceConnectivity(L1, L2, a1, a2, b1, b2, x1, x2, y1, y2, W, label, threshold, nRows, nCols) 271 | return label 272 | -------------------------------------------------------------------------------- /EnforceConnectivity.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from queue import Queue 3 | import sys 4 | import time 5 | from scipy.io import loadmat 6 | from test_utils import compare_matrix 7 | 8 | DBL_MAX = sys.float_info[0] # max float value 9 | 10 | TEST_LABEL_STEP_1 = False 11 | TEST_LABEL_STEP_2 = False 12 | 13 | 14 | class Superpixel(object): 15 | def __init__(self, L=0, S=0): 16 | self.Label = L 17 | self.Size = S 18 | self.xLoc = [] 19 | self.yLoc = [] 20 | self.Neighbor = [] 21 | 22 | def __eq__(self, other): 23 | if isinstance(other, int): 24 | raise NotImplementedError("Not implemented!(int)") 25 | elif isinstance(other, Superpixel): 26 | return self.Label == other.Label 27 | else: 28 | raise NotImplementedError("Compare between class and other classes not implemented yet!") 29 | 30 | def __str__(self): 31 | return " label={}, Size={}, xLoc={}, yLoc={}, Neighbor={}".format(self.Label, self.Size, 32 | self.xLoc, self.yLoc, 33 | self.Neighbor) 34 | 35 | __repr__ = __str__ 36 | 37 | 38 | def EnforceConnectivity(L1: np.ndarray, L2: np.ndarray, a1: np.ndarray, a2: np.ndarray, b1: np.ndarray, b2: np.ndarray, 39 | x1: np.ndarray, x2: np.ndarray, y1: np.ndarray, y2: np.ndarray, W: np.ndarray, 40 | label: np.ndarray, threshold: int, nRows: int, nCols: int): 41 | print("[{}] EnforceConnectivity...".format(time.ctime()[11:19])) 42 | print("\t[{}] [EnforceConnectivity.py] step_1/3".format(time.ctime()[11:19])) 43 | mask = np.zeros([nRows, nCols], dtype=np.bool) 44 | strayX = [] # unsigned short 45 | strayY = [] # unsigned short 46 | Size = [] # unsigned short 47 | xLoc = [] # unsigned short 48 | yLoc = [] # unsigned short 49 | centerL1 = [] # double 50 | centerL2 = [] # double 51 | centera1 = [] # double 52 | centera2 = [] # double 53 | centerb1 = [] # double 54 | centerb2 = [] # double 55 | centerx1 = [] # double 56 | centerx2 = [] # double 57 | centery1 = [] # double 58 | centery2 = [] # double 59 | centerW = [] # double 60 | 61 | sLabel = -1 # int 62 | 63 | for i in range(nRows): 64 | for j in range(nCols): 65 | if mask[i][j] == 0: 66 | sLabel += 1 67 | Count = 1 68 | centerL1.append(0) 69 | centerL2.append(0) 70 | centera1.append(0) 71 | centera2.append(0) 72 | centerb1.append(0) 73 | centerb2.append(0) 74 | centerx1.append(0) 75 | centerx2.append(0) 76 | centery1.append(0) 77 | centery2.append(0) 78 | centerW.append(0) 79 | strayX.append(i) 80 | strayY.append(j) 81 | Weight = W[i][j] # double 82 | centerL1[sLabel] += L1[i][j] * Weight 83 | centerL2[sLabel] += L2[i][j] * Weight 84 | centera1[sLabel] += a1[i][j] * Weight 85 | centera2[sLabel] += a2[i][j] * Weight 86 | centerb1[sLabel] += b1[i][j] * Weight 87 | centerb2[sLabel] += b2[i][j] * Weight 88 | centerx1[sLabel] += x1[i][j] * Weight 89 | centerx2[sLabel] += x2[i][j] * Weight 90 | centery1[sLabel] += y1[i][j] * Weight 91 | centery2[sLabel] += y2[i][j] * Weight 92 | centerW[sLabel] += W[i][j] 93 | L = label[i * nCols + j] 94 | label[i * nCols + j] = sLabel 95 | mask[i][j] = 1 96 | xLoc.append(i) 97 | yLoc.append(j) 98 | while len(xLoc) > 0: 99 | x = xLoc.pop(0) 100 | y = yLoc.pop(0) 101 | minX = 0 if x - 1 <= 0 else x - 1 102 | maxX = nRows - 1 if x + 1 >= nRows - 1 else x + 1 103 | minY = 0 if y - 1 <= 0 else y - 1 104 | maxY = nCols - 1 if y + 1 >= nCols - 1 else y + 1 105 | for m in range(minX, maxX + 1): 106 | for n in range(minY, maxY + 1): 107 | if not mask[m][n] and label[m * nCols + n] == L: 108 | Count += 1 109 | xLoc.append(m) 110 | yLoc.append(n) 111 | mask[m][n] = 1 112 | label[m * nCols + n] = sLabel 113 | Weight = W[m][n] 114 | centerL1[sLabel] += L1[m][n] * Weight 115 | centerL2[sLabel] += L2[m][n] * Weight 116 | centera1[sLabel] += a1[m][n] * Weight 117 | centera2[sLabel] += a2[m][n] * Weight 118 | centerb1[sLabel] += b1[m][n] * Weight 119 | centerb2[sLabel] += b2[m][n] * Weight 120 | centerx1[sLabel] += x1[m][n] * Weight 121 | centerx2[sLabel] += x2[m][n] * Weight 122 | centery1[sLabel] += y1[m][n] * Weight 123 | centery2[sLabel] += y2[m][n] * Weight 124 | centerW[sLabel] += W[m][n] 125 | Size.append(Count) 126 | centerL1[sLabel] /= centerW[sLabel] 127 | centerL2[sLabel] /= centerW[sLabel] 128 | centera1[sLabel] /= centerW[sLabel] 129 | centera2[sLabel] /= centerW[sLabel] 130 | centerb1[sLabel] /= centerW[sLabel] 131 | centerb2[sLabel] /= centerW[sLabel] 132 | centerx1[sLabel] /= centerW[sLabel] 133 | centerx2[sLabel] /= centerW[sLabel] 134 | centery1[sLabel] /= centerW[sLabel] 135 | centery2[sLabel] /= centerW[sLabel] 136 | sLabel += 1 137 | Count = 0 138 | 139 | if TEST_LABEL_STEP_1: 140 | data = loadmat("test_matlab_data\\test_27_EC_label_step1.mat") 141 | print( 142 | compare_matrix.compare_2D_matrix(label.reshape([nRows, nCols]), data["tLabel"].transpose([1, 0]), 1000, 0)) 143 | exit() 144 | 145 | print("\t[{}] [EnforceConnectivity.py] step_2/3".format(time.ctime()[11:19])) 146 | Sarray = [] # vector Sarray; 147 | for i in range(sLabel): 148 | if Size[i] < threshold: 149 | x = strayX[i] 150 | y = strayY[i] 151 | L = label[x * nCols + y] 152 | mask[x][y] = 0 153 | indexMark = 0 154 | S = Superpixel(L, Size[i]) 155 | S.xLoc.append(x) 156 | S.yLoc.append(y) 157 | while indexMark < len(S.xLoc): 158 | x = S.xLoc[indexMark] 159 | y = S.yLoc[indexMark] 160 | indexMark += 1 161 | minX = 0 if x - 1 <= 0 else x - 1 162 | maxX = nRows - 1 if x + 1 >= nRows - 1 else x + 1 163 | minY = 0 if y - 1 <= 0 else y - 1 164 | maxY = nCols - 1 if y + 1 >= nCols - 1 else y + 1 165 | for m in range(minX, maxX + 1): 166 | for n in range(minY, maxY + 1): 167 | if mask[m][n] and label[m * nCols + n] == L: 168 | mask[m][n] = 0 169 | S.xLoc.append(m) 170 | S.yLoc.append(n) 171 | elif label[m * nCols + n] != L: 172 | NewLabel = label[m * nCols + n] 173 | if NewLabel not in S.Neighbor: 174 | S.Neighbor.insert(0, NewLabel) 175 | Sarray.append(S) 176 | 177 | if TEST_LABEL_STEP_2: 178 | data = loadmat("test_matlab_data\\test_27_EC_label_step2.mat") 179 | print( 180 | compare_matrix.compare_2D_matrix(label.reshape([nRows, nCols]), data["tLabel"].transpose([1, 0]), 1000, 0)) 181 | exit() 182 | 183 | print("\t[{}] [EnforceConnectivity.py] step_3/3".format(time.ctime()[11:19])) 184 | S = 0 185 | while len(Sarray) > 0: 186 | MinDist = DBL_MAX 187 | Label1 = int(Sarray[S].Label) 188 | Label2 = -1 189 | for I in range(len(Sarray[S].Neighbor)): 190 | D = (centerL1[Label1] - centerL1[Sarray[S].Neighbor[I]]) * (centerL1[Label1] - centerL1[Sarray[S].Neighbor[I]]) + \ 191 | (centerL2[Label1] - centerL2[Sarray[S].Neighbor[I]]) * (centerL2[Label1] - centerL2[Sarray[S].Neighbor[I]]) + \ 192 | (centera1[Label1] - centera1[Sarray[S].Neighbor[I]]) * (centera1[Label1] - centera1[Sarray[S].Neighbor[I]]) + \ 193 | (centera2[Label1] - centera2[Sarray[S].Neighbor[I]]) * (centera2[Label1] - centera2[Sarray[S].Neighbor[I]]) + \ 194 | (centerb1[Label1] - centerb1[Sarray[S].Neighbor[I]]) * (centerb1[Label1] - centerb1[Sarray[S].Neighbor[I]]) + \ 195 | (centerb2[Label1] - centerb2[Sarray[S].Neighbor[I]]) * (centerb2[Label1] - centerb2[Sarray[S].Neighbor[I]]) + \ 196 | (centerx1[Label1] - centerx1[Sarray[S].Neighbor[I]]) * (centerx1[Label1] - centerx1[Sarray[S].Neighbor[I]]) + \ 197 | (centerx2[Label1] - centerx2[Sarray[S].Neighbor[I]]) * (centerx2[Label1] - centerx2[Sarray[S].Neighbor[I]]) + \ 198 | (centery1[Label1] - centery1[Sarray[S].Neighbor[I]]) * (centery1[Label1] - centery1[Sarray[S].Neighbor[I]]) + \ 199 | (centery2[Label1] - centery2[Sarray[S].Neighbor[I]]) * (centery2[Label1] - centery2[Sarray[S].Neighbor[I]]) 200 | if abs(D - MinDist) > 1e-6: 201 | MinDist = D 202 | Label2 = Sarray[S].Neighbor[I] 203 | W1 = centerW[Label1] 204 | W2 = centerW[Label2] 205 | W = W1 + W2 206 | centerL1[Label2] = (W2 * centerL1[Label2] + W1 * centerL1[Label1]) / W 207 | centerL2[Label2] = (W2 * centerL2[Label2] + W1 * centerL2[Label1]) / W 208 | centera1[Label2] = (W2 * centera1[Label2] + W1 * centera1[Label1]) / W 209 | centera2[Label2] = (W2 * centera2[Label2] + W1 * centera2[Label1]) / W 210 | centerb1[Label2] = (W2 * centerb1[Label2] + W1 * centerb1[Label1]) / W 211 | centerb2[Label2] = (W2 * centerb2[Label2] + W1 * centerb2[Label1]) / W 212 | centerx1[Label2] = (W2 * centerx1[Label2] + W1 * centerx1[Label1]) / W 213 | centerx2[Label2] = (W2 * centerx2[Label2] + W1 * centerx2[Label1]) / W 214 | centery1[Label2] = (W2 * centery1[Label2] + W1 * centery1[Label1]) / W 215 | centery2[Label2] = (W2 * centery2[Label2] + W1 * centery2[Label1]) / W 216 | centerW[Label2] = W 217 | 218 | for i in range(len(Sarray[S].xLoc)): 219 | x = Sarray[S].xLoc[i] 220 | y = Sarray[S].yLoc[i] 221 | label[x * nCols + y] = Label2 222 | 223 | if Superpixel(Label2) in Sarray: 224 | Stmp = Sarray.index(Superpixel(Label2)) 225 | Size[Label2] = Size[Label1] + Size[Label2] 226 | if Size[Label2] >= threshold: 227 | del Sarray[Stmp] 228 | del Sarray[S] 229 | else: 230 | Sarray[Stmp].xLoc.extend(Sarray[S].xLoc) 231 | Sarray[Stmp].yLoc.extend(Sarray[S].yLoc) 232 | Sarray[Stmp].Neighbor.extend(Sarray[S].Neighbor) 233 | Sarray[Stmp].Neighbor = list(set(Sarray[Stmp].Neighbor)) 234 | Sarray[Stmp].Neighbor.sort() 235 | I = Sarray[Stmp].Neighbor.index(Label1) 236 | del Sarray[Stmp].Neighbor[I] 237 | I = Sarray[Stmp].Neighbor.index(Label2) 238 | del Sarray[Stmp].Neighbor[I] 239 | del Sarray[S] 240 | else: 241 | del Sarray[S] 242 | 243 | for i in range(len(Sarray)): 244 | if Label1 in Sarray[i].Neighbor and Label2 in Sarray[i].Neighbor: 245 | I = Sarray[i].Neighbor.index(Label1) 246 | del Sarray[i].Neighbor[I] 247 | elif Label1 in Sarray[i].Neighbor and Label2 not in Sarray[i].Neighbor: 248 | I = Sarray[i].Neighbor.index(Label1) 249 | Sarray[i].Neighbor[I] = Label2 250 | S = 0 251 | return label 252 | -------------------------------------------------------------------------------- /Initialize.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import time 4 | 5 | # PI = math.pi 6 | 7 | 8 | PI = 3.1415926 9 | 10 | 11 | def Initialize(L: np.ndarray, a: np.ndarray, b: np.ndarray, nRows: int, nCols: int, StepX: int, StepY: int, 12 | Color: float, Distance: float): 13 | print("\t[{}] [Initialize.py] step_1/3".format(time.ctime()[11:19])) 14 | vcos = np.vectorize(math.cos) 15 | vsin = np.vectorize(math.sin) 16 | thetaL = (np.resize(L.copy(), [nRows, nCols]) / 255.0 * PI / 2.0).astype(np.float64) 17 | thetaa = (np.resize(a.copy(), [nRows, nCols]) / 255.0 * PI / 2.0).astype(np.float64) 18 | thetab = (np.resize(b.copy(), [nRows, nCols]) / 255.0 * PI / 2.0).astype(np.float64) 19 | thetax = np.empty([nRows, nCols], dtype=np.float64) 20 | thetay = np.empty([nRows, nCols], dtype=np.float64) 21 | for i in range(thetax.shape[0]): 22 | thetax[i, :] = i 23 | for j in range(thetay.shape[1]): 24 | thetay[:, j] = j 25 | thetax = (thetax / StepX) * PI / 2 26 | thetay = (thetay / StepY) * PI / 2 27 | L1 = Color * vcos(thetaL) 28 | L2 = Color * vsin(thetaL) 29 | a1 = Color * vcos(thetaa) * 2.55 30 | a2 = Color * vsin(thetaa) * 2.55 31 | b1 = Color * vcos(thetab) * 2.55 32 | b2 = Color * vsin(thetab) * 2.55 33 | x1 = Distance * vcos(thetax) 34 | x2 = Distance * vsin(thetax) 35 | y1 = Distance * vcos(thetay) 36 | y2 = Distance * vsin(thetay) 37 | 38 | print("\t[{}] [Initialize.py] step_2/3".format(time.ctime()[11:19])) 39 | size = nRows * nCols 40 | sigmaL1 = L1.sum() / size 41 | sigmaL2 = L2.sum() / size 42 | sigmaa1 = a1.sum() / size 43 | sigmaa2 = a2.sum() / size 44 | sigmab1 = b1.sum() / size 45 | sigmab2 = b2.sum() / size 46 | sigmax1 = x1.sum() / size 47 | sigmax2 = x2.sum() / size 48 | sigmay1 = y1.sum() / size 49 | sigmay2 = y2.sum() / size 50 | 51 | 52 | print("\t[{}] [Initialize.py] step_3/3".format(time.ctime()[11:19])) 53 | W = L1 * sigmaL1 + L2 * sigmaL2 + a1 * sigmaa1 + a2 * sigmaa2 + b1 * sigmab1 + \ 54 | b2 * sigmab2 + x1 * sigmax1 + x2 * sigmax2 + y1 * sigmay1 + y2 * sigmay2 55 | L1 /= W 56 | L2 /= W 57 | a1 /= W 58 | a2 /= W 59 | b1 /= W 60 | b2 /= W 61 | x1 /= W 62 | x2 /= W 63 | y1 /= W 64 | y2 /= W 65 | return L1.astype(np.float32), L2.astype(np.float32), a1.astype(np.float32), \ 66 | a2.astype(np.float32), b1.astype(np.float32), b2.astype(np.float32), \ 67 | x1.astype(np.float32), x2.astype(np.float32), y1.astype(np.float32), \ 68 | y2.astype(np.float32), W.astype(np.float64) 69 | -------------------------------------------------------------------------------- /LSC.py: -------------------------------------------------------------------------------- 1 | from myrgb2lab import myrgb2lab 2 | import cmath 3 | from Seeds import gen_seeds 4 | from Initialize import Initialize 5 | import numpy as np 6 | from DoSuperpixel import DoSuperpixel 7 | from scipy.io import loadmat 8 | from test_utils import compare_matrix 9 | from test_utils.compare_matrix import compare_2D_matrix 10 | import time 11 | 12 | TEST_RGB2LAB = False 13 | TEST_INITIALIZATION = False 14 | FAKE_INITIALIZATION = False 15 | 16 | 17 | # LSC superpixel segmentation algorithm 18 | def LSC(I: np.ndarray, nRows: int, nCols: int, superpixelnum: int, ratio: float, label: np.ndarray): 19 | new_label = np.empty([nRows, nCols], dtype=np.uint16) 20 | print("[{}] Setting Parameter...".format(time.ctime()[11:19])) 21 | colorCoefficient = 20 22 | distCoefficient = colorCoefficient * ratio 23 | seedNum = superpixelnum 24 | iterationNum = 20 25 | thresholdCoef = 4 26 | 27 | print("[{}] Translating image from RGB format to LAB format...".format(time.ctime()[11:19])) 28 | # img = I.transpose([2, 1, 0]) 29 | # R = np.copy(img[0]).reshape([nRows * nCols]) 30 | # G = np.copy(img[1]).reshape([nRows * nCols]) 31 | # B = np.copy(img[2]).reshape([nRows * nCols]) 32 | # L = np.empty([nRows * nCols], dtype=np.uint8) 33 | # a = np.empty([nRows * nCols], dtype=np.uint8) 34 | # b = np.empty([nRows * nCols], dtype=np.uint8) 35 | # myrgb2lab(L, a, b, nRows, nCols, I) 36 | L, a, b = myrgb2lab(I, nRows, nCols) 37 | 38 | if TEST_RGB2LAB: 39 | data = loadmat(r"test_matlab_data\test_27_RGBLAB.mat") 40 | # print(compare_2D_matrix(R.copy().reshape([nRows, nCols]).transpose([1, 0]), data["tR"], 10, 1), end="", flush=True) 41 | # print(compare_2D_matrix(G.copy().reshape([nRows, nCols]).transpose([1, 0]), data["tG"], 10, 1), end="", flush=True) 42 | # print(compare_2D_matrix(B.copy().reshape([nRows, nCols]).transpose([1, 0]), data["tB"], 10, 1), end="", flush=True) 43 | print(compare_2D_matrix(L.copy().reshape([nRows, nCols]).transpose([1, 0]), data["tL"], 10, 1), end="", flush=True) 44 | print(compare_2D_matrix(a.copy().reshape([nRows, nCols]).transpose([1, 0]), data["ta"], 10, 1), end="", flush=True) 45 | print(compare_2D_matrix(b.copy().reshape([nRows, nCols]).transpose([1, 0]), data["tb"], 10, 1), end="", flush=True) 46 | exit() 47 | 48 | print("[{}] Producing Seeds...".format(time.ctime()[11:19])) 49 | ColNum = int(cmath.sqrt(seedNum * nCols / nRows).real) 50 | RowNum = int(seedNum / ColNum) 51 | StepX = int(nRows / RowNum) 52 | StepY = int(nCols / ColNum) 53 | # seedArray = [] 54 | # newSeedNum = Seeds_deprecated(nRows, nCols, RowNum, ColNum, StepX, StepY, seedNum, seedArray) 55 | seedArray = gen_seeds(row_num=nRows, col_num=nCols, seed_num=seedNum) 56 | newSeedNum = len(seedArray) 57 | 58 | print("[{}] Initialization...".format(time.ctime()[11:19])) 59 | L1, L2, a1, a2, b1, b2, x1, x2, y1, y2, W = Initialize(L, a, b, nRows, nCols, StepX, StepY, colorCoefficient, 60 | distCoefficient) 61 | 62 | if FAKE_INITIALIZATION: 63 | data = loadmat("test_matlab_data\\test_27_Init.mat") 64 | L1 = data["tL1"] 65 | L2 = data["tL2"] 66 | a1 = data["ta1"] 67 | a2 = data["ta2"] 68 | b1 = data["tb1"] 69 | b2 = data["tb2"] 70 | x1 = data["tx1"] 71 | x2 = data["tx2"] 72 | y1 = data["ty1"] 73 | y2 = data["ty2"] 74 | W = data["tW"] 75 | if TEST_INITIALIZATION: 76 | data = loadmat("test_matlab_data\\test_27_Init.mat") 77 | print(compare_matrix.compare_2D_matrix(L1, data["tL1"], 10, 1e-4), end="", flush=True) 78 | print(compare_matrix.compare_2D_matrix(L2, data["tL2"], 10, 1e-4), end="", flush=True) 79 | print(compare_matrix.compare_2D_matrix(a1, data["ta1"], 10, 1e-4), end="", flush=True) 80 | print(compare_matrix.compare_2D_matrix(a2, data["ta2"], 10, 1e-4), end="", flush=True) 81 | print(compare_matrix.compare_2D_matrix(b1, data["tb1"], 10, 1e-4), end="", flush=True) 82 | print(compare_matrix.compare_2D_matrix(b2, data["tb2"], 10, 1e-4), end="", flush=True) 83 | print(compare_matrix.compare_2D_matrix(x1, data["tx1"], 10, 1e-4), end="", flush=True) 84 | print(compare_matrix.compare_2D_matrix(x2, data["tx2"], 10, 1e-4), end="", flush=True) 85 | print(compare_matrix.compare_2D_matrix(y1, data["ty1"], 10, 1e-4), end="", flush=True) 86 | print(compare_matrix.compare_2D_matrix(y2, data["ty2"], 10, 1e-4), end="", flush=True) 87 | print(compare_matrix.compare_2D_matrix(W, data["tW"], 10, 1e-1), end="\n", flush=True) 88 | exit() 89 | 90 | del L 91 | del a 92 | del b 93 | 94 | print("[{}] Producing Superpixel...".format(time.ctime()[11:19])) 95 | return DoSuperpixel(L1, L2, a1, a2, b1, b2, x1, x2, y1, y2, W, label, seedArray, newSeedNum, nRows, nCols, StepX, 96 | StepY, iterationNum, thresholdCoef, new_label) 97 | -------------------------------------------------------------------------------- /LSC_demo.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.ndimage import correlate 3 | from my_imread import my_imread 4 | from my_fspecial import my_fspecial 5 | from LSC_mex import LSC_mex 6 | from test_utils import compare_matrix 7 | from PIL import Image 8 | from scipy.io import loadmat 9 | from DisplayLabel import DisplayLabel 10 | from DislaySuperpixel import DisplaySuperpixel 11 | 12 | 13 | # [MATLAB] name='02'; 14 | name = '27ab22bdff8437f2dab2f16849abf938.jpg' 15 | # [MATLAB] img=imread([name,'.jpg']); 16 | img = my_imread(name) 17 | # [MATLAB] gaus=fspecial('gaussian',3); 18 | gaus = my_fspecial('gaussian', 3) 19 | # [MATLAB] I=imfilter(img,gaus); 20 | I = correlate(img.astype(np.float64), 21 | gaus.reshape(gaus.shape[0], gaus.shape[1], 1), 22 | mode="constant").round().astype(np.uint8) 23 | # [MATLAB] superpixelNum=200; 24 | superpixelNum = 200 25 | # [MATLAB] ratio=0.075; 26 | ratio = 0.075 27 | # [MATLAB] label=LSC_mex(I,superpixelNum,ratio); 28 | label = LSC_mex(I, superpixelNum, ratio) 29 | 30 | # label_2D = loadmat("FINAL.mat")["label"] 31 | nRows, nCols, _ = img.shape 32 | label_2D = label.reshape([nCols, nRows]).transpose([1, 0]) 33 | 34 | # [MATLAB]DisplaySuperpixel(label,img,name); 35 | DisplaySuperpixel(label_2D, img, name) 36 | # [MATLAB]DisplayLabel(label,name); 37 | DisplayLabel(label_2D, name) 38 | -------------------------------------------------------------------------------- /LSC_mex.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from LSC import LSC 3 | 4 | 5 | def LSC_mex(I, superpixelNum: int, ratio: float): 6 | """ 7 | mex wrapper of mexFuncion 8 | :param I: image data 9 | :param superpixelNum: number of superpixel to generate 10 | :param ratio: I don't know the algorithm... just ratio 11 | :return: label data 12 | """ 13 | return mexFunction(I, superpixelNum, ratio) 14 | 15 | 16 | def mexFunction(I: np.ndarray, superpixelNum: int, ratio=0.1): 17 | assert len(I.shape) == 3, "The input image must be in CIERGB form" 18 | assert I.dtype == np.uint8, "The input image must be in CIERGB form" 19 | nRows, nCols, _ = I.shape 20 | pixel = nRows * nCols 21 | label = np.empty([pixel], dtype=np.uint16) 22 | return LSC(I, nCols, nRows, superpixelNum, ratio, label) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linear-Spectral-Clustering-Superpixel-Segmentation-Algorithm_Python 2 | A Python implementation of LSC algorithm by shifvb 3 | 4 | Developed on Python3.6(Windows x86_64), should be run well on Python3.3+ 5 | 6 | If you want to see the demo, just run LSC_demo.py 7 | 8 | ## (C) Zhengqin Li, Jiansheng Chen, 2014 9 | [HomePage](http://jschenthu.weebly.com/projects.html) 10 | 11 | You are free to use, change or redistribute this code for any non-commrecial purposes. 12 | If you use this software,please cite thefollowing in any resulting publication and email us: 13 | 14 | [1] Zhengqin Li, Jiansheng Chen, Superpixel Segmentation using Linear Spectral Clustering, IEEE Conference on Computer Vision and Pattern Recognition (CVPR), Jun. 2015 15 | 16 | (C) Zhengqin Li, Jiansheng Chen, 2014 17 | 18 | li-zq12@mails.tsinghua.edu.cn 19 | 20 | jschenthu@mail.tsinghua.edu.cn 21 | 22 | Tsinghua University 23 | 24 | ### Abstract 25 | 26 | ![](http://jschenthu.weebly.com/uploads/2/4/1/1/24110356/6091384_1_orig.jpg) 27 | 28 | We present in this paper a superpixel segmentation algorithm called Linear Spectral Clustering (LSC), which produces compact and uniform superpixels with low computational costs. 29 | Basically, a normalized cuts formulation of the superpixel segmentation is adopted based on a similarity metric that measures the color similarity and space proximity between image pixels. 30 | However, instead of using the traditional eigen-based algorithm, we approximate the similarity metric using a kernel function leading to an explicitly mapping of pixel values and coordinates into a high dimensional feature space. 31 | We prove that by appropriately weighting each point in this feature space, the objective functions of weighted K-means and normalized cuts share the same optimum point. 32 | As such, it is possible to optimize the cost function of normalized cuts by iteratively applying simple K-means clustering in the proposed feature space. 33 | LSC is of linear computational complexity and high memory efficiency and is able to preserve global properties of images. 34 | Experimental results show that LSC performs equally well or better than state of the art superpixel segmentation algorithms in terms of several commonly used evaluation metrics in image segmentation. 35 | 36 | ### Grant: 37 | 38 | National Natural Science Foundation of China Project (#61101152) 39 | 40 | Tsinghua University Initiative Scientific Research Program Project (#20131089382) 41 | 42 | Beijing Higher Education Young Elite Teacher Project (#YETP0104) 43 | 44 | ### Publication: 45 | 46 | Zhengqin Li, Jiansheng Chen, Superpixel Segmentation using Linear Spectral Clustering, IEEE Conference on Computer Vision and Pattern Recognition (CVPR), Jun. 2015 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Seeds.py: -------------------------------------------------------------------------------- 1 | from point import Point 2 | import math 3 | 4 | 5 | # def Seeds_deprecated(nRows: int, nCols: int, Row_num: int, Col_num: int, Row_step: int, Col_step: int, seed_num: int, 6 | # point_array: list): 7 | # Row_remain = nRows - Row_step * Row_num 8 | # Col_remain = nCols - Col_step * Col_num 9 | # t1 = 1 10 | # t2 = 1 11 | # count = 0 12 | # centerx = -1 13 | # centery = -1 14 | # for i in range(Row_num): 15 | # t2 = 1 16 | # for j in range(Col_num): 17 | # centerx = int(i * Row_step + 0.5 * Row_step + t1) 18 | # centery = int(j * Col_step + 0.5 * Col_step + t2) 19 | # centerx = nRows - 1 if (centerx >= nRows - 1) else centerx 20 | # centery = nCols - 1 if (centery >= nCols - 1) else centery 21 | # if t2 < Col_remain: 22 | # t2 += 1 23 | # point_array.append(Point(centerx, centery)) 24 | # count += 1 25 | # if t1 < Row_remain: 26 | # t1 += 1 27 | # return count 28 | 29 | 30 | def gen_seeds(row_num: int, col_num: int, seed_num: int) -> tuple: 31 | """ 32 | generate seeds 33 | :param row_num: the image row number 34 | :param col_num: the image column number 35 | :param seed_num: the seed number should be generated 36 | :return: 37 | seeds_list: a tuple contains all the seeds 38 | """ 39 | seeds_list = [] 40 | num_seeds_col = int(math.sqrt(seed_num * col_num / row_num)) 41 | num_seeds_row = int(seed_num / num_seeds_col) 42 | step_x = int(row_num / num_seeds_row) 43 | step_y = int(col_num / num_seeds_col) 44 | row_remain = row_num - num_seeds_row * step_x 45 | col_remain = col_num - num_seeds_col * step_y 46 | current_row_remain = 1 47 | current_col_remain = 1 48 | current_seeds_count = 0 49 | for i in range(num_seeds_row): 50 | for j in range(num_seeds_col): 51 | center_x = int(i * step_x + 0.5 * step_x + current_row_remain) 52 | center_y = int(j * step_y + 0.5 * step_y + current_col_remain) 53 | if center_x > row_num - 1: 54 | center_x = row_num - 1 55 | if center_y > col_num - 1: 56 | center_y = col_num - 1 57 | if current_col_remain < col_remain: 58 | current_col_remain += 1 59 | seeds_list.append(Point(x=center_x, y=center_y)) 60 | current_seeds_count += 1 61 | if current_row_remain < row_remain: 62 | current_row_remain += 1 63 | return tuple(seeds_list) 64 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shifvb/Finished_Senior_LSC_Python/477f0fd98d8a3f7e565ceef16ea4444ae5a8be05/__init__.py -------------------------------------------------------------------------------- /my_fspecial.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def matlab_style_gauss2D(shape=(3, 3), sigma=0.5): 5 | """ 6 | 2D gaussian mask - should give the same result as MATLAB's 7 | fspecial('gaussian',[shape],[sigma]) 8 | """ 9 | m, n = [(ss - 1.) / 2. for ss in shape] 10 | y, x = np.ogrid[-m:m + 1, -n:n + 1] 11 | h = np.exp(-(x * x + y * y) / (2. * sigma * sigma)) 12 | h[h < np.finfo(h.dtype).eps * h.max()] = 0 13 | sumh = h.sum() 14 | if sumh != 0: 15 | h /= sumh 16 | return h 17 | 18 | 19 | def my_fspecial(type_str, shape, *args): 20 | if type_str != "gaussian": 21 | raise ValueError("type {} not implemented!".format(type_str)) 22 | if isinstance(shape, int): 23 | shape = [shape, shape] 24 | if len(args) == 0: 25 | return matlab_style_gauss2D(shape) 26 | return matlab_style_gauss2D(shape, args[0]) 27 | 28 | 29 | def test(): 30 | r = my_fspecial('gaussian', 5) 31 | print(r) 32 | 33 | if __name__ == '__main__': 34 | test() 35 | -------------------------------------------------------------------------------- /my_imread.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import numpy as np 3 | 4 | 5 | # def my_imread_deprecated(path): 6 | # import cv2 7 | # arr = cv2.imread(path) 8 | # for i in range(arr.shape[0]): # change BGR to RGB image 9 | # for j in range(arr.shape[1]): 10 | # t = arr[i][j][2] 11 | # arr[i][j][2] = arr[i][j][0] 12 | # arr[i][j][0] = t 13 | # return arr 14 | 15 | 16 | def my_imread(path: str): 17 | return np.asarray(Image.open(path), dtype=np.uint8) 18 | -------------------------------------------------------------------------------- /myrgb2lab.py: -------------------------------------------------------------------------------- 1 | from math import pow 2 | import numpy as np 3 | import math 4 | from skimage import color 5 | 6 | 7 | # # Change from RGB colour space to LAB colour space 8 | # def RGB2XYZ_deprecated_v1(sR: int, sG: int, sB: int): 9 | # R = sR / 255.0 10 | # G = sG / 255.0 11 | # B = sB / 255.0 12 | # 13 | # r = R / 12.92 if R <= 0.04045 else math.pow((R + 0.055) / 1.055, 2.4) 14 | # g = G / 12.92 if G <= 0.04045 else math.pow((G + 0.055) / 1.055, 2.4) 15 | # b = B / 12.92 if B <= 0.04045 else math.pow((B + 0.055) / 1.055, 2.4) 16 | # 17 | # X = r * 0.412453 + g * 0.357580 + b * 0.180423 18 | # Y = r * 0.212671 + g * 0.715160 + b * 0.072169 19 | # Z = r * 0.019334 + g * 0.119193 + b * 0.950227 20 | # return X, Y, Z 21 | # 22 | # 23 | # def RGB2LAB_deprecated_v1(sR: int, sG: int, sB: int): 24 | # X, Y, Z = RGB2XYZ_deprecated_v1(sR, sG, sB) 25 | # 26 | # epsilon = 0.008856 # actual CIE standard 27 | # kappa = 903.3 # actual CIE standard 28 | # 29 | # Xr = 0.950456 # reference white 30 | # Yr = 1.0 # reference white 31 | # Zr = 1.088754 # reference white 32 | # 33 | # xr = X / Xr 34 | # yr = Y / Yr 35 | # zr = Z / Zr 36 | # 37 | # fx = math.pow(xr, 1.0 / 3.0) if xr > epsilon else (kappa * xr + 16.0) / 116.0 38 | # fy = math.pow(yr, 1.0 / 3.0) if yr > epsilon else (kappa * yr + 16.0) / 116.0 39 | # fz = math.pow(zr, 1.0 / 3.0) if zr > epsilon else (kappa * zr + 16.0) / 116.0 40 | # 41 | # lval = (116.0 * fy - 16.0) / 100 * 255 + 0.5 42 | # aval = 500.0 * (fx - fy) + 128 + 0.5 43 | # bval = 200.0 * (fy - fz) + 128 + 0.5 44 | # return int(lval), int(aval), int(bval) 45 | 46 | 47 | # def myrgb2lab_deprecated(L: np.ndarray, A: np.ndarray, B: np.ndarray, nRows: int, nCols: int, I: np.ndarray) -> None: 48 | # epsilon = 0.008856 # actual CIE standard 49 | # kappa = 903.3 # actual CIE standard 50 | # xyz_data = color.rgb2xyz(I).transpose([2, 1, 0]) 51 | # xr = xyz_data[0].copy().reshape([nRows * nCols]) / 0.950456 52 | # yr = xyz_data[1].copy().reshape([nRows * nCols]) 53 | # zr = xyz_data[2].copy().reshape([nRows * nCols]) / 1.088754 54 | # for i in range(nCols * nRows): 55 | # temp_xr = xr[i] 56 | # temp_yr = yr[i] 57 | # temp_zr = zr[i] 58 | # fx = pow(temp_xr, 1.0 / 3.0) if temp_xr > epsilon else (kappa * temp_xr + 16.0) / 116.0 59 | # fy = pow(temp_yr, 1.0 / 3.0) if temp_yr > epsilon else (kappa * temp_yr + 16.0) / 116.0 60 | # fz = pow(temp_zr, 1.0 / 3.0) if temp_zr > epsilon else (kappa * temp_zr + 16.0) / 116.0 61 | # L[i] = (116.0 * fy - 16.0) / 100 * 255 + 0.5 62 | # A[i] = 500.0 * (fx - fy) + 128 + 0.5 63 | # B[i] = 200.0 * (fy - fz) + 128 + 0.5 64 | 65 | 66 | def myrgb2lab(I: np.ndarray, row_num: int, col_num: int): 67 | """ 68 | change rgb to lab format 69 | :param I: rgb format image 70 | :return: 71 | L: L channel, range from 0 to 255, dtype: uint8, shape: (row_num * col_num,) 72 | a: a channel, range from 0 to 255, dtype: uint8, shape: (row_num * col_num,) 73 | b: b channel, range from 0 to 255, dtype: uint8, shape: (row_num * col_num,) 74 | """ 75 | lab_img = color.rgb2lab(I).transpose([2, 1, 0]) 76 | L = lab_img[0].copy().reshape([row_num * col_num]) 77 | a = lab_img[1].copy().reshape([row_num * col_num]) 78 | b = lab_img[2].copy().reshape([row_num * col_num]) 79 | L /= (100 / 255) # L is [0, 100], change it to [0, 255] 80 | L += 0.5 81 | a += 128 + 0.5 # A is [-128, 127], change it to [0, 255] 82 | b += 128 + 0.5 # B is [-128, 127], change it to [0, 255] 83 | return L.astype(np.uint8), a.astype(np.uint8), b.astype(np.uint8) 84 | -------------------------------------------------------------------------------- /point.py: -------------------------------------------------------------------------------- 1 | class Point(object): 2 | def __init__(self, x=0, y=0): 3 | self.x = x 4 | self.y = y 5 | 6 | def __str__(self): 7 | return ": x={}, y={}".format(self.x, self.y) 8 | 9 | __repr__ = __str__ 10 | -------------------------------------------------------------------------------- /preEnforceConnetivity.py: -------------------------------------------------------------------------------- 1 | # Enforce Connectivity by merging very small superpixels with their neighbors 2 | import numpy as np 3 | import time 4 | 5 | 6 | def preEnforceConnectivity(label: np.ndarray, nRows: int, nCols: int): 7 | print("[{}] preEnforceConnectivity...".format(time.ctime()[11:19])) 8 | dx8 = (-1, -1, 0, 1, 1, 1, 0, -1) 9 | dy8 = (0, -1, -1, -1, 0, 1, 1, 1) 10 | adj = 0 11 | Bond = 20 12 | mask = np.zeros([nRows, nCols], dtype=np.bool) # bool type(C++) 13 | xLoc = [] 14 | yLoc = [] 15 | for i in range(nRows): 16 | for j in range(nCols): 17 | if mask[i][j] == 0: 18 | L = label[i * nCols + j] 19 | for k in range(8): 20 | x = i + dx8[k] 21 | y = j + dy8[k] 22 | if 0 <= x <= nRows - 1 and 0 <= y <= nCols - 1: 23 | if mask[x][y] and label[x * nCols + y] != L: 24 | adj = label[x * nCols + y] 25 | break 26 | mask[i][j] = 1 27 | xLoc.append(i) 28 | yLoc.append(j) 29 | indexMarker = 0 30 | while indexMarker < len(xLoc): 31 | x = xLoc[indexMarker] 32 | y = yLoc[indexMarker] 33 | indexMarker += 1 34 | minX = 0 if x - 1 <= 0 else x - 1 35 | maxX = nRows - 1 if x + 1 >= nRows - 1 else x + 1 36 | minY = 0 if y - 1 <= 0 else y - 1 37 | maxY = nCols - 1 if y + 1 >= nCols - 1 else y + 1 38 | for m in range(minX, maxX + 1): 39 | for n in range(minY, maxY + 1): 40 | if not mask[m][n] and label[m * nCols + n] == L: 41 | mask[m][n] = 1 42 | xLoc.append(m) 43 | yLoc.append(n) 44 | if indexMarker < Bond: 45 | for k in range(len(xLoc)): 46 | x = xLoc[k] 47 | y = yLoc[k] 48 | label[x * nCols + y] = adj 49 | xLoc.clear() 50 | yLoc.clear() 51 | del mask 52 | -------------------------------------------------------------------------------- /test_utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .gen_label import gen_label 2 | -------------------------------------------------------------------------------- /test_utils/compare_matrix.py: -------------------------------------------------------------------------------- 1 | from scipy.io import loadmat 2 | from math import fabs 3 | 4 | 5 | def compare_3D_matrix(python_array, 6 | matlab_array, 7 | max_count_num=100, 8 | diff_threshold=0): 9 | A = python_array 10 | B = matlab_array 11 | assert A.shape == B.shape, "A:{}, B:{}".format(A.shape, B.shape) 12 | assert A.dtype == B.dtype, "A:{}, B:{}".format(A.dtype, B.dtype) 13 | 14 | count = 0 15 | for r in range(A.shape[0]): 16 | for c in range(A.shape[1]): 17 | for k in range(A.shape[2]): 18 | if count > max_count_num: 19 | return count 20 | diff = A[r][c][k] - B[r][c][k] if A[r][c][k] > B[r][c][k] else B[r][c][k] - A[r][c][k] 21 | if diff > diff_threshold: 22 | count += 1 23 | print("[DEBUG] pos: {}, A:{}, B: {}, diff:{}".format((r, c, k), A[r][c][k], B[r][c][k], diff)) 24 | return count 25 | 26 | 27 | def compare_2D_matrix(python_array, 28 | matlab_array, 29 | max_count_num=100, 30 | diff_threshold=0): 31 | A = python_array 32 | B = matlab_array 33 | assert A.shape == B.shape, "A:{}, B:{}".format(A.shape, B.shape) 34 | assert A.dtype == B.dtype, "A:{}, B:{}".format(A.dtype, B.dtype) 35 | 36 | count = 0 37 | for r in range(A.shape[0]): 38 | for c in range(A.shape[1]): 39 | if count > max_count_num: 40 | return count 41 | diff = A[r][c] - B[r][c] if A[r][c] > B[r][c] else B[r][c] - A[r][c] 42 | if diff > diff_threshold: 43 | count += 1 44 | print("[DEBUG] pos: {}, A:{}, B: {}, diff:{}".format((r, c), A[r][c], B[r][c], diff)) 45 | return count 46 | 47 | 48 | def compare_1D_array(python_array, matlab_array, max_count_num=100, diff_threshold=0): 49 | A = python_array 50 | B = matlab_array 51 | assert A.dtype == B.dtype, "A:{}, B:{}".format(A.dtype, B.dtype) 52 | assert A.shape == B.shape, "A:{}, B:{}".format(A.shape, B.shape) 53 | count = 0 54 | for i in range(A.shape[0]): 55 | if count > max_count_num: 56 | return count 57 | diff = A[i] - B[i] if A[i] > B[i] else B[i] - A[i] 58 | if diff > diff_threshold: 59 | count += 1 60 | print("[DEBUG] pos: {}, A:{}, B: {}, diff:{}".format(i, A[i], B[i], diff)) 61 | return count 62 | -------------------------------------------------------------------------------- /test_utils/gen_label.py: -------------------------------------------------------------------------------- 1 | def gen_label(nRows: int, nCols: int, default_value=-1, gen_function=None): 2 | if gen_function is not None: 3 | return [gen_function(_) for _ in range(nRows * nCols)] 4 | return [default_value for _ in range(nRows * nCols)] 5 | --------------------------------------------------------------------------------