ImagingBinary.pas 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { Unit with operations on binary images. Binary images in Imaging are
  25. ifGray8 images where pixels with value 0 are considerend off, an pixels > 0
  26. are on.}
  27. unit ImagingBinary;
  28. {$I ImagingOptions.inc}
  29. interface
  30. uses
  31. ImagingTypes, Imaging, ImagingFormats, ImagingUtility;
  32. type
  33. { Basic morphologic operators.}
  34. TMorphologyOp = (
  35. moErode, // Erosion
  36. moDilate // Dilatation
  37. );
  38. { Structuring element for morphology operations. Use ones and
  39. zeroes to define your struct elements.}
  40. TStructElement = array of array of Byte;
  41. { Thresholding using Otsu's method (which chooses the threshold
  42. to minimize the intraclass variance of the black and white pixels!).
  43. If Threshold is nil Image is automatically converted to binary using
  44. computed threshold level. Otherwise computed threshold is stored in Threshold
  45. and Image is not modified (if you're just interesting in global threshold level).}
  46. procedure OtsuThresholding(var Image: TImageData; Threshold: PInteger = nil);
  47. { Applies basic morphology operators (Erode/Dilate) on Image using given structuring element
  48. Strel. You can do composite operations (Open/Close) by calling this function
  49. twice each time with different operator.}
  50. procedure Morphology(var Image: TImageData; const Strel: TStructElement; Op: TMorphologyOp);
  51. implementation
  52. procedure OtsuThresholding(var Image: TImageData; Threshold: PInteger);
  53. var
  54. Histogram: array[Byte] of Single;
  55. Level, Max, Min, I, J, NumPixels: Integer;
  56. Pix: PByte;
  57. Mean, Variance: Single;
  58. Mu, Omega, LevelMean, LargestMu: Single;
  59. begin
  60. ConvertImage(Image, ifGray8);
  61. FillChar(Histogram, SizeOf(Histogram), 0);
  62. Min := 255;
  63. Max := 0;
  64. Level := 0;
  65. NumPixels := Image.Width * Image.Height;
  66. Pix := Image.Bits;
  67. // Compute histogram and determine min and max pixel values
  68. for I := 0 to NumPixels - 1 do
  69. begin
  70. Histogram[Pix^] := Histogram[Pix^] + 1.0;
  71. if Pix^ < Min then
  72. Min := Pix^;
  73. if Pix^ > Max then
  74. Max := Pix^;
  75. Inc(Pix);
  76. end;
  77. // Normalize histogram
  78. for I := 0 to 255 do
  79. Histogram[I] := Histogram[I] / NumPixels;
  80. // Compute image mean and variance
  81. Mean := 0.0;
  82. Variance := 0.0;
  83. for I := 0 to 255 do
  84. Mean := Mean + (I + 1) * Histogram[I];
  85. for I := 0 to 255 do
  86. Variance := Variance + Sqr(I + 1 - Mean) * Histogram[I];
  87. // Now finally compute threshold level
  88. LargestMu := 0;
  89. for I := 0 to 255 do
  90. begin
  91. Omega := 0.0;
  92. LevelMean := 0.0;
  93. for J := 0 to I - 1 do
  94. begin
  95. Omega := Omega + Histogram[J];
  96. LevelMean := LevelMean + (J + 1) * Histogram[J];
  97. end;
  98. Mu := Sqr(Mean * Omega - LevelMean);
  99. Omega := Omega * (1.0 - Omega);
  100. if Omega > 0.0 then
  101. Mu := Mu / Omega
  102. else
  103. Mu := 0;
  104. if Mu > LargestMu then
  105. begin
  106. LargestMu := Mu;
  107. Level := I;
  108. end;
  109. end;
  110. if Threshold = nil then
  111. begin
  112. // Do thresholding using computed level
  113. Pix := Image.Bits;
  114. for I := 0 to Image.Width * Image.Height - 1 do
  115. begin
  116. if Pix^ >= Level then
  117. Pix^ := 255
  118. else
  119. Pix^ := 0;
  120. Inc(Pix);
  121. end;
  122. end
  123. else
  124. Threshold^ := Level;
  125. end;
  126. procedure Morphology(var Image: TImageData; const Strel: TStructElement; Op: TMorphologyOp);
  127. var
  128. X, Y, I, J: Integer;
  129. SWidth, SHeight, PixCount, PixVal, NumOnes, PosX, PosY: Integer;
  130. ImgOut: TImageData;
  131. OutPix: PByte;
  132. begin
  133. Assert(Image.Format = ifGray8);
  134. Assert((Length(Strel) > 0) and (Length(Strel[0]) > 0));
  135. SWidth := Length(Strel);
  136. SHeight := Length(Strel[0]);
  137. NumOnes := 0;
  138. if Op = moErode then
  139. begin
  140. // We need to know number of ones in the strel for erosion
  141. for I := 0 to SWidth - 1 do
  142. for J := 0 to SHeight - 1 do
  143. NumOnes := NumOnes + Strel[I, J];
  144. end;
  145. InitImage(ImgOut);
  146. NewImage(Image.Width, Image.Height, ifGray8, ImgOut);
  147. OutPix := ImgOut.Bits;
  148. for J := 0 to Image.Height - 1 do
  149. for I := 0 to Image.Width - 1 do
  150. begin
  151. PixCount := 0;
  152. for X := 0 to SWidth - 1 do
  153. begin
  154. PosX := ClampInt(X + I - SWidth div 2, 0, Image.Width - 1);
  155. for Y := 0 to SHeight - 1 do
  156. begin
  157. PosY := ClampInt(Y + J - SHeight div 2, 0, Image.Height - 1);
  158. if (PosX >= 0) and (PosX < Image.Width) and
  159. (PosY >= 0) and (PosY < Image.Height) then
  160. begin
  161. PixVal := PByteArray(Image.Bits)[PosY * Image.Width + PosX];
  162. end
  163. else
  164. PixVal := 0;
  165. if (Strel[X, Y] > 0) and (PixVal > 0) then
  166. Inc(PixCount);
  167. end;
  168. end;
  169. case Op of
  170. moErode: OutPix^ := Iff(PixCount = NumOnes, 255, 0);
  171. moDilate: OutPix^ := Iff(PixCount > 0, 255, 0);
  172. end;
  173. Inc(OutPix);
  174. end;
  175. FreeImage(Image);
  176. Image := ImgOut;
  177. end;
  178. {
  179. File Notes:
  180. -- TODOS ----------------------------------------------------
  181. - nothing now
  182. -- 0.25.0 Changes/Bug Fixes -----------------------------------
  183. - Unit created with basic stuff (otsu and erode/dilate morphology ops).
  184. }
  185. end.