寻找山脊线算法

以下的改进是http://www.imagepy.org/的作者原创,我只是对其理解之后改进和说明,欢迎大家使用这个小软件!

C++版本已经编写完毕,有需要的朋友请留邮箱

算法含义:

提取一个山脉的山峰,图像中就是在距离变换之后提取局部最亮的线

算法应用:

用作图像的断裂修补

方法:

1.直接用分水岭算法进行区域分割。

2.类似分水岭的算法,找山脊线。

分水岭算法:

这里有个哲学的思想,还是开头大佬点拨我的,修补前景就等价于分割背景,一定要理解这句话的含义,我们把白色的直线当做前景修复对象,那么就等价于用白线去分割黑色的背景。

opencv的分水岭算法不是很好,必须找到合适的mask才能达到要求。

skimage的分水岭相对较好,效果差强人意吧.

分水岭结果如上图,在这基础上阈值一下就可以得到我们的图像(这个图像有多余的线条),然后再和原始图像做差值得到修补的线条,把修补的很长线条去除之后再加上原图就可以了。

大家也发现了一个问题,手动的元素比较多,预处理做的不好(局部极大值找的不好,我用的自适应阈值,效果不好就不上图了),上图的效果算比较好的,至少可以解决我们的问题。

代码:

1 #include

2 #include

3 using namespacestd;4 using namespacecv;5

6 #define WINDOW_NAME "【程序窗口1】"

7 Mat g_maskImage, g_srcImage;8 Point prevPt(-1, -1);9 static void on_Mouse(int event, int x, int y, int flags, void*);10

11 intmain()12 {13 //载入原图,初始化掩膜和灰度图

14 g_srcImage = imread("123.png");15 imshow(WINDOW_NAME, g_srcImage);16 Mat srcImage, grayImage;17 g_srcImage.copyTo(srcImage);18 cvtColor(g_srcImage, g_maskImage, COLOR_BGR2GRAY);19 cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);20 g_maskImage = Scalar::all(0);21 //设置鼠标回调函数

22 setMouseCallback(WINDOW_NAME, on_Mouse);23 //轮询按键

24 while (1)25 {26 int c = waitKey(0);27 if ((char)c == 27) break;28 if ((char)c == '2') { //按键‘2’, 恢复源图

29 g_maskImage = Scalar::all(0);30 srcImage.copyTo(g_srcImage);31 imshow("image", g_srcImage);32 }33 if ((char)c == '1' || (char)c == ' ') {34 //定义一些参数

35 vector >contours;36 vectorhierarchy;37 //寻找轮廓

38 findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);39 //轮廓为空时的处理

40 if (contours.empty()) continue;41 //复制掩膜

42 Mat maskImage(g_maskImage.size(), CV_32S);43 maskImage = Scalar::all(0);44 //循环绘制出轮廓

45 int compCount = 0;46 for (int index = 0; index >= 0; index = hierarchy[index][0], compCount++)47 drawContours(maskImage, contours, index, Scalar::all(compCount + 1), -1, LINE_8, hierarchy);48 //compCount为零时的处理

49 if (compCount == 0)50 continue;51 //生成随机颜色

52 vectorcolorTab;53 for (unsigned int i = 0; i < compCount; i++) {54 int b = theRNG().uniform(0, 255);55 int g = theRNG().uniform(0, 255);56 int r = theRNG().uniform(0, 255);57 colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));58 }59 //计算处理时间并输出到窗口中

60 double dTime = (double)getTickCount();61 watershed(srcImage, maskImage);62 dTime = (double)getTickCount() -dTime;63 printf("处理时间=%gms\n", dTime*1000. /getTickFrequency());64 //双层循环,将分水岭图像遍历存入watershedImage中

65 Mat watershedImage(maskImage.size(), CV_8UC3);66 for (unsigned int i = 0; i < maskImage.rows; i++)67 for (unsigned int j = 0; j < maskImage.cols; j++)68 {69 int index = maskImage.at(i, j);70 if (index == -1)71 watershedImage.at(i, j) = Vec3b(255, 255, 255);72 else if (index <= 0 || index >compCount)73 watershedImage.at(i, j) = Vec3b(0, 0, 0);74 else

75 watershedImage.at(i, j) = colorTab[index - 1];76 }77 //混合灰度图和分水岭效果图并显示最终的窗口78 //watershedImage = watershedImage*0.5 + grayImage*0.5;

79 imshow("watershed transform", watershedImage);80 }81 }82 return 0;83 }84

85 static void on_Mouse(int event, int x, int y, int flags, void*)86 {87 //处理鼠标不在窗口中的情况

88 if (x < 0 || x >= g_srcImage.cols || y < 0 || y >= g_srcImage.rows) return;89 //处理鼠标左键相关消息

90 if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON)) //左键抬起动作或处于没有按下状态

91 prevPt = Point(-1, -1);92 else if (event == EVENT_LBUTTONDOWN) //左键按下动作

93 prevPt =Point(x, y);94 //鼠标左键按下并移动,绘制出白色线条

95 else if (event == EVENT_MOUSEMOVE && (flags &EVENT_FLAG_LBUTTON))96 {97 Point pt(x, y);98 if (prevPt.x < 0) prevPt =pt;99 line(g_maskImage, prevPt, pt, Scalar::all(255), 5);100 line(g_srcImage, prevPt, pt, Scalar::all(255), 5);101 prevPt =pt;102 imshow(WINDOW_NAME, g_srcImage);103 }104

105 }

找山脊线算法:

类似于骨架提取、图像细化、中轴线提取等思想

把白色的线当做前景图,然后进行距离变换,之后进行全局阈值把前景全部包括在mask里面,接着利用原始图对mask进行操作,具体的细节看代码分析。

以下是效果图,很完美的解决了问题。

算法的原理:

第一步:对图像进行距离变换---->>>之后阈值化(全局阈值就好)

例如:效果图

注释:这里不进行距离变换,直接进行阈值化也可以,但是对于特殊的图像效果很差。因为掩膜的应用可以结合分水岭[0:255]进行操作,没有距离变换的掩膜就只有255,这就和图像细化或者中轴线算法差不多了。

原图

距离变换图

掩膜图

结果图

掩膜图

结果图

第二步:对二值化图像进行标记(做mask)

边界标记成4,这里为了防止越界,按照opencv c++的说法就是img.rows-1....

背景标记成2

前景标记成0,这里是除去图形边界的前景

图形边界标记成1

第三步:分水岭算法

进行操作的是距离图+掩膜图(dis+mask)

这里使用涨水的方式对像素进行操作,从刚开始标记的边界1开始向内部涨水,由于距离变换之后映射到【0-255】,所以水从0开始涨,知道255为止:

这一步关键一点是内部的凹池,采用类似水漫算法进行:

就这样把低于水平线的像素都填充为背景2了,当然按照其他的行列循环也是可以的,怎么写都行!

注释:

这里有几个小技巧:

1.因为是从边界开始判断的,比如边界高度为100,那么水就从100开始涨,不然0-99是没意义的,具体代码见程序说217行。

2.每一次的循环最少是图形一圈,因为这里以涨水为标准,比如上面的以原图二值化为掩膜,那么图像都为255,这时候就是一次性得出山脊线。

简单的说就是每次处理的单位都是一个水平线,比如水平线是200,那么边界高度为201就是下一次涨水再进行处理。

3.掩膜取得合适得出的图像效果最好,也不是越大越好,具体例子就不举了。

4.因为这里查表遵循的是不断裂原则(貌似图像细化和骨架提取都是这样),回头想想你把掩膜图进行细化然后再和原图叠加也能进行图像修复哈!

下面是我自己的一个误区:(这里大家看完上面说明就知道以下的问题是不存在的)

当然这里的算法不稳定,一些特殊的图像还是不行,比如下面的图像,水还没涨到200,图就已经分割完了,程序是水涨一次就分割一次,但是这个图明显都大于200.

1 from scipy.misc importimread2 importmatplotlib.pyplot as plt3 importnumpy as np4 from numba importjit5 from skimage.data importcamera6 importscipy.ndimage as ndimg7 from time importtime8 importcv29 from scipy.ndimage importlabel, generate_binary_structure10

11 strc = np.ones((3, 3), dtype=np.bool)12

13

14 defcount(n):15 a = [(n>>i) & 1 for i in range(8)]16 if sum(a)<=1:returnFalse17 if a[1] & a[3] & a[5] & a[7]:returnFalse18 a = np.array([[a[0],a[1],a[2]],19 [a[7], 0 ,a[3]],20 [a[6],a[5],a[4]]])21 n = label(a, strc)[1]22 return n<2

23

24

25 lut = np.array([count(n) for n in range(256)])26 lut = np.dot(lut.reshape((-1, 8)), [1, 2, 4, 8, 16, 32, 64, 128]).astype(np.uint8)27

28

29 #@jit

30 defcore(n):31 a = np.zeros(8, dtype=np.uint8)32 for i in range(8):33 a[i] = (n >> i * 2) & 3

34 if a[1] == 1 and a[0] == 0: a[0] = 1

35 if a[1] == 1 and a[2] == 0: a[0] = 1

36 if a[3] == 1 and a[2] == 0: a[2] = 1

37 if a[3] == 1 and a[4] == 0: a[4] = 1

38 if a[5] == 1 and a[4] == 0: a[4] = 1

39 if a[5] == 1 and a[6] == 0: a[6] = 1

40 if a[7] == 1 and a[6] == 0: a[6] = 1

41 if a[7] == 1 and a[0] == 0: a[0] = 1

42 for i in range(8):43 if a[i] == 0 or a[i] == 2: a[i] =044 if a[i] == 1 or a[i] == 3: a[i] = 1

45 return np.dot(a, [1, 2, 4, 8, 16, 32, 64, 128])46

47

48 index = np.array([core(i) for i in range(65536)], dtype=np.uint8)49

50 #51 '''

52 lut = np.array([223, 221, 1, 221, 1, 221, 1, 221, 1, 0, 0, 0, 1, 221, 1, 221, 207, 204,53 0, 204, 207, 51, 207, 1, 207, 204, 0, 204, 207, 51, 207, 51], dtype=np.uint8)54 '''

55

56 #57 defnbs8(h, w):58 return np.array([-w - 1, -w, -w + 1, +1, +w + 1, +w, +w - 1, -1], dtype=np.int32)59

60 #类似创建2X2内核(上下左右4个数)

61 defnbs4(h, w):62 return np.array([-1, -w, 1, w], dtype=np.int32)63

64

65 #@jit

66

67 deffill(img, msk, p, level, pts, s, nbs, buf):68 n =069 cur =070 buf[0] =p71 msk[p] = 2

72 bs = 1

73 while cur <74 p="buf[cur]75" for dp innbs:76 if msk continue>

77 if img[p + dp] <78 buf p msk dp>

80 bs += 1

81 if bs ==len(buf):82 buf[:bs - cur] =buf[cur:bs]83 bs -=cur84 cur =085 else:86 pts[s + n] = p +dp87 msk[ p + dp] = 1

88 n += 1

89 cur += 1

90 returnn91

92

93 #@jit

94 defcheck(msk, p, nbs, lut):95 c =096 s =097 for i in range(8):98 v = msk[p +nbs[i]]99 #if v==0: c|=(0<

100 if v == 1: c |= (1 << i * 2)101 if v == 2: c |= (2 << i * 2)102 if v == 3: c |= (3 << i * 2)103 v =index[c]104 if lut[v // 8] >> v % 8 & 1:105 msk[p] = 2

106 else:107 msk[p] = 3

108

109

110 #@jit

111 defstep(img, msk, pts, s, level, nbs, nbs8):112 ddd =0113 cur =0114 buf = np.zeros(10240, dtype=np.int64)115 while cur <116 p="pts[cur]117" if img>level:118 cur += 1116>

119 continue

120

121 filled =False122 for dp innbs:123 if msk[p + dp] == 4: msk[p] = 2

124 if msk[p + dp] ==0:125 if img[p + dp] >=level:126 msk[p + dp] = 1

127 pts[s] = p +dp128 s += 1

129 else:130 n =fill(img, msk, p, level, pts, s, nbs, buf)131 s +=n132 filled =True133

134 iffilled:135 cur += 1

136 continue

137 elif msk[p] == 1:138 check(msk, p, nbs8, lut)139

140 cur += 1

141 returncur142

143

144 @jit145 defclear(msk, pts, s):146 cur =0147 for c inrange(s):148 if msk[pts[c]] == 1:149 pts[cur] =pts[c]150 cur += 1

151 returncur152

153

154 @jit155 defcollect(img, mark, nbs, pts):156 bins = np.zeros(img.max() + 1, dtype=np.uint32)157 cur =0158 '''

159 for p in range(len(mark)):160 if mark[p] != 0: continue161 for dp in nbs:162 if mark[p + dp] == 1:163 mark[p] = 2164 '''

165 for p inrange(len(mark)):166 if mark[p] == 1: mark[p] = 2

167 for p inrange(len(mark)):168 if mark[p] != 0: continue

169 s =0170 for dp innbs:171 if mark[p + dp] == 2:172 s += 1

173 mark[p] = 1

174 pts[cur] =p175 cur += 1

176 break

177 if s == 0: bins[img[p]] += 1

178 returncur, bins179

180

181 #@jit

182 defwatershed(img, mark):183 oimg, omark =img, mark184 ndim =img.ndim185 mark[[0, -1], :] = 4

186 mark[:, [0, -1]] = 4

187

188 nb4 = nbs4(*img.shape)189 nb8 = nbs8(*img.shape)190 acc = np.cumprod((1,) + img.shape[::-1][:-1])[::-1]191 img =img.ravel()192 mark =mark.ravel()193

194 pts = np.zeros(131072, dtype=np.int64)195 s, bins =collect(img, mark, nb4, pts)196 for level inrange(len(bins)):197 if bins[level] == 0: continue

198 s =clear(mark, pts, s)199 s =step(img, mark, pts, s, level, nb4, nb8)200 '''

201 dem = cv2.imread('gis1.jpg',0)202 plt.imshow(dem, cmap='gray')203 mark = (dem > 150).astype(np.uint8)204 plt.imshow(mark, cmap='gray')205 plt.show()206

207 watershed(dem, mark.copy())208 start = time()209 watershed(dem, mark)210 print(time() - start)211 dem[:] = dem * 0.8212 dem[mark == 3] = 255213 plt.imshow(dem, cmap='gray')214 plt.show()215 '''

216 #dem = imread('line2.png')

217 dem = cv2.imread('0.jpg')218 dem =cv2.cvtColor(dem,cv2.COLOR_BGR2GRAY)219 ret2, dem = cv2.threshold(dem, 0, 255, cv2.THRESH_BINARY |cv2.THRESH_OTSU)220 dis = ndimg.distance_transform_edt(~dem).astype(np.uint8)221 dis = ~dis222 mark = (dis<230).astype(np.uint8)223 plt.imshow(dis, cmap='gray')224 plt.imshow(mark, cmap='gray')225 watershed(dis, mark)226 dem //= 2

227 dem[mark==3] = 255

228 plt.imshow(dem, cmap='gray')229 plt.show()

C++版本:

1 //---table

2 uchar lut[] = { 200, 206, 220, 204, 0, 207, 0, 204, 0, 207, 221, 51, 1, 207, 221, 51,3 0, 0, 221, 204, 0, 0, 0, 204, 1, 207, 221, 51, 1, 207, 221, 51};4

5 //---FindRidge

6 void FindRidge(InputArray& _src, Mat& mask, vectorEdge_Point, uchar thred)7 {8 Mat src = _src.getMat();//, mask = _mask.getMat();

9

10 mask =src.clone();11 bitwise_not(mask, mask);12 distanceTransform(mask, mask, DIST_L2, DIST_MASK_3, 5);13 normalize(mask, mask, 0, 255, NORM_MINMAX);14 mask.convertTo(mask, CV_8UC1);15 threshold(mask, mask, thred, 255, THRESH_BINARY_INV);16 Mat temp =mask.clone();17

18 bitwise_not(mask, mask);19 mask = mask - 253;20 Mat rows = Mat::ones(Size(src.cols, 1), CV_8UC1), cols = Mat::zeros(Size(1, src.rows), CV_8UC1);21 rows.setTo(4); cols.setTo(4);22 Mat src_rows_beg = mask.row(0), src_cols_beg = mask.col(0);23 Mat src_rows_end = mask.row(src.rows - 1), src_cols_end = mask.col(src.cols - 1);24 rows.copyTo(src_rows_beg); rows.copyTo(src_rows_end);25 cols.copyTo(src_cols_beg); cols.copyTo(src_cols_end);26

27 Edge_Point =FindEdge(temp, mask);28 bool TF = true;29 while(TF)30 {31 TF = false;32 for (size_t i = 0; i < mask.rows - 1; i++)33 {34 uchar* msk_up = mask.ptr(i - 1);35 uchar* msk =mask.ptr(i);36 uchar* msk_dw = mask.ptr(i + 1);37 for (size_t j = 0; j < mask.cols - 1; j++)38 {39 uchar _temp_data =msk[j];40 msk[j] = msk[j] == 2 && (msk_up[j] == 255 || msk_up[j] == 0)41 && (msk_dw[j] == 255 || msk_dw[j] == 0)42 && (msk[j - 1] == 255 || msk[j - 1] == 0)43 && (msk[j + 1] == 255 || msk[j + 1] == 0) ? 0: msk[j];44 msk[j] = msk[j] == 0 && (msk_up[j] == 2 || msk_dw[j] == 2 || msk[j - 1] == 2 || msk[j + 1] == 2) ? 2: msk[j];45 TF = _temp_data != msk[j] ? true: TF;46 }47 }48 }49

50 distanceTransform(src, src, DIST_L2, DIST_MASK_3, 5);51 normalize(src, src, 0, 255, NORM_MINMAX);52 src.convertTo(src, CV_8UC1);53

54 const int histSize = 255;55 float range[] = { 0, 255};56 const float* histRange ={ range };57 Mat hist;58 calcHist(&src, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);59

60 hist = hist.reshape(1,1);61 normalize(hist, hist, 0, 1000, NORM_MINMAX);62 hist.convertTo(hist, CV_32FC1);63 //float* ptr = hist.ptr(0);

64 for (size_t level = 0; level <= 255; level++)65 {66 if (!hist.at(0,level)) continue;67 FloorEdge(src, Edge_Point, mask, level);68 }69 }70

71 /*

72

73 vector FindEdge(InputArray& _src, Mat& mask)74 {75 Mat src1 = _src.getMat(),src = src1.clone();76 Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));77 morphologyEx(src, src, MORPH_ERODE, kernel);78 vector wjy_Point;79 vector> contours;80 vector hierarchy;81 findContours(src, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(-1, -1));82 for (size_t i = 0; i < contours.size(); i++)83 {84 for (size_t j = 0; j < contours[i].size(); j++)85 {86 wjy_Point.push_back(contours[i][j]);87 mask.at(contours[i][j]) = 255;88 }89 }90 return wjy_Point;91 }92 */

93

94

95 vector FindEdge(Mat& src,Mat&mask)96 {97 //Mat src = mask.clone();

98 Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));99 //morphologyEx(src, src, MORPH_ERODE, kernel);

100 vectorwjy_Point;101 vector>contours;102 vectorhierarchy;103 findContours(src, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(-1, -1));104 for (size_t i = 0; i < contours.size(); i++)105 {106 for (size_t j = 0; j < contours[i].size(); j++)107 {108 wjy_Point.push_back(contours[i][j]);109 mask.at(contours[i][j]) = 255;110 }111 }112 returnwjy_Point;113 }114

115

116 void FloorEdge(InputArray& _src, vector& Edge_Point, Mat& mask,intlevel)117 {118 Mat src =_src.getMat();119 for (int i = 0; i < Edge_Point.size(); i++)120 {121 vectortemp_vector;122 temp_vector.push_back(Point(Edge_Point[i].x, Edge_Point[i].y - 1));123 temp_vector.push_back(Point(Edge_Point[i].x, Edge_Point[i].y + 1));124 temp_vector.push_back(Point(Edge_Point[i].x - 1, Edge_Point[i].y));125 temp_vector.push_back(Point(Edge_Point[i].x + 1, Edge_Point[i].y));126 uchar* msk =mask.ptr(Edge_Point[i].y);127 uchar* img =src.ptr(Edge_Point[i].y);128 if (img[Edge_Point[i].x] > level) continue;129 bool Flag = true;130 uchar count_num = 0;131 for (size_t j = 0; j < temp_vector.size(); j++)132 {133 uchar* pre_data =mask.ptr(temp_vector[j].y);134 if (pre_data[temp_vector[j].x] == 2 || pre_data[temp_vector[j].x] == 4)135 {136 pre_data[temp_vector[j].x] = 2;137 continue;138 }139 else if (pre_data[temp_vector[j].x] == 128 || pre_data[temp_vector[j].x] == 255)140 {141 continue;142 count_num++;143 }144 else

145 {146 if (src.at(temp_vector[j]) >=level)147 {148 count_num++;149 pre_data[temp_vector[j].x] = 255;150 Edge_Point.push_back(temp_vector[j]);151 //Edge_Point.insert(Edge_Point.begin()+i+1,temp_vector[j]);

152 }153 else

154 {155 int temp =FillBlock(src, Edge_Point, mask, level, Edge_Point[i]);156 Flag = false;157 }158 }159 }160 if (false)161 {162 msk[Edge_Point[i].x] = 128;163 Edge_Point.erase(Edge_Point.begin() +i);164 i--;165 continue;166 }167 else if (count_num == 4)168 {169 Edge_Point.push_back(Edge_Point[i]);170 Edge_Point.erase(Edge_Point.begin() +i);171 i--;172 continue;173 }174 else if (msk[Edge_Point[i].x] == 255)175 {176 msk[Edge_Point[i].x] = CheckData(mask, Edge_Point[i]) == true ? 128 : 2;177 Edge_Point.erase(Edge_Point.begin() +i);178 i--;179 }180 }181 }182

183 int FillBlock(InputArray& _src, vector& Edge_Point, Mat& mask, intlevel, Point center)184 {185 Mat src =_src.getMat();186 mask.at(center) = 2;187 vectorfill_point;188 int count = 0, count_mount = 1;189 fill_point.push_back(center);190 while (count img;193 vectormsk;194 for (int i = -1; i < 2; i++)195 {196 img.push_back(src.ptr(fill_point[count].y +i));197 msk.push_back(mask.ptr(fill_point[count].y +i));198 }199 for (size_t i = 0; i < 3; i++)200 {201 for (int j = -1; j < 2; j++)202 {203 if (img[i][fill_point[count].x + j] < level && !(j == 0 && i == 1) && msk[i][fill_point[count].x + j] == 0)204 {205 fill_point.push_back(Point(fill_point[count].x + j, fill_point[count].y + i - 1));206 msk[i][fill_point[count].x + j] = 2;207 }208 else if (img[i][fill_point[count].x + j] >= level && msk[i][fill_point[count].x + j] == 0)209 {210 Edge_Point.push_back(Point(fill_point[count].x + j, fill_point[count].y + i - 1));211 msk[i][fill_point[count].x + j] = 255;212 }213 }214 }215 //msk[1][fill_point[count].x] = 2;

216 count_mount = fill_point.size() - 1;217 fill_point.erase(fill_point.begin());218 }219 return 0;220 }221

222

223 bool CheckData(Mat&mask, Point center)224 {225 uchar* msk_up = mask.ptr(center.y - 1);226 uchar* msk =mask.ptr(center.y);227 uchar* msk_dw = mask.ptr(center.y + 1);228 int num[8];229 int sum = (num[0] = msk_up[center.x - 1] == 255 || msk_up[center.x - 1] == 128 || msk_up[center.x - 1] == 0 ? 1 : 0)230 + (num[1] = msk_up[center.x] == 255 || msk_up[center.x] == 128 || msk_up[center.x] == 0 ? 1 : 0) * 2

231 + (num[2] = msk_up[center.x + 1] == 255 || msk_up[center.x + 1] == 128 || msk_up[center.x + 1] == 0 ? 1 : 0) * 4

232 + (num[3] = msk[center.x - 1] == 255 || msk[center.x - 1] == 128 || msk[center.x - 1] == 0 ? 1 : 0) * 8

233 + (num[4] = msk[center.x + 1] == 255 || msk[center.x + 1] == 128 || msk[center.x + 1] == 0 ? 1 : 0) * 16

234 + (num[5] = msk_dw[center.x - 1] == 255 || msk_dw[center.x - 1] == 128 || msk_dw[center.x - 1] == 0 ? 1 : 0) * 32

235 + (num[6] = msk_dw[center.x] == 255 || msk_dw[center.x] == 128 || msk_dw[center.x] == 0 ? 1 : 0) * 64

236 + (num[7] = msk_dw[center.x + 1] == 255 || msk_dw[center.x + 1] == 128 || msk_dw[center.x + 1] == 0 ? 1 : 0) * 128;237 return ((lut[uchar(sum / 8)] >> sum % 8) & 1) != 1 ? true : false;238 }

78>74>

python 山脊图_《图像处理实例》 之 寻找山脊线相关推荐

  1. python 山脊图_纯Python绘制艺术感满满的山脊地图,创意满分

    而今天的文章,我们就来一起基于 Python ,配合颜色与字体的选择搭配,使用简短的代码,就可以创作出艺术海报级别的 山脊地图 . 2 基于ridge_map的山脊地图绘制 我们主要使用 matplo ...

  2. python 山脊图_纯Python绘制满满艺术感的山脊地图

    ❝ 本文示例代码及附件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes❞ 1 简介 下面的这幅图可能很多读者朋友们都看到 ...

  3. python 山脊图_爬虫:带你一键爬取王者荣耀英雄皮肤壁纸

    一.前言 王者荣耀这款手游,想必大家都玩过或听过,游戏里英雄有各式各样的皮肤,制作得很精美,有些拿来做电脑壁纸它不香吗.本文带你利用Python爬虫一键下载王者荣耀英雄皮肤壁纸. 1. 目标 创建一个 ...

  4. python 山脊图_Python提取DEM数据的山脊线代码

    #自动提取山脊线 importarcgisscriptingimportosimportsys#创建GP工具 gp=arcgisscripting.create(9.3)#DEM参数设置 inputd ...

  5. python图像识别 车牌_[图像处理] Python+OpenCV实现车牌区域识别

    点击上方蓝色字体,关注我们 15 本篇文章主要调用OpenCV库(cv2)进行车牌区域识别,具体步骤包括: 1.灰度转换:将彩色图片转换为灰度图像,常见的R=G=B=像素平均值. 2.高斯平滑和中值滤 ...

  6. python 函数图_如何查看Python函数调用图 Ubuntu

    说明:有时候想看看Python的函数调用图,此时 pycallgraph 就显示出他的用途了. 安装 pycallgraph pip install pycallgraph 安装 graphviz,使 ...

  7. python 鱼骨图_数据分析必备的三种思考模型

    原标题:数据分析必备的三种思考模型 新人学习数据分析有个误区,认为Excel很Low,SQL做久了又是表哥表姐,学习python又陷入无尽的工具包中不能自拔,迷茫到找项目学习,结果是分析又不得要领,说 ...

  8. 如何使用python做图_如何使用python做动图

    python可以做动图吗 ImageMagick 是一套功能强大.稳定而且开源的工具集和开发包,可以用来读.写和处理超过200种基本格式的图片文件,包括PNG,JPEG,GIF,HEIC,TIFF,D ...

  9. python阶梯图_不会你还不懂怎么用Python制图吧?师兄教你如何学会绘制漂亮的阶梯图...

    Python制图你真的会吗?一文学会如何绘制漂亮的阶梯图 说到Python制图就不得不提matplotlib这个最为常用的库,matplotlib库作为Python经典的二维绘图库,在Python的数 ...

最新文章

  1. 客户资料查询传递数据格式
  2. 服务器安装配置流水帐
  3. 包(package)
  4. dataTable表头未对其解决方法
  5. 开源高性能异步网关:Soul
  6. 基于Java语言构建区块链(四)—— 交易(UTXO)
  7. python psycopg2_如何在Python上用“pip”安装psycopg2?
  8. [C#基础]c#中的BeginInvoke和EndEndInvoke
  9. python全栈学习--day12(函数高级应用-带参数的装饰器,多个装饰器装饰一个函数)...
  10. LPWSTR 类型的实参与const.char *类型形参不兼容
  11. 项目进行JVM调优 Jconsole
  12. cloudera cdh5.13.0 vmware 快速安装
  13. mysql ip v4 v6_mysql IPv4 IPv6
  14. git pull origin拉取报错 Pulling without specifying how to reconcile divergent branches is
  15. 服务器的固态硬盘使用raid非ssd,畅谈固态硬盘搭建RAID的方法
  16. EBS系统打补丁(Patch)
  17. <C++>一篇文章搞懂类和对象中常函数和常对象的实质以及避免空指针访问的小妙招
  18. 有道云笔记markdown上传本地图片的方法
  19. 2017 年前端工具趋势
  20. 算法竞赛常见赛制及题目形式

热门文章

  1. 计算广告4——用户增长
  2. TensorFlow学习——Tensorflow Object Detection API(win10,CPU)
  3. 【欣赏】一组唯美的图片
  4. 广东迅视资管 别让“顺风车”再度行驶至安全边缘
  5. Win系统 - 关于GPU,你需要长的“姿势”
  6. 一步步教你如何在Ubuntu虚拟机中安装QEMU并模拟模拟arm 开发环境(一)uImage u-boot
  7. [附源码]计算机毕业设计springboot动物保护协会网站
  8. 基于java植物大全智能识别系统设计与实现
  9. 50个好网站,从此上网不再虚度!
  10. uniapp使用第三方字体