001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package imagingbook.common.morphology; 010 011import static imagingbook.common.math.Arithmetic.sqr; 012 013/** 014 * This class defines static methods related to binary structuring elements (kernels). 015 * 016 * @author WB 017 */ 018public abstract class StructuringElements { 019 020 private StructuringElements() {} 021 022 /** 023 * Creates and returns a square binary structuring element (kernel) of size 3x3 (radius 1). 024 * 025 * @return a 3x3 binary box kernel 026 */ 027 public static byte[][] makeBoxKernel3x3() { 028 return makeBoxKernel(1); 029 } 030 031 /** 032 * Creates and returns a square binary "box" kernel with the specified radius. The kernel size is (2 * radius + 1) x 033 * (2 * radius + 1). It is always odd. 034 * 035 * @param radius the kernel radius 036 * @return a square binary box kernel 037 */ 038 public static byte[][] makeBoxKernel(int radius) { 039 if (radius < 0) { 040 throw new IllegalArgumentException("radius must be >= 0"); 041 } 042 int n = radius + radius + 1; 043 byte[][] H = new byte[n][n]; 044 for (int v = 0; v < H.length; v++) { 045 for (int u = 0; u < H[v].length; u++) { 046 H[v][u] = 1; 047 } 048 } 049 return H; 050 } 051 052 // TODO: compare to CircularMask (filters), define BinaryKernel class? 053 054 /** 055 * Creates and returns a square binary "disk" kernel with the specified radius. The kernel size is (2 * radius + 1) 056 * x (2 * radius + 1). It is always odd. 057 * 058 * @param radius the kernel radius 059 * @return a square binary disk kernel 060 */ 061 public static byte[][] makeDiskKernel(double radius) { 062 if (radius < 0) { 063 throw new IllegalArgumentException("radius must be >= 0"); 064 } 065 int r = (int) Math.floor(radius); 066 int n = r + r + 1; 067 byte[][] kernel = new byte[n][n]; 068 double r2 = sqr(radius) + 0.5; 069 for (int v = -r; v <= r; v++) { 070 for (int u = -r; u <= r; u++) { 071 if (sqr(u) + sqr(v) <= r2) 072 kernel[v + r][u + r] = 1; 073 } 074 } 075 return kernel; 076 } 077 078 /** 079 * Converts the specified {@code int[][]} to a {@code byte[][]}, which is returned. 080 * 081 * @param iA the {@code int[][]} array to be converted 082 * @return the resulting {@code byte[][]} 083 */ 084 public static byte[][] toByteArray(int[][] iA) { 085 byte[][] bA = new byte[iA.length][]; 086 for (int i = 0; i < iA.length; i++) { 087 bA[i] = new byte[iA[i].length]; 088 for (int j = 0; j < iA[i].length; j++) { 089 bA[i][j] = (byte) iA[i][j]; 090 } 091 } 092 return bA; 093 } 094 095 /** 096 * Returns a copy of the specified structuring element that is mirrored along both axes. For example 097 * <pre> 098 * | A B | 099 * | C D |</pre> 100 * converts to 101 * <pre> 102 * | D C | 103 * | B A |</pre> 104 * 105 * @param H the original structuring element 106 * @return the reflected structuring element 107 */ 108 public static byte[][] reflect(byte[][] H) { 109 // mirrors (transposes) the structuring element around the center (hot spot) 110 // used to implement erosion by a dilation 111 final int n = H.length; // number of rows 112 final int m = H[0].length; // number of columns 113 byte[][] rH = new byte[n][m]; 114 for (int j = 0; j < n; j++) { 115 for (int i = 0; i < m; i++) { 116 rH[j][i] = H[n - j - 1][m - i - 1]; 117 } 118 } 119 return rH; 120 } 121 122}