001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image.processing.morphology; 031 032import java.util.HashSet; 033import java.util.Set; 034 035import org.openimaj.image.FImage; 036import org.openimaj.image.pixel.ConnectedComponent; 037import org.openimaj.image.pixel.Pixel; 038import org.openimaj.image.processor.KernelProcessor; 039import org.openimaj.image.processor.connectedcomponent.ConnectedComponentProcessor; 040import org.openimaj.math.geometry.shape.Rectangle; 041 042/** 043 * Morphological hit-and-miss transform of connected 044 * components and (assumed binary) FImages. 045 * 046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 047 */ 048public class HitAndMiss implements ConnectedComponentProcessor, KernelProcessor<Float, FImage> { 049 /** 050 * Hit and miss operator for detecting convex corners 051 */ 052 public final static HitAndMiss CONVEX_CORNERS; 053 static { 054 CONVEX_CORNERS = new HitAndMiss( 055 new StructuringElement( 056 new Pixel[] {new Pixel(0,-1), new Pixel(0,0), new Pixel(1,0)}, 057 new Pixel[] {new Pixel(-1,0), new Pixel(-1,1), new Pixel(0,1)}), 058 new StructuringElement( 059 new Pixel[] {new Pixel(0,-1), new Pixel(0,0), new Pixel(-1,0)}, 060 new Pixel[] {new Pixel(1,0), new Pixel(1,1), new Pixel(0,1)}), 061 new StructuringElement( 062 new Pixel[] {new Pixel(-1,0), new Pixel(0,0), new Pixel(0,1)}, 063 new Pixel[] {new Pixel(0,-1), new Pixel(1,-1), new Pixel(1,0)}), 064 new StructuringElement( 065 new Pixel[] {new Pixel(0,1), new Pixel(0,0), new Pixel(1,0)}, 066 new Pixel[] {new Pixel(-1,-1), new Pixel(0,-1), new Pixel(-1,0)}) 067 ); 068 } 069 070 protected Set<Pixel> outputPixels = new HashSet<Pixel>(); 071 protected StructuringElement [] elements; 072 protected int cx; 073 protected int cy; 074 protected int sw=0; 075 protected int sh=0; 076 077 /** 078 * Construct a hit and miss operator with the given 079 * structuring elements. 080 * @param ses the structuring elements 081 */ 082 public HitAndMiss(StructuringElement... ses) { 083 this.elements = ses; 084 085 for (StructuringElement se : ses) { 086 int [] sz = se.size(); 087 088 if (sw < sz[0]) sw = sz[0]; 089 if (sh < sz[1]) sh = sz[1]; 090 } 091 cx = sw/2; 092 cy = sw/2; 093 } 094 095 @Override 096 public void process(ConnectedComponent cc) { 097 //hit and miss transform; doesn't affect original pixels. 098 outputPixels.clear(); 099 100 for (StructuringElement element : elements) { 101 Set<Pixel> pixels = cc.getPixels(); 102 int [] se_size = element.size(); 103 Rectangle cc_bb = cc.calculateRegularBoundingBox(); 104 for (int j=(int) (cc_bb.y-se_size[1]); j<=cc_bb.y+se_size[1]+cc_bb.height; j++) { 105 for (int i=(int) (cc_bb.x-se_size[0]); i<=cc_bb.x+se_size[0]+cc_bb.width; i++) { 106 Pixel p = new Pixel(i, j); 107 108 if (element.matches(p, pixels)) { 109 outputPixels.add(p); 110 } 111 } 112 } 113 } 114 } 115 116 /** 117 * Get the pixels selected by the hit and miss operator 118 * @return a list of selected pixels 119 */ 120 public Set<Pixel> getPixels() { 121 return outputPixels; 122 } 123 124 @Override 125 public int getKernelHeight() { 126 return sh; 127 } 128 129 @Override 130 public int getKernelWidth() { 131 return sw; 132 } 133 134 @Override 135 public Float processKernel(FImage patch) { 136 137 for (StructuringElement element : elements) { 138 int count = 0; 139 for (Pixel p : element.positive) { 140 int px = cx - p.x; 141 int py = cy - p.y; 142 if (px>=0 && py>=0 && px<sw && py<sh && patch.pixels[py][px] == 1) 143 count++; 144 } 145 146 for (Pixel p : element.negative) { 147 int px = cx - p.x; 148 int py = cy - p.y; 149 if (px>=0 && py>=0 && px<sw && py<sh && patch.pixels[py][px] == 0) 150 count++; 151 } 152 153 if (count == element.positive.size()+element.negative.size()) return 1f; 154 } 155 156 return 0f; 157 } 158}