SegmentList WaterShedDecomposer::watershed(ImageGray const & gradient,
                                           ImageColor const & image) const {
   SegmentList segments;
   std::unique_ptr<int[]> labels(new int[image.area()]);
   for (int i=0; i<image.area(); ++i) labels[i] = -1;

   int offsets[]{-image.width(), -1, 1, image.width()};

   QList<GradPixelRef> queue;
   for (int i=0; i<gradient.area(); ++i) {
      queue << GradPixelRef{gradient.at(i).l, i};
   }
   std::sort(queue.begin(), queue.end(), lessThan);

   int label;
   int lastLabel = -1;
   int i, j;
   QList<int> neighLbls;
   Pixel * pixel;
   Segment * segment;
   foreach (GradPixelRef const & gradPix, queue) {
      i = gradPix.index;
      pixel = new Pixel(Position(i%image.width(), i/image.width()), image.at(i));

      // gather neighbours
      neighLbls.clear();
      for (int o=0; o<4; ++o) {
         j = i + offsets[o];
         if (image.areNeighbours(i, j) && labels[j] > -1 && !neighLbls.contains(labels[j])) {
            neighLbls << labels[j];
         }
      }

      // treat pixel according to neighbour count
      switch (neighLbls.size()) {
      case 0: // new marker
         labels[i] = ++lastLabel;
         segment = new Segment();
         segment->addPixel(pixel);
         segments << segment;
         break;
      case 1: // add to basin
         label = neighLbls.first();
         labels[i] = label;
         segments.at(label)->addPixel(pixel);
         break;
      default: // new watershed
         // add pixel the segment of the nearest neighbour in color
         double distMin = std::numeric_limits<double>::max();
         double dist;
         int jMin = 0;
         for (int o=0; o<4; ++o) {
            j = i + offsets[o];
            if (image.areNeighbours(i, j) && labels[j] > -1) {
               dist = (image.at(i)-image.at(j)).magnitudeSquared();
               if (dist < distMin) {
                  distMin = dist;
                  jMin = j;
               }
            }
         }
         labels[i] = labels[jMin];
         segments.at(labels[jMin])->addPixel(pixel);

         // beneighbour the segments
         for (int k=0; k<neighLbls.count()-1; ++k) {
            for (int l=k+1; l<neighLbls.count(); ++l) {
               segments.at(neighLbls.at(k))->addNeighbour(segments.at(neighLbls.at(l)));
               segments.at(neighLbls.at(l))->addNeighbour(segments.at(neighLbls.at(k)));
            }
         }
         break;
      }
   }