Watershed algorithm (theoretical + opencv implementation)

The image is represented using one-dimensional coordinates, and while 2D and 3D representations are more complex to visualize, they aren't necessary here. I won’t be using MATLAB, but the concept can still be clearly conveyed.

Step 1: Identify the local minimum points in the image. There are several methods to do this—using a kernel or comparing pixel values directly. It’s not too difficult to implement.

Step 2: Begin filling from the lowest point. Water starts to fill the image, which is essentially a gradient-based process. The marked local minima remain dry, while intermediate points get submerged.

Step 3: Locate the local maximum points, which correspond to the three key positions in the figure.

Step 4: Use these local minima and maxima to segment the image.

Classification map

Simulation result graph

Does it feel like this method is efficient and simple? Let's take a look at the next image:

Using the above steps, we first identified three local minima. In the second step, water started to flood, and all three were recorded. Then, two local maxima were found.

Is that what we wanted?

The answer is no. We don’t want the small, insignificant local minimums, as they may just represent noise. We don’t need such fine-grained segmentation.

This shows some flaws. But don’t worry, OpenCV has an improved version of the watershed algorithm that solves this issue.

Analog classification map

Simulation result graph

Improved watershed algorithm in OpenCV:

To address the issues mentioned earlier, we considered whether we could mark certain details so that they wouldn't be counted as true local minima.

The OpenCV improvement involves manual marking. You can label specific points, and these labels guide the watershed algorithm. The results are very effective!

For example, if we mark two triangles on the image, during the first step, we find three local minima. When water floods, all three are submerged. However, if the middle one isn’t marked, it gets submerged (no lifebuoy), while the other two remain, achieving the desired result.

Note: The markings here use different labels. I used the same triangle for simplicity. Since labels are used for classification, different regions should have different labels. This is reflected in the OpenCV code below.

Analog classification map

Simulation result graph

Note: The actual implementation isn’t complete. I believe the principle is sufficient for now. I will study the algorithm in depth when needed. Once you understand the concept, you can adapt it. The interview is over. That’s okay! Hahaha~

OpenCV Implementation:

#include

#include

using namespace cv;

using namespace std;

void waterSegment(InputArray& _src, OutputArray& _dst, int& noOfSegment);

int main(int argc, char** argv) {

Mat inputImage = imread("coins.jpg");

assert(!inputImage.data);

Mat grayImage, outputImage;

int offSegment;

waterSegment(inputImage, outputImage, offSegment);

waitKey(0);

return 0;

}

void waterSegment(InputArray& _src, OutputArray& _dst, int& noOfSegment)

{

Mat src = _src.getMat();

Mat grayImage;

cvtColor(src, grayImage, CV_BGR2GRAY);

threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);

Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9), Point(-1, -1));

morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);

distanceTransform(grayImage, grayImage, DIST_L2, DIST_MASK_3, 5);

normalize(grayImage, grayImage, 0, 1, NORM_MINMAX);

grayImage.convertTo(grayImage, CV_8UC1);

threshold(grayImage, grayImage, 0, 255, THRESH_BINARY | THRESH_OTSU);

morphologyEx(grayImage, grayImage, MORPH_CLOSE, kernel);

vector> contours;

vector hierarchy;

Mat showImage = Mat::zeros(grayImage.size(), CV_32SC1);

findContours(grayImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));

for (size_t i = 0; i < contours.size(); i++)

{

// Here, static_cast(i+1) is used to assign different labels to each region

drawContours(showImage, contours, static_cast(i), Scalar::all(static_cast(i+1)), 2);

}

Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));

morphologyEx(src, src, MORPH_ERODE, k);

watershed(src, showImage);

// Assign random colors

vector Colors;

for (size_t i = 0; i < contours.size(); i++) {

int r = theRNG().uniform(0, 255);

int g = theRNG().uniform(0, 255);

int b = theRNG().uniform(0, 255);

Colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));

}

// Display

Mat dst = Mat::zeros(showImage.size(), CV_8UC3);

int index = 0;

for (int row = 0; row < showImage.rows; row++) {

for (int col = 0; col < showImage.cols; col++) {

index = showImage.at(row, col);

if (index > 0 && index <= contours.size()) {

dst.at(row, col) = colors[index - 1];

} else if (index == -1) {

dst.at(row, col) = Vec3b(255, 255, 255);

} else {

dst.at(row, col) = Vec3b(0, 0, 0);

}

}

}

}

Watershed merge code:

void segMerge(Mat& image, Mat& segments, int& numSeg)

{

vector Samples;

int newNumSeg = numSeg;

// Initialize the size of the vector

for (size_t i = 0; i < newNumSeg; i++) {

Mat sample;

Samples.push_back(sample);

}

for (size_t i = 0; i < segments.rows; i++) {

for (size_t j = 0; j < segments.cols; j++) {

int index = segments.at(i, j);

if (index >= 0 && index <= newNumSeg) {

if (!samples[index].data) {

samples[index] = image(Rect(j, i, 1, 1));

} else {

vconcat(samples[index], image(Rect(j, i, 2, 1)), samples[index]);

}

}

}

}

vector Hist_bases;

Mat hsv_base;

int h_bins = 35;

int s_bins = 30;

int histSize[2] = { h_bins, s_bins };

float h_range[2] = { 0, 256 };

float s_range[2] = { 0, 180 };

const float* range[2] = { h_range, s_range };

int channels[2] = { 0, 1 };

Mat hist_base;

for (size_t i = 1; i < numSeg; i++) {

if (samples[i].dims > 0) {

cvtColor(samples[i], hsv_base, CV_BGR2HSV);

calcHist(&hsv_base, 1, channels, Mat(), hist_base, 2, histSize, range);

normalize(hist_base, hist_base, 0, 1, NORM_MINMAX);

Hist_bases.push_back(hist_base);

} else {

Hist_bases.push_back(Mat());

}

}

double similarity = 0;

vector Merged;

for (size_t i = 0; i < hist_bases.size(); i++) {

for (size_t j = i + 1; j < hist_bases.size(); j++) {

if (!merged[j]) {

if (hist_bases[i].dims > 0 && hist_bases[j].dims > 0) {

similarity = compareHist(hist_bases[i], hist_bases[j], HISTCMP_BHATTACHARYYA);

if (similarity > 0.8) {

merged[j] = true;

if (i != j) {

newNumSeg--;

for (size_t p = 0; p < segments.rows; p++) {

for (size_t k = 0; k < segments.cols; k++) {

int index = segments.at(p, k);

if (index == j) segments.at(p, k) = i;

}

}

}

}

}

}

}

}

numSeg = newNumSeg;

}

For Huawei Touch

Huawei Touch Screen Price,Mobile Phone Touch Panel,Mobile Phone LCD,Mobile Touch Screen Manufacturers

Dongguan Jili Electronic Technology Co., Ltd. , https://www.jlglassoca.com