<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
分水嶺技術是一種眾所周知的分割演演算法,特別適用於提取圖片中的相鄰或重疊物件。使用分水嶺方法時,我們必須從使用者定義的標記開始。這些標記可以使用點選手動定義,也可以使用閾值或形態學處理方法定義。
分水嶺技術將輸入影象中的畫素視為基於這些標記的區域性極小值(稱為地形)——該方法從標記向外“淹沒”山谷,直到各種標記的山谷相遇。為了產生準確的分水嶺分割,必須準確地設定標記。
我們使用一種基於OpenCV標記的分水嶺技術來指定哪些谷點應該合併,哪些不應該合併。它是一種互動式影象分割,而不是自動影象分割。
任何灰度影象都可以看作是一個地形表面,高峰代表高強度,山谷代表低強度。首先,用各種顏色的水(標籤)填充孤立的山谷(區域性極小值)。來自不同山谷的河流,顏色明顯不同,隨著水位上升,根據相鄰的山峰(梯度)開始融合。為了避免這種情況,在水與水相遇的地方建造了屏障。你不斷注水,設定障礙,直到所有的山峰都被淹沒,分割結果由建立的障礙決定。
然而,由於影象中存在噪聲或其他異常,該方法會產生過分割的結果。因此,OpenCV建立了一個基於標記的分水嶺方法,允許您選擇哪些谷點應該合併,哪些不應該合併。它是一種互動式影象分割方法。我們所做的就是給每一個前景物體區域貼上不同的標籤,我們不確定的區域是標籤記為0。然後,使用分水嶺演演算法。獲得的結果中,物件的邊界值將為-1。
#include <iostream> #include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <vector> void showImg(const std::string& windowName,const cv::Mat& img){ cv::imshow(windowName,img); } void getBackground(const cv::Mat& source,cv::Mat& dst) { cv::dilate(source,dst,cv::Mat::ones(3,3,CV_8U)); //Kernel 3x3 } void getForeground(const cv::Mat& source,cv::Mat& dst) { cv::distanceTransform(source,dst,cv::DIST_L2,3,CV_32F); cv::normalize(dst, dst, 0, 1, cv::NORM_MINMAX); } void findMarker(const cv::Mat& sureBg,cv::Mat& markers, std::vector<std::vector<cv::Point>>& contours) { cv::findContours(sureBg,contours,cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // Draw the foreground markers for (size_t i = 0,size = contours.size(); i < size; i++) drawContours(markers, contours, static_cast<int>(i), cv::Scalar(static_cast<int>(i)+1), -1); } void getRandomColor(std::vector<cv::Vec3b>& colors,size_t size) { for (int i = 0; i < size ; ++i) { int b = cv::theRNG().uniform(0, 256); int g = cv::theRNG().uniform(0, 256); int r = cv::theRNG().uniform(0, 256); colors.emplace_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r)); } } int main (int argc,char** argv) { // 讀取圖片 if(argc < 2) { std::cerr << "Errorn"; std::cerr << "Provide Input Image:n<program> <inputimage>n"; return -1; } cv::Mat original_img = cv::imread(argv[1]); if(original_img.empty()) { std::cerr << "Errorn"; std::cerr << "Cannot Read Imagen"; return -1; } // 去除影象中的噪聲, 均值偏移模糊(MeanShift)是一種影象邊緣保留濾波演演算法,常用於影象分水嶺分割前的去噪,可顯著提高分水嶺分割效果。 cv::Mat shifted; cv::pyrMeanShiftFiltering(original_img,shifted,21,51); showImg("Mean Shifted",shifted); // 將原始影象轉換為灰度和二值影象 cv::Mat gray_img; cv::cvtColor(original_img,gray_img,cv::COLOR_BGR2GRAY); showImg("GrayIMg",gray_img); cv::Mat bin_img; cv::threshold(gray_img,bin_img,0,255,cv::THRESH_BINARY | cv::THRESH_OTSU); showImg("thres img",bin_img); // 尋找確定的背景影象, 在這一步中,我們找到影象中的背景區域。 cv::Mat sure_bg; getBackground(bin_img,sure_bg); showImg("Sure Background",sure_bg); // 找到確定前景的影象, 對於影象的前景,我們採用距離變換演演算法 cv::Mat sure_fg; getForeground(bin_img,sure_fg); showImg("Sure ForeGround",sure_fg); // 找到標記,在應用分水嶺演演算法之前,我們需要標記。為此,我們將使用opencv中提供的findContour()函數來查詢影象中的標記。 cv::Mat markers = cv::Mat::zeros(sure_bg.size(),CV_32S); std::vector<std::vector<cv::Point>> contours; findMarker(sure_bg,markers,contours); cv::circle(markers, cv::Point(5, 5), 3, cv::Scalar(255), -1); //Drawing Circle around the marker // 應用分水嶺演演算法 cv::watershed(original_img,markers); cv::Mat mark; markers.convertTo(mark, CV_8U); cv::bitwise_not(mark, mark); //黑變白,白變黑 showImg("MARKER",mark); //高亮顯示影象中的標記 std::vector<cv::Vec3b> colors; getRandomColor(colors,contours.size()); //構建結果影象 cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3); // 用隨機的顏色填充已標記的物體 for (int i = 0; i < markers.rows; i++) { for (int j = 0; j < markers.cols; j++) { int index = markers.at<int>(i,j); if (index > 0 && index <= static_cast<int>(contours.size())) dst.at<cv::Vec3b>(i,j) = colors[index-1]; } } showImg("Final Result",dst); cv::waitKey(0); return 0; }
結果展示:
import cv2 as cv import numpy as np import argparse import random as rng rng.seed(12345) parser = argparse.ArgumentParser(description='Code for Image Segmentation with Distance Transform and Watershed Algorithm. Sample code showing how to segment overlapping objects using Laplacian filtering, in addition to Watershed and Distance Transformation') parser.add_argument('--input', help='Path to input image.', default='HFOUG.jpg') args = parser.parse_args() src = cv.imread(cv.samples.findFile(args.input)) if src is None: print('Could not open or find the image:', args.input) exit(0) # Show source image cv.imshow('Source Image', src) cv.waitKey() gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) # noise removal kernel = np.ones((5, 5), np.uint8) opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=2) # 獲取背景圖 sure_bg = opening.copy() # 背景 # Show output image cv.imshow('Black Background Image', sure_bg) # 黑色是背景 cv.waitKey() # 獲取前景圖 dist = cv.distanceTransform(opening, cv.DIST_L2, 3) # Normalize the distance image for range = {0.0, 1.0} # so we can visualize and threshold it cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX) cv.imshow('Distance Transform Image', dist) _, dist = cv.threshold(dist, 0.2, 1.0, cv.THRESH_BINARY) # Dilate a bit the dist image kernel1 = np.ones((3, 3), dtype=np.uint8) dist = cv.dilate(dist, kernel1) cv.imshow('Peaks', dist) # 構建初始markers dist_8u = dist.astype('uint8') # Find total markers contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 建立即將應用分水嶺演演算法的標記影象 markers = np.zeros(dist.shape, dtype=np.int32) # 標記前景 for i in range(len(contours)): cv.drawContours(markers, contours, i, (i + 1), -1) # 輪廓標記從1開始 # 標記背景 cv.circle(markers, (5, 5), 3, 255, -1) # 此處背景標記為255 print("before watershed: ", np.unique(markers)) # 0表示不確定標記區域 # 視覺化markers markers_8u = (markers * 10).astype('uint8') cv.imshow('Markers', markers_8u) cv.waitKey() # 應用分水嶺分割演演算法 markers = cv.watershed(src, markers) print("after watershed: ", np.unique(markers)) # -1表示邊界 # mark = np.zeros(markers.shape, dtype=np.uint8) mark = markers.astype('uint8') mark = cv.bitwise_not(mark) # uncomment this if you want to see how the mark # image looks like at that point # cv.imshow('Markers_v2', mark) # Generate random colors colors = [] for contour in contours: colors.append((rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256))) # Create the result image dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8) # Fill labeled objects with random colors for i in range(markers.shape[0]): for j in range(markers.shape[1]): index = markers[i, j] if index > 0 and index <= len(contours): # -1表示邊界, 255表示背景 dst[i, j, :] = colors[index - 1] # Visualize the final image cv.imshow('Final Result', dst) cv.waitKey()
結果展示:
import cv2 as cv import numpy as np import argparse import random as rng def process_img2(img): img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0.1) img_gray = cv2.medianBlur(img_gray, 5) _, image_binary = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY) kernel = np.ones((7, 7), np.uint8) # sure_bg = cv.morphologyEx(image_binary, cv.MORPH_CLOSE, kernel, iterations=3) sure_bg = cv.dilate(image_binary, kernel, iterations=2) sure_bg = cv.bitwise_not(sure_bg) element = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) image_binary = cv2.morphologyEx(image_binary, cv2.MORPH_OPEN, element) imageSC = cv2.distanceTransform(image_binary, cv2.DIST_L2, 5) imageSC = imageSC.astype(np.uint8) imageSC = cv2.normalize(imageSC, 0, 255, cv2.NORM_MINMAX) _, imageSC = cv2.threshold(imageSC, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY) return imageSC, sure_bg rng.seed(12345) imgPath = "HFOUG.jpg" src = cv.imread(imgPath) shifted = cv2.pyrMeanShiftFiltering(src, 7, 15) if src is None: print('Could not open or find the image:') # print('Could not open or find the image:', args.input) exit(0) # Show source image cv.imshow('Source Image', src) cv.waitKey() opening, sure_bg = process_img2(shifted) # Show output image cv.imshow('Background Image', sure_bg) # 背景 cv.waitKey() # 獲取前景圖 dist = cv.distanceTransform(opening, cv.DIST_L2, 3) # Normalize the distance image for range = {0.0, 1.0} # so we can visualize and threshold it cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX) cv.imshow('Distance Transform Image', dist) _, dist = cv.threshold(dist, 0.3, 1.0, cv.THRESH_BINARY) # Dilate a bit the dist image kernel1 = np.ones((3, 3), dtype=np.uint8) dist = cv.dilate(dist, kernel1) cv.imshow('Peaks', dist) # 構建初始markers dist_8u = dist.astype('uint8') # Find total markers contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 建立即將應用分水嶺演演算法的標記影象 # markers = np.zeros(dist.shape, dtype=np.int32) markers = sure_bg.copy().astype(np.int32) # 標記前景 for i in range(len(contours)): cv.drawContours(markers, contours, i, (i + 1), -1) # 輪廓標記從1開始 # 標記背景 # cv.circle(markers, (5, 5), 3, 255, -1) # 此處背景標記為255 # 視覺化markers print("before watershed: ", np.unique(markers)) # 0表示不確定標記區域 markers_8u = (markers * 10).astype('uint8') cv.imshow('Markers', markers_8u) # 應用分水嶺分割演演算法 markers = cv.watershed(src, markers) print("after watershed: ", np.unique(markers)) # -1表示邊界 # mark = np.zeros(markers.shape, dtype=np.uint8) mark = markers.astype('uint8') mark = cv.bitwise_not(mark) cv.imshow('Markers_v2', mark) # Generate random colors colors = [] for contour in contours: colors.append((rng.randint(0, 256), rng.randint(0, 256), rng.randint(0, 256))) # Create the result image dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8) # Fill labeled objects with random colors for i in range(markers.shape[0]): for j in range(markers.shape[1]): index = markers[i, j] if index > 0 and index <= len(contours): # -1表示邊界, 255表示背景 dst[i, j, :] = colors[index - 1] # Visualize the final image cv.imshow('Final Result', dst) cv.waitKey(0) cv2.destroyAllWindows()
結果展示:
到此這篇關於Python實現基於標記的分水嶺分割演演算法的文章就介紹到這了,更多相關Python分水嶺分割演演算法內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45