[
  {
    "path": "README.md",
    "content": "# PEGBIS (Python Efficient Graph-Based Image Segmentation)\nPython implementation of \"Efficient Graph-Based Image Segmentation\" paper written by P. Felzenszwalb, D. Huttenlocher. \nThe paper is available: http://cs.brown.edu/~pff/papers/seg-ijcv.pdf <br>\nC++ implementation is written by the author and is available on:\nhttp://cs.brown.edu/~pff/segment/ <br>\nThe C++ implementation is much more faster than python implementation (obviously). \n<br>\n<br>\n### Results\nparameters: (Sigma=0.5, K=300, Min=50) <br>\n![alt text](https://github.com/salaee/egbis/blob/master/results/results_1.png)\n<br>\nparameters: (Sigma=0.5, K=300, Min=50) <br>\n![alt text](https://github.com/salaee/egbis/blob/master/results/results_2.png)\n<br>\nparameters: (Sigma=0.5, K=1000, Min=50) <br>\n![alt text](https://github.com/salaee/egbis/blob/master/results/results_3.png)\n<br>\nparameters: (Sigma=0.8, K=500, Min=10) <br>\n![alt text](https://github.com/salaee/egbis/blob/master/results/results_4.png)\n<br>\nparameters: (Sigma=0.5, K=500, Min=50) <br>\n![alt text](https://github.com/salaee/egbis/blob/master/results/results_5.png)\n<br>\n<br>\n### Requirements\nPython 3.5<br>\n\n##### Libraries used: \nscipy and matplotlib\n\n"
  },
  {
    "path": "disjoint_set.py",
    "content": "import numpy as np\n\n\n# disjoint-set forests using union-by-rank and path compression (sort of).\nclass universe:\n    def __init__(self, n_elements):\n        self.num = n_elements\n        self.elts = np.empty(shape=(n_elements, 3), dtype=int)\n        for i in range(n_elements):\n            self.elts[i, 0] = 0  # rank\n            self.elts[i, 1] = 1  # size\n            self.elts[i, 2] = i  # p\n\n    def size(self, x):\n        return self.elts[x, 1]\n\n    def num_sets(self):\n        return self.num\n\n    def find(self, x):\n        y = int(x)\n        while y != self.elts[y, 2]:\n            y = self.elts[y, 2]\n        self.elts[x, 2] = y\n        return y\n\n    def join(self, x, y):\n        # x = int(x)\n        # y = int(y)\n        if self.elts[x, 0] > self.elts[y, 0]:\n            self.elts[y, 2] = x\n            self.elts[x, 1] += self.elts[y, 1]\n            self.elts[y, 1] = self.elts[x, 1]\n        else:\n            self.elts[x, 2] = y\n            self.elts[y, 1] += self.elts[x, 1]\n            self.elts[x, 1] = self.elts[y, 1]\n            if self.elts[x, 0] == self.elts[y, 0]:\n                self.elts[y, 0] += 1\n        self.num -= 1\n"
  },
  {
    "path": "filter.py",
    "content": "import numpy as np\nimport math\nnp.seterr(over='ignore')\n\n\n# some constants\nWIDTH = 4.0\n\n\n# convolve image with gaussian filter\ndef smooth(src, sigma):\n    mask = make_fgauss(sigma)\n    mask = normalize(mask)\n    dst = convolve_even(src, mask)\n    return dst\n\n\n# gaussian filter\ndef make_fgauss(sigma):\n    sigma = max(sigma, 0.01)\n    length = int(math.ceil(sigma * WIDTH)) + 1\n    mask = np.zeros(shape=(length, length), dtype=float)\n    for i in range(length):\n        for j in range(length):\n            mask[i, j] = math.exp(-0.5 * (math.pow(i / sigma, 2) + math.pow(j / sigma, 2)))\n    return mask\n\n# normalize mask so it integrates to one\ndef normalize(mask):\n    sum = 4 * np.sum(np.absolute(mask)) - 3 * abs(mask[0]) - \\\n          2 * np.sum(np.absolute(mask[0, :])) - 2 * np.sum(np.absolute(mask[:, 0]))\n    return np.divide(mask, sum)\n\n\n# convolve src with mask.  output is flipped!\ndef convolve_even(src, mask):\n    output = np.zeros(shape=src.shape, dtype=float)\n    height, width = src.shape\n    length = len(mask)\n\n    for y in range(height):\n        for x in range(width):\n            sum = float(mask[0, 0] * src[y, x])\n            for i in range(0, length):\n                for j in range(0, length):\n                    if i != 0 and j != 0:\n                        sum += mask[i, j] * (src[max(y - j, 0), max(x - i, 0)] + src[max(y - j, 0), min(x + i, width - 1)] + \\\n                                             src[min(y + j, height - 1), min(x + i, width - 1)] + src[min(y + j, height - 1), max(x - i, 0)])\n            output[y, x] = sum\n    return output\n"
  },
  {
    "path": "main.py",
    "content": "from skimage import io\nimport matplotlib.pyplot as plt\nfrom filter import *\nfrom segment_graph import *\nimport time\n\n\n# --------------------------------------------------------------------------------\n# Segment an image:\n# Returns a color image representing the segmentation.\n#\n# Inputs:\n#           in_image: image to segment.\n#           sigma: to smooth the image.\n#           k: constant for threshold function.\n#           min_size: minimum component size (enforced by post-processing stage).\n#\n# Returns:\n#           num_ccs: number of connected components in the segmentation.\n# --------------------------------------------------------------------------------\ndef segment(in_image, sigma, k, min_size):\n    start_time = time.time()\n    height, width, band = in_image.shape\n    print(\"Height:  \" + str(height))\n    print(\"Width:   \" + str(width))\n    smooth_red_band = smooth(in_image[:, :, 0], sigma)\n    smooth_green_band = smooth(in_image[:, :, 1], sigma)\n    smooth_blue_band = smooth(in_image[:, :, 2], sigma)\n\n    # build graph\n    edges_size = width * height * 4\n    edges = np.zeros(shape=(edges_size, 3), dtype=object)\n    num = 0\n    for y in range(height):\n        for x in range(width):\n            if x < width - 1:\n                edges[num, 0] = int(y * width + x)\n                edges[num, 1] = int(y * width + (x + 1))\n                edges[num, 2] = diff(smooth_red_band, smooth_green_band, smooth_blue_band, x, y, x + 1, y)\n                num += 1\n            if y < height - 1:\n                edges[num, 0] = int(y * width + x)\n                edges[num, 1] = int((y + 1) * width + x)\n                edges[num, 2] = diff(smooth_red_band, smooth_green_band, smooth_blue_band, x, y, x, y + 1)\n                num += 1\n\n            if (x < width - 1) and (y < height - 1):\n                edges[num, 0] = int(y * width + x)\n                edges[num, 1] = int((y + 1) * width + (x + 1))\n                edges[num, 2] = diff(smooth_red_band, smooth_green_band, smooth_blue_band, x, y, x + 1, y + 1)\n                num += 1\n\n            if (x < width - 1) and (y > 0):\n                edges[num, 0] = int(y * width + x)\n                edges[num, 1] = int((y - 1) * width + (x + 1))\n                edges[num, 2] = diff(smooth_red_band, smooth_green_band, smooth_blue_band, x, y, x + 1, y - 1)\n                num += 1\n    # Segment\n    u = segment_graph(width * height, num, edges, k)\n\n    # post process small components\n    for i in range(num):\n        a = u.find(edges[i, 0])\n        b = u.find(edges[i, 1])\n        if (a != b) and ((u.size(a) < min_size) or (u.size(b) < min_size)):\n            u.join(a, b)\n\n    num_cc = u.num_sets()\n    output = np.zeros(shape=(height, width, 3))\n\n    # pick random colors for each component\n    colors = np.zeros(shape=(height * width, 3))\n    for i in range(height * width):\n        colors[i, :] = random_rgb()\n\n    for y in range(height):\n        for x in range(width):\n            comp = u.find(y * width + x)\n            output[y, x, :] = colors[comp, :]\n\n    elapsed_time = time.time() - start_time\n    print(\n        \"Execution time: \" + str(int(elapsed_time / 60)) + \" minute(s) and \" + str(\n            int(elapsed_time % 60)) + \" seconds\")\n\n    # displaying the result\n    fig = plt.figure()\n    a = fig.add_subplot(1, 2, 1)\n    plt.imshow(in_image)\n    a.set_title('Original Image')\n    a = fig.add_subplot(1, 2, 2)\n    output = output.astype(int)\n    plt.imshow(output)\n    a.set_title('Segmented Image')\n    plt.show()\n\n\nif __name__ == \"__main__\":\n    sigma = 0.5\n    k = 500\n    min = 50\n    input_path = \"data/paris.jpg\"\n\n    # Loading the image\n    input_image = io.imread(input_path)\n    print(\"Loading is done.\")\n    print(\"processing...\")\n    segment(input_image, sigma, k, min)\n"
  },
  {
    "path": "segment_graph.py",
    "content": "from disjoint_set import *\nimport math\nimport numpy as np\nimport random\n\n\n# ---------------------------------------------------------\n# Segment a graph:\n# Returns a disjoint-set forest representing the segmentation.\n#\n# Inputs:\n#           num_vertices: number of vertices in graph.\n#           num_edges: number of edges in graph\n#           edges: array of edges.\n#           c: constant for threshold function.\n#\n# Output:\n#           a disjoint-set forest representing the segmentation.\n# ------------------------------------------------------------\ndef segment_graph(num_vertices, num_edges, edges, c):\n    # sort edges by weight (3rd column)\n    edges[0:num_edges, :] = edges[edges[0:num_edges, 2].argsort()]\n    # make a disjoint-set forest\n    u = universe(num_vertices)\n    # init thresholds\n    threshold = np.zeros(shape=num_vertices, dtype=float)\n    for i in range(num_vertices):\n        threshold[i] = get_threshold(1, c)\n\n    # for each edge, in non-decreasing weight order...\n    for i in range(num_edges):\n        pedge = edges[i, :]\n\n        # components connected by this edge\n        a = u.find(pedge[0])\n        b = u.find(pedge[1])\n        if a != b:\n            if pedge[2] <= min(threshold[a], threshold[b]):\n                u.join(a, b)\n                a = u.find(a)\n                b = u.find(b)\n                threshold[a] = pedge[2] + get_threshold(u.size(a), c)\n                threshold[b] = threshold[a]\n\n    return u\n\n\ndef get_threshold(size, c):\n    return c / size\n\n\n# returns square of a number\ndef square(value):\n    return value * value\n\n\n# randomly creates RGB\ndef random_rgb():\n    rgb = np.zeros(3, dtype=int)\n    rgb[0] = random.randint(0, 255)\n    rgb[1] = random.randint(0, 255)\n    rgb[2] = random.randint(0, 255)\n    return rgb\n\n\n# dissimilarity measure between pixels\ndef diff(red_band, green_band, blue_band, x1, y1, x2, y2):\n    result = math.sqrt(\n        square(red_band[y1, x1] - red_band[y2, x2]) + square(green_band[y1, x1] - green_band[y2, x2]) + square(\n            blue_band[y1, x1] - blue_band[y2, x2]))\n    return result\n"
  }
]