PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/octave-forge/main/image/inst/bwmorph.m

#
Objective C | 624 lines | 579 code | 45 blank | 0 comment | 52 complexity | 040db312342e22c06718f6fbe268e52c MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, GPL-3.0, LGPL-3.0
  1. ## Copyright (C) 2004 Josep Mones i Teixidor
  2. ##
  3. ## This program is free software; you can redistribute it and/or modify
  4. ## it under the terms of the GNU General Public License as published by
  5. ## the Free Software Foundation; either version 2 of the License, or
  6. ## (at your option) any later version.
  7. ##
  8. ## This program is distributed in the hope that it will be useful,
  9. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ## GNU General Public License for more details.
  12. ##
  13. ## You should have received a copy of the GNU General Public License
  14. ## along with this program; If not, see <http://www.gnu.org/licenses/>.
  15. ## -*- texinfo -*-
  16. ## @deftypefn {Function File} {@var{BW2} = } bwmorph (@var{BW},@var{operation})
  17. ## @deftypefnx {Function File} {@var{BW2} = } bwmorph (@var{BW},@var{operation},@var{n})
  18. ## Perform a morphological operation on a binary image.
  19. ##
  20. ## BW2=bwmorph(BW,operation) performs a morphological operation
  21. ## specified by @var{operation} on binary image @var{BW}. All possible
  22. ## operations and their meaning are specified in a table below.
  23. ##
  24. ## BW2=bwmorph(BW,operation,n) performs a morphological operation
  25. ## @var{n} times. Keep in mind that it has no sense to apply some
  26. ## operations more than once, since some of them return the same result
  27. ## regardless how many iterations we request. Those return a warning if
  28. ## are called with n>1 and they compute the result for n=1.
  29. ##
  30. ## @var{n}>1 is actually used for the following operations: diag,
  31. ## dilate, erode, majority, shrink, skel, spur, thicken and thin.
  32. ##
  33. ## @table @code
  34. ## @item 'bothat'
  35. ## Performs a bottom hat operation, a closing operation (which is a
  36. ## dilation followed by an erosion) and finally substracts the original
  37. ## image.
  38. ##
  39. ## @item 'bridge'
  40. ## Performs a bridge operation. Sets a pixel to 1 if it has two nonzero
  41. ## neighbours which are not connected, so it "bridges" them. There are
  42. ## 119 3-by-3 patterns which trigger setting a pixel to 1.
  43. ##
  44. ## @item 'clean'
  45. ## Performs an isolated pixel remove operation. Sets a pixel to 0 if all
  46. ## of its eight-connected neighbours are 0.
  47. ##
  48. ## @item 'close'
  49. ## Performs closing operation, which is a dilation followed by erosion.
  50. ## It uses a ones(3) matrix as structuring element for both operations.
  51. ##
  52. ## @item 'diag'
  53. ## Performs a diagonal fill operation. Sets a pixel to 1 if that
  54. ## eliminates eight-connectivity of the background.
  55. ##
  56. ## @item 'dilate'
  57. ## Performs a dilation operation. It uses ones(3) as structuring element.
  58. ##
  59. ## @item 'erode'
  60. ## Performs an erosion operation. It uses ones(3) as structuring element.
  61. ##
  62. ## @item 'fill'
  63. ## Performs a interior fill operation. Sets a pixel to 1 if all
  64. ## four-connected pixels are 1.
  65. ##
  66. ## @item 'hbreak'
  67. ## Performs a H-break operation. Breaks (sets to 0) pixels that are
  68. ## H-connected.
  69. ##
  70. ## @item 'majority'
  71. ## Performs a majority black operation. Sets a pixel to 1 if five
  72. ## or more pixels in a 3-by-3 window are 1. If not it is set to 0.
  73. ##
  74. ## @item 'open'
  75. ## Performs an opening operation, which is an erosion followed by a
  76. ## dilation. It uses ones(3) as structuring element.
  77. ##
  78. ## @item 'remove'
  79. ## Performs a iterior pixel remove operation. Sets a pixel to 0 if
  80. ## all of its four-connected neighbours are 1.
  81. ##
  82. ## @item 'shrink'
  83. ## Performs a shrink operation. Sets pixels to 0 such that an object
  84. ## without holes erodes to a single pixel (set to 1) at or near its
  85. ## center of mass. An object with holes erodes to a connected ring lying
  86. ## midway between each hole and its nearest outer boundary. It preserves
  87. ## Euler number.
  88. ##
  89. ## @item 'skel'
  90. ## Performs a skeletonization operation. It calculates a "median axis
  91. ## skeleton" so that points of this skeleton are at the same distance of
  92. ## its nearby borders. It preserver Euler number. Please read
  93. ## compatibility notes for more info.
  94. ##
  95. ## It uses the same algorithm as skel-pratt but this could change for
  96. ## compatibility in the future.
  97. ##
  98. ## @item 'skel-lantuejol'
  99. ## Performs a skeletonization operation as described in Gonzalez & Woods
  100. ## "Digital Image Processing" pp 538-540. The text references Lantuejoul
  101. ## as authour of this algorithm.
  102. ##
  103. ## It has the beauty of being a clean and simple approach, but skeletons
  104. ## are thicker than they need to and, in addition, not guaranteed to be
  105. ## connected.
  106. ##
  107. ## This algorithm is iterative. It will be applied the minimum value of
  108. ## @var{n} times or number of iterations specified in algorithm
  109. ## description. It's most useful to run this algorithm with @code{n=Inf}.
  110. ##
  111. ## @item 'skel-pratt'
  112. ## Performs a skeletonization operation as described by William K. Pratt
  113. ## in "Digital Image Processing".
  114. ##
  115. ## @item 'spur'
  116. ## Performs a remove spur operation. It sets pixel to 0 if it has only
  117. ## one eight-connected pixel in its neighbourhood.
  118. ##
  119. ## @item 'thicken'
  120. ## Performs a thickening operation. This operation "thickens" objects
  121. ## avoiding their fusion. Its implemented as a thinning of the
  122. ## background. That is, thinning on negated image. Finally a diagonal
  123. ## fill operation is performed to avoid "eight-connecting" objects.
  124. ##
  125. ## @item 'thin'
  126. ## Performs a thinning operation. When n=Inf, thinning sets pixels to 0
  127. ## such that an object without holes is converted to a stroke
  128. ## equidistant from its nearest outer boundaries. If the object has
  129. ## holes it creates a ring midway between each hole and its near outer
  130. ## boundary. This differ from shrink in that shrink converts objects
  131. ## without holes to a single pixels and thin to a stroke. It preserves
  132. ## Euler number.
  133. ##
  134. ## @item 'tophat'
  135. ## Performs a top hat operation, a opening operation (which is an
  136. ## erosion followed by a dilation) and finally substracts the original
  137. ## image.
  138. ## @end table
  139. ##
  140. ## Some useful concepts to understant operators:
  141. ##
  142. ## Operations are defined on 3-by-3 blocks of data, where the pixel in
  143. ## the center of the block. Those pixels are numerated as follows:
  144. ##
  145. ## @multitable @columnfractions 0.05 0.05 0.05
  146. ## @item X3 @tab X2 @tab X1
  147. ## @item X4 @tab X @tab X0
  148. ## @item X5 @tab X6 @tab X7
  149. ## @end multitable
  150. ##
  151. ## @strong{Neighbourhood definitions used in operation descriptions:}
  152. ## @table @code
  153. ## @item 'four-connected'
  154. ## It refers to pixels which are connected horizontally or vertically to
  155. ## X: X1, X3, X5 and X7.
  156. ## @item 'eight-connected'
  157. ## It refers to all pixels which are connected to X: X0, X1, X2, X3, X4,
  158. ## X5, X6 and X7.
  159. ## @end table
  160. ##
  161. ## @strong{Compatibility notes:}
  162. ## @table @code
  163. ## @item 'fill'
  164. ## Checking MATLAB behaviour is needed because its documentation doesn't
  165. ## make clear if it creates a black pixel if all eight-connected pixels
  166. ## are black or if four-connected suffice (as we do currently following
  167. ## Pratt's book).
  168. ## @item 'skel'
  169. ## Algorithm used here is described in Pratt's book. When applying it to
  170. ## the "circles" image in MATLAB documentation, results are not the
  171. ## same. Perhaps MATLAB uses Blum's algoritm (for further info please
  172. ## read comments in code).
  173. ## @item 'skel-pratt'
  174. ## This option is not available in MATLAB.
  175. ## @item 'skel-lantuejoul'
  176. ## This option is not available in MATLAB.
  177. ## @item 'thicken'
  178. ## This implementation also thickens image borders. This can easily be
  179. ## avoided i necessary. MATLAB documentation doesn't state how it behaves.
  180. ## @end table
  181. ##
  182. ## References:
  183. ## W. K. Pratt, "Digital Image Processing"
  184. ## Gonzalez and Woods, "Digital Image Processing"
  185. ##
  186. ## @seealso{imdilate, imerode, imtophat, imbothat, makelut, applylut}
  187. ## @end deftypefn
  188. ## TODO: As soon as Octave doesn't segfault when assigning values to a
  189. ## TODO: bool matrix, remove all conversions from lut to logical and
  190. ## TODO: just create it as a logical from the beginning.
  191. ## TODO: n behaviour should be tested in all cases for compatibility.
  192. ## Author: Josep Mones i Teixidor <jmones@puntbarra.com>
  193. function BW2 = bwmorph(BW, operation, n)
  194. if(nargin<2 || nargin>3)
  195. usage("BW2=bwmorph(BW, operation [,n])");
  196. endif
  197. if(nargin<3)
  198. n=1;
  199. endif
  200. if(n<0)
  201. error("bwmorph: n should be > 0");
  202. elseif(n==0) ## we'll just return the same matrix (check this!)
  203. BW2=BW;
  204. endif
  205. ## post processing command
  206. postcmd="";
  207. switch(operation)
  208. case('bothat')
  209. se = ones(3);
  210. BW2 = imbothat (BW, se);
  211. if(n>1)
  212. ## TODO: check if ignoring n>1 is ok. Should I just ignore it
  213. ## TODO: without a warning?
  214. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  215. endif
  216. return;
  217. case('bridge')
  218. ## see __bridge_lut_fun__ for rules
  219. ## lut=makelut("__bridge_lut_fun__",3);
  220. lut=logical([0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  221. 0;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  222. 0;0;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  223. 1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  224. 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  225. 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  226. 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  227. 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  228. 0;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  229. 0;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  230. 0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  231. 1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  232. 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  233. 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  234. 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  235. 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
  236. BW2=applylut(BW, lut);
  237. if(n>1)
  238. ## TODO: check if ignoring n>1 is ok.
  239. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  240. endif
  241. return;
  242. case('clean')
  243. ## BW(j,k)=X&&(X0||X1||...||X7)
  244. ## lut=makelut(inline("x(2,2)&&any((x.*[1,1,1;1,0,1;1,1,1])(:))","x"),3);
  245. ## which is the same as...
  246. lut=repmat([zeros(16,1);ones(16,1)],16,1); ## identity
  247. lut(17)=0; ## isolated to 0
  248. ## I'd prefer to create lut directly as a logical, but assigning a
  249. ## value to a logical segfaults 2.1.57. We'll change it as soon as
  250. ## it works.
  251. BW2=applylut(BW, logical(lut));
  252. if(n>1)
  253. ## TODO: check if ignoring n>1 is ok.
  254. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  255. endif
  256. return;
  257. case('close')
  258. se = ones(3);
  259. BW2 = imclose (BW, se);
  260. if(n>1)
  261. ## TODO: check if ignoring n>1 is ok.
  262. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  263. endif
  264. return;
  265. case('diag')
  266. ## see __diagonal_fill_lut_fun__ for rules
  267. ## lut=makelut("__diagonal_fill_lut_fun__",3);
  268. lut=logical([0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  269. 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  270. 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  271. 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  272. 0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  273. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  274. 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  275. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  276. 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  277. 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  278. 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  279. 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  280. 0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  281. 0;0;1;1;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  282. 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  283. 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
  284. cmd="BW2=applylut(BW, lut);";
  285. case('dilate')
  286. cmd="BW2=imdilate(BW, ones(3));";
  287. case('erode')
  288. cmd="BW2=imerode(BW, ones(3));";
  289. case('fill')
  290. ## lut=makelut(inline("x(2,2)||(sum((x&[0,1,0;1,0,1;0,1,0])(:))==4)","x"),3);
  291. ## which is the same as...
  292. lut=repmat([zeros(16,1);ones(16,1)],16,1); ## identity
  293. ## 16 exceptions
  294. lut([171,172,175,176,235,236,239,240,427,428,431,432,491,492,495,496])=1;
  295. BW2=applylut(BW, logical(lut));
  296. if(n>1)
  297. ## TODO: check if ignoring n>1 is ok.
  298. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  299. endif
  300. return;
  301. case('hbreak')
  302. ## lut=makelut(inline("x(2,2)&&!(all(x==[1,1,1;0,1,0;1,1,1])||all(x==[1,0,1;1,1,1;1,0,1]))","x"),3);
  303. ## which is the same as
  304. lut=repmat([zeros(16,1);ones(16,1)],16,1); ## identity
  305. lut([382,472])=0; ## the 2 exceptions
  306. BW2=applylut(BW, logical(lut));
  307. if(n>1)
  308. ## TODO: check if ignoring n>1 is ok.
  309. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  310. endif
  311. return;
  312. case('majority')
  313. ## lut=makelut(inline("sum((x&ones(3,3))(:))>=5"),3);
  314. lut=logical([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  315. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;
  316. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;
  317. 0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;
  318. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;
  319. 0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;
  320. 0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;
  321. 0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  322. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;
  323. 0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;
  324. 0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;
  325. 0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  326. 0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;
  327. 0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  328. 0;0;0;1;0;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  329. 0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
  330. cmd="BW2=applylut(BW, lut);";
  331. case('open')
  332. se = ones(3);
  333. BW2 = imopen (BW, se);
  334. if(n>1)
  335. ## TODO: check if ignoring n>1 is ok.
  336. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  337. endif
  338. return;
  339. case('remove')
  340. ## lut=makelut(inline("x(2,2)&&!(sum((x&[0,1,0;1,1,1;0,1,0])(:))==5)","x"),3);
  341. lut=repmat([zeros(16,1);ones(16,1)],16,1); ## identity
  342. ## 16 qualifying patterns
  343. lut([187,188,191,192,251,252,255,256,443,444,447,448,507,508,511,512])=0;
  344. BW2=applylut(BW, logical(lut));
  345. if(n>1)
  346. ## TODO: check if ignoring n>1 is ok.
  347. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  348. endif
  349. return;
  350. case('shrink')
  351. ## lut1=makelut("__conditional_mark_patterns_lut_fun__",3,"S");
  352. lut1=logical([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;0;1;1;1;1;0;1;0;0;1;1;
  353. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;1;1;0;0;0;0;0;0;0;1;
  354. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
  355. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  356. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  357. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;
  358. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
  359. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;
  360. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  361. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;1;0;1;1;0;0;0;0;0;0;0;1;
  362. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  363. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  364. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  365. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;0;
  366. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
  367. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;0;1;1;0;0]);
  368. ## lut2=makelut(inline("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'S')","m"),3);
  369. lut2=logical([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;0;0;1;0;0;0;0;0;1;0;
  370. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;0;0;0;0;0;0;1;1;0;0;1;0;
  371. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;0;0;0;0;1;1;0;1;
  372. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;
  373. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
  374. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0;1;1;0;1;
  375. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;1;0;1;0;0;1;0;1;1;0;1;
  376. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;1;1;0;1;0;1;0;1;
  377. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;1;0;1;1;1;1;1;1;1;
  378. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;0;0;1;0;1;0;0;1;0;1;1;1;1;
  379. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  380. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  381. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;0;1;0;0;1;0;1;1;1;1;
  382. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  383. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  384. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
  385. cmd="BW2=BW&applylut(applylut(BW, lut1), lut2);";
  386. case({'skel','skel-pratt'})
  387. ## WARNING: Result doesn't look as MATLAB's sample. It has been
  388. ## WARNING: coded following Pratt's guidelines for what he calls
  389. ## WARNING: is a "reasonably close approximation". I couldn't find
  390. ## WARNING: any bug.
  391. ## WARNING: Perhaps MATLAB uses Blum's algorithm (which Pratt
  392. ## WARNING: refers to) in: H. Blum, "A Transformation for
  393. ## WARNING: Extracting New Descriptors of Shape", Symposium Models
  394. ## WARNING: for Perception of Speech and Visual Form, W.
  395. ## WARNING: Whaten-Dunn, Ed. MIT Press, Cambridge, MA, 1967.
  396. ## lut1=makelut("__conditional_mark_patterns_lut_fun__",3,"K");
  397. lut1=logical([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;0;0;0;0;1;
  398. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0;1;
  399. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;0;0;0;1;
  400. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  401. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;
  402. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  403. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;0;0;0;1;
  404. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  405. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  406. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1;
  407. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  408. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  409. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  410. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1;
  411. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
  412. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;1;1;1;1;0]);
  413. ## lut2=makelut(inline("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'K')","m"),3);
  414. lut2=logical([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;0;1;0;0;0;1;0;1;1;0;0;0;1;
  415. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;1;0;0;1;1;0;0;1;1;
  416. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;0;0;0;1;0;1;0;1;
  417. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;1;1;0;1;1;1;0;1;1;1;
  418. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;1;
  419. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;
  420. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;1;1;1;1;1;1;1;1;
  421. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;
  422. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
  423. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;0;0;1;1;1;1;1;1;
  424. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  425. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  426. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
  427. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  428. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  429. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
  430. cmd="BW2=BW&applylut(applylut(BW, lut1), lut2);";
  431. postcmd="BW2=bwmorph(BW2,'bridge');";
  432. case('skel-lantuejoul')
  433. ## init values
  434. se=ones(3,3); ## structuring element used everywhere
  435. BW2=zeros(size(BW)); ## skeleton result
  436. eBW=BW; ## eBW will hold k-times eroded BW
  437. i=1;
  438. while i<=n
  439. if(!any(eBW)) ## if erosion result is 0-matrix then
  440. break; ## we are over
  441. endif
  442. BW2|=eBW-dilate(erode(eBW, se), se); ## eBW - opening operation on eBW
  443. ## contributes to skeleton
  444. eBW=erode(eBW,se);
  445. i++;
  446. endwhile
  447. return; ## no general loop in this case
  448. case('spur')
  449. ## lut=makelut(inline("xor(x(2,2),(sum((x&[0,1,0;1,0,1;0,1,0])(:))==0)&&(sum((x&[1,0,1;0,0,0;1,0,1])(:))==1)&&x(2,2))","x"),3);
  450. ## which is the same as
  451. lut=repmat([zeros(16,1);ones(16,1)],16,1); ## identity
  452. lut([18,21,81,273])=0; ## 4 qualifying patterns
  453. lut=logical(lut);
  454. cmd="BW2=applylut(BW, lut);";
  455. case('thicken')
  456. ## This implementation also "thickens" the border. To avoid this,
  457. ## a simple solution could be to add a border of 1 to the reversed
  458. ## image.
  459. BW2=bwmorph(!BW,'thin',n);
  460. BW2=bwmorph(BW2,'diag');
  461. return;
  462. case('thin')
  463. ## lut1=makelut("__conditional_mark_patterns_lut_fun__",3,"T");
  464. lut1=logical([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;0;1;1;
  465. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;0;0;0;0;0;0;0;1;
  466. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;0;0;0;1;
  467. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  468. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;
  469. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;
  470. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
  471. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;
  472. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  473. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;0;0;0;0;0;0;0;1;
  474. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  475. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;
  476. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
  477. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;0;
  478. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
  479. 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;0;1;1;0;0]);
  480. ## lut2=makelut(inline("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'T')","m"),3);
  481. lut2=logical([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;1;1;0;1;0;1;1;0;0;0;0;1;0;
  482. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;0;0;0;0;1;1;0;0;1;0;
  483. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;1;0;0;0;1;1;0;1;
  484. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;
  485. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
  486. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0;1;1;0;1;
  487. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;1;0;0;1;0;1;1;0;1;
  488. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;1;1;0;1;0;1;0;1;
  489. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;1;0;1;1;1;1;1;1;1;
  490. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;0;1;0;1;1;1;1;
  491. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  492. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  493. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;0;1;0;0;1;0;1;1;1;1;
  494. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
  495. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
  496. 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
  497. cmd="BW2=BW&applylut(applylut(BW, lut1), lut2);";
  498. case('tophat')
  499. se = ones(3);
  500. BW2 = imtophat (BW, se);
  501. if(n>1)
  502. ## TODO: check if ignoring n>1 is ok.
  503. disp("WARNING: n>1 has no sense here. Using n=1. Please fill a bug if you think this behaviour is not correct");
  504. endif
  505. return;
  506. otherwise
  507. error("bwmorph: unknown operation type requested.");
  508. endswitch
  509. ## we use this assignment because of the swap operation inside the
  510. ## while.
  511. BW2=BW;
  512. ## if it doesn't change we don't need to process it further
  513. i=1;
  514. while(i<=n) ## for wouldn't work because n can be Inf
  515. [BW,BW2]=swap(BW,BW2);
  516. eval(cmd);
  517. if(all((BW2==BW)(:)))
  518. break
  519. endif
  520. i+=1;
  521. endwhile
  522. ## process post processing commands if needed
  523. if (isempty (postcmd))
  524. eval(postcmd);
  525. endif
  526. endfunction
  527. function [b, a] = swap (a, b)
  528. endfunction
  529. %!demo
  530. %! bwmorph(ones(11),'shrink', Inf)
  531. %! # Should return 0 matrix with 1 pixel set to 1 at (6,6)
  532. ## TODO: code tests
  533. ## Test skel-lantuejoul using Gozalez&Woods example (fig 8.39)
  534. %!shared slBW, rslBW
  535. %! uint8(0); # fail for 2.1.57 or less instead of crashing later
  536. %! slBW=logical(zeros(12,7));
  537. %! slBW(2,2)=true;
  538. %! slBW(3:4,3:4)=true;
  539. %! rslBW=slBW;
  540. %! slBW(5:6,3:5)=true;
  541. %! slBW(7:11,2:6)=true;
  542. %! rslBW([6,7,9],4)=true;
  543. %!assert(bwmorph(slBW,'skel-lantuejoul',1),[rslBW(1:5,:);logical(zeros(7,7))]);
  544. %!assert(bwmorph(slBW,'skel-lantuejoul',2),[rslBW(1:8,:);logical(zeros(4,7))]);
  545. %!assert(bwmorph(slBW,'skel-lantuejoul',3),rslBW);
  546. %!assert(bwmorph(slBW,'skel-lantuejoul',Inf),rslBW);
  547. %
  548. % $Log$
  549. % Revision 1.4 2007/03/23 16:14:36 adb014
  550. % Update the FSF address
  551. %
  552. % Revision 1.3 2007/01/04 23:41:47 hauberg
  553. % Minor changes in help text
  554. %
  555. % Revision 1.2 2006/10/15 08:04:55 hauberg
  556. % Fixed texinfo in bwmorph
  557. %
  558. % Revision 1.1 2006/08/20 12:59:32 hauberg
  559. % Changed the structure to match the package system
  560. %
  561. % Revision 1.6 2004/09/16 02:14:40 pkienzle
  562. % Use frivolous uint8() call to block tests for version 2.1.57 and earlier
  563. %
  564. % Revision 1.5 2004/09/15 20:36:57 jmones
  565. % logical(1) => true
  566. %
  567. % Revision 1.4 2004/09/15 20:00:00 jmones
  568. % Updated tests to match Gonzalez&Woods example
  569. %
  570. % Revision 1.3 2004/09/15 13:51:10 pkienzle
  571. % Use logical in tests; reduce number of shared variables in tests.
  572. %
  573. % Revision 1.2 2004/09/01 22:35:47 jmones
  574. % Added Lantuejoul skeletonizing algorithm from Gonzalez&Woods
  575. %
  576. % Revision 1.1 2004/08/15 19:47:04 jmones
  577. % bwmorph added: Perform a morphological operation on a binary image
  578. %
  579. %