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.mser.components; 010 011import ij.process.ImageProcessor; 012import imagingbook.common.geometry.basic.Pnt2d.PntInt; 013 014/** 015 * Basically a 2D array of pixels which holds all necessary information about the image geometry, keeps track of which 016 * pixels have been visited and knows how to access neighboring pixels (currently 4-neighborhood only). 017 * 018 * @author WB 019 * @version 2022/11/19 020 */ 021public class PixelMap { 022 // TODO: Bring in line with binary region neighborhoods (type). 023 024 /** Image width */ 025 public final int width; 026 027 /** Image height */ 028 public final int height; 029 030 private final Pixel[][] pixels; 031 032 /** 033 * Constructor. 034 * @param ip source image 035 */ 036 public PixelMap(ImageProcessor ip) { 037 this.width = ip.getWidth(); 038 this.height = ip.getHeight(); 039 this.pixels = makeImagePoints(ip); 040 } 041 042 private Pixel[][] makeImagePoints(ImageProcessor ip) { 043 Pixel[][] ipts = new Pixel[width][height]; 044 for (int u = 0; u < width; u++) { 045 for (int v = 0; v < height; v++) { 046 ipts[u][v] = new Pixel(u, v, ip.get(u, v)); 047 } 048 } 049 return ipts; 050 } 051 052 /** 053 * Returns the {@link Pixel} instance at the specified position. 054 * 055 * @param u horizontal position 056 * @param v vertical position 057 * @return the {@link Pixel} instance 058 */ 059 public Pixel getPixel(int u, int v) { 060 return pixels[u][v]; 061 } 062 063 /** 064 * Returns a new 1D array (i.e., a "flattened" vector in row-first order) of {@link Pixel} elements, e.g., for 065 * sorting pixels by value. 066 * 067 * @return a 1D array of pixels 068 */ 069 public Pixel[] getPixelVector() { 070 final Pixel[] pix = new Pixel[width * height]; 071 int i = 0; 072 for (int v = 0; v < height; v++) { 073 for (int u = 0; u < width; u++) { 074 pix[i] = pixels[u][v]; 075 i++; 076 } 077 } 078 return pix; 079 } 080 081 /** 082 * Sets all pixels to unvisited and resets next-neighbor search directions. 083 */ 084 void reset() { 085 //this.visited.unsetAll(); 086 for (int u = 0; u < width; u++) { 087 for (int v = 0; v < height; v++) { 088 pixels[u][v].reset(); 089 } 090 } 091 } 092 093 // -------------------------------------------------------- 094 095 private static final int[] dX = {1, 0, -1, 0}; 096 private static final int[] dY = {0, -1, 0, 1}; 097 098 /** 099 * A pixel value which knows its coordinates. This is a non-static class, i.e., {@link Pixel} instances can only 100 * exist in the context of a {@link PixelMap} instance. 101 */ 102 public class Pixel extends PntInt implements Comparable<Pixel> { 103 104 public final int val; // the pixel value 105 private byte dir = 0; // next-neighbor search direction 106 107 // only the enclosing PixelMap can instantiate pixels 108 public Pixel(int x, int y, int val) { 109 super(x, y); // Pnt2d.PntInt 110 this.val = val; 111 } 112 113 /** 114 * Sets this pixel to unvisited and resets its next-neighbor search direction. 115 */ 116 public void reset() { 117 this.dir = 0; 118 } 119 120 /** 121 * Gets the next neighbor of this pixel that is inside the containing image. 122 * 123 * @return the next neighboring {@link Pixel} or {@code null} if no more neighbors 124 */ 125 public Pixel getNextNeighbor() { 126 int u = -1, v = -1; 127 boolean found = false; 128 while (this.dir < 4 && !found) { 129 // try direction dir 130 u = this.x + dX[this.dir]; // coordinates of neighbor in direction n 131 v = this.y + dY[this.dir]; 132 found = (u >= 0 && u < width && v >= 0 && v < height); 133 this.dir++; 134 } 135 if (found) { 136 return PixelMap.this.getPixel(u, v); 137 } 138 return null; 139 } 140 141 @Override // sorts by increasing val 142 public int compareTo(Pixel other) { 143 //return val - other.val; 144 return Integer.compare(this.val, other.val); 145 } 146 147 @Override 148 public String toString() { 149 return String.format("%s[x=%d, y=%d, val=%d]", Pixel.class.getSimpleName(), x, y, val); 150 } 151 152 } 153}