diff --git a/src/ch/unizh/ini/jaer/projects/cochsoundloc/DetectMovementFilter.java b/src/ch/unizh/ini/jaer/projects/cochsoundloc/DetectMovementFilter.java index 44ae08693..61bbec6e1 100644 --- a/src/ch/unizh/ini/jaer/projects/cochsoundloc/DetectMovementFilter.java +++ b/src/ch/unizh/ini/jaer/projects/cochsoundloc/DetectMovementFilter.java @@ -61,7 +61,7 @@ public EventPacket filterPacket(EventPacket this.confidenceThreshold) { java.awt.geom.Point2D.Float location = clst.getLocation(); - //log.info("ClusterNumEvents: " + clst.getNumEvents() + " AvgEventRate: "+clst.getAvgEventRate()); + //log.info("ClusterNumEvents: " + clst.getNumEvents() + " AvgEventRate: "+clst.getAvgEventRateHz()); //log.info("ClusterLocation: ( " + location.x + " , " + location.y + " )"); if (connectToPanTiltThread == true) { CommObjForPanTilt filterOutput = new CommObjForPanTilt(); diff --git a/src/net/sf/jaer/eventprocessing/tracking/ClusterInterface.java b/src/net/sf/jaer/eventprocessing/tracking/ClusterInterface.java index 41e943afc..0317da93f 100644 --- a/src/net/sf/jaer/eventprocessing/tracking/ClusterInterface.java +++ b/src/net/sf/jaer/eventprocessing/tracking/ClusterInterface.java @@ -63,6 +63,13 @@ public interface ClusterInterface { */ public float getRadius(); + /** Returns area in pixels. + * + * @return some measure of cluster area size. + */ + public float getArea(); + + /** Returns measure of cluster 'mass' or importance. This mass is increased by support (e.g. events) and decays away with lack of support. * * @return the mass measure. diff --git a/src/net/sf/jaer/eventprocessing/tracking/RectangularClusterTracker.java b/src/net/sf/jaer/eventprocessing/tracking/RectangularClusterTracker.java index ef7f89c17..7821657ed 100644 --- a/src/net/sf/jaer/eventprocessing/tracking/RectangularClusterTracker.java +++ b/src/net/sf/jaer/eventprocessing/tracking/RectangularClusterTracker.java @@ -51,6 +51,7 @@ import net.sf.jaer.graphics.ChipCanvas; import net.sf.jaer.graphics.FrameAnnotater; import net.sf.jaer.util.DrawGL; +import net.sf.jaer.util.EngineeringFormat; import net.sf.jaer.util.filter.LowpassFilter; /** @@ -70,6 +71,7 @@ public class RectangularClusterTracker extends EventFilter2D // TODO delegate worker object to update the clusters (RectangularClusterTrackerDelegate) // public TelluridePatchExtractor TelluridePatchExtractor = new TelluridePatchExtractor(); + EngineeringFormat fmt = new EngineeringFormat(); /** * maximum and minimum allowed dynamic aspect ratio when dynamic @@ -126,14 +128,21 @@ public class RectangularClusterTracker extends EventFilter2D // events, then slow down, speed // up, etc. protected boolean highwayPerspectiveEnabled = getBoolean("highwayPerspectiveEnabled", false); + protected int thresholdMassForVisibleCluster = getInt("thresholdMassForVisibleCluster", 10); - protected float thresholdVelocityForVisibleCluster = getFloat("thresholdVelocityForVisibleCluster", 0); protected int clusterMassDecayTauUs = getInt("clusterMassDecayTauUs", 10000); + + private boolean useEventRatePerPixelForVisibilty = getBoolean("useEventRatePerPixelForVisibilty", true); + private float eventRatePerPixelLowpassTauS = getFloat("eventRatePerPixelLowpassTauS", 0.1f); + private float thresholdEventRatePerPixelForVisibleClusterHz = getFloat("thresholdEventRatePerPixelForVisibleClusterHz", 10f); + + protected float thresholdVelocityForVisibleCluster = getFloat("thresholdVelocityForVisibleCluster", 0); protected boolean enableClusterExitPurging = getBoolean("enableClusterExitPurging", true); private boolean purgeIfClusterOverlapsBorder = getBoolean("purgeIfClusterOverlapsBorder", true); protected float velAngDiffDegToNotMerge = getFloat("velAngDiffDegToNotMerge", 60); protected boolean showClusterNumber = getBoolean("showClusterNumber", false); protected boolean showClusterEps = getBoolean("showClusterEps", false); + protected boolean showClusterEpsPerPx = getBoolean("showClusterEpsPerPx", false); private boolean showClusterRadius = getBoolean("showClusterRadius", false); protected boolean showClusterVelocity = getBoolean("showClusterVelocity", false); protected boolean showClusterVelocityVector = getBoolean("showClusterVelocityVector", false); @@ -159,11 +168,11 @@ public class RectangularClusterTracker extends EventFilter2D private float smoothIntegral = getFloat("smoothIntegral", .001f); private float surroundInhibitionCost = getFloat("surroundInhibitionCost", 1); - /** + /** * scaling can't make cluster bigger or smaller than this ratio to default * cluster size. */ - private float maxSizeScaleRatio = getFloat("maxSizeScaleRatio",4); + private float maxSizeScaleRatio = getFloat("maxSizeScaleRatio", 4); /** * The list of clusters (visible and invisible). @@ -238,12 +247,18 @@ public RectangularClusterTracker(AEChip chip) { */ protected void setTooltips() { final String sizing = "2: Sizing", mov = "4: Movement", life = "3: Lifetime", disp = "Display", common = "1: Common", - update = "5: Update", logg = "7: Logging", pi = "6: PI Controller", options="8: Options"; + update = "5: Update", logg = "7: Logging", pi = "6: PI Controller", options = "8: Options"; setPropertyTooltip(life, "enableClusterExitPurging", "enables rapid purging of clusters that hit edge of scene"); setPropertyTooltip(life, "purgeIfClusterOverlapsBorder", "purge any cluster that overlaps edge (if false, center must go outside border)"); + setPropertyTooltip(common, "clusterMassDecayTauUs", "time constant of exponential decay of \"mass\" of cluster between events (us)"); setPropertyTooltip(common, "thresholdMassForVisibleCluster", "Cluster needs this \"mass\" to be visible. Mass increments with each event and decays with e-folding time constant of clusterMassDecayTauUs. Use \"showAllClusters\" to diagnose fleeting clusters."); + + setPropertyTooltip(common, "useEventRatePerPixelForVisibilty", "select this option to use normalized event rate per pixel rather than mass for visibility and lifetime"); + setPropertyTooltip(common, "eventRatePerPixelLowpassTauS", "time constant of cluster event rate lowpass filter in seconds"); + setPropertyTooltip(common, "thresholdEventRatePerPixelForVisibleClusterHz", "clusters with average event rates in Hz per pixel above this are \"visible\""); + setPropertyTooltip(life, "thresholdVelocityForVisibleCluster", "cluster must have at least this velocity in pixels/sec to become visible"); setPropertyTooltip(life, "dontMergeEver", "never merge overlapping clusters"); @@ -294,8 +309,9 @@ protected void setTooltips() { setPropertyTooltip(disp, "showClusterVelocity", "shows velocity vector as (vx,vy) in px/s"); setPropertyTooltip(disp, "showClusterRadius", "draws cluster radius"); setPropertyTooltip(disp, "showClusterEps", "shows cluster events per second"); + setPropertyTooltip(disp, "showClusterEpsPerPx", "shows cluster events per second per pixel"); setPropertyTooltip(disp, "showClusterNumber", "shows cluster ID number"); - setPropertyTooltip(common, "showClusterMass", "shows cluster mass; mass is decaying measure of the rate of captured events"); + setPropertyTooltip(disp, "showClusterMass", "shows cluster mass; mass is decaying measure of the rate of captured events"); setPropertyTooltip(common, "velocityVectorScaling", "scaling of drawn velocity vectors from pps to pixels in AEChip pixel space"); setPropertyTooltip(update, "useOnePolarityOnlyEnabled", "use only one event polarity"); setPropertyTooltip(update, "useOffPolarityOnlyEnabled", "use only OFF events, not ON - if useOnePolarityOnlyEnabled"); @@ -714,7 +730,7 @@ protected void mergeClusters() { // another cluster. if (isHighwayPerspectiveEnabled()) { for (Cluster c : clusters) { - c.setRadius(defaultClusterRadius*chip.getMaxSize()); + c.setRadius(defaultClusterRadius * chip.getMaxSize()); } } } @@ -735,22 +751,7 @@ protected void pruneClusters(int t) { continue; // don't kill off cluster spawned from first event } boolean massTooSmall = false; - // if ( clusterLifetimeIncreasesWithAge ){ - // int age = c.getLifetime(); - // int supportTime = clusterMassDecayTauUs; - // if ( age < clusterMassDecayTauUs ){ - // supportTime = age; - // } - // if ( timeSinceSupport > supportTime ){ - // killOff = true; - //// System.out.println("pruning unsupported "+c); - // } - // } else{ - // if ( timeSinceSupport > clusterMassDecayTauUs ){ - // killOff = true; - //// System.out.println("pruning unzupported "+c); - // } - // } + int lifetime = c.getLifetime(); // TODO patch to handle clusters that are not hit with events @@ -758,13 +759,20 @@ protected void pruneClusters(int t) { lifetime = t - c.getBirthTime(); } - float massThreshold = thresholdMassForVisibleCluster; - if (highwayPerspectiveEnabled) { - massThreshold *= c.getPerspectiveScaleFactor(); - } - // do not kill off clusters that were just born or have not lived at least their clusterMassDecayTauUs - if (((lifetime == 0) || (lifetime >= clusterMassDecayTauUs)) && (c.getMassNow(t) < massThreshold)) { - massTooSmall = true; + if (!useEventRatePerPixelForVisibilty) { + float massThreshold = thresholdMassForVisibleCluster; + if (highwayPerspectiveEnabled) { + massThreshold *= c.getPerspectiveScaleFactor(); + } + // do not kill off clusters that were just born or have not lived at least their clusterMassDecayTauUs + if (((lifetime == 0) || (lifetime >= clusterMassDecayTauUs)) && (c.getMassNow(t) < massThreshold)) { + massTooSmall = true; + } + } else { // event rate per pixel + // do not kill off clusters that were just born or have not lived at least their clusterMassDecayTauUs + if (((lifetime == 0) || (lifetime >= clusterMassDecayTauUs)) && (c.getAvgEventRateHz()/c.getArea() < thresholdEventRatePerPixelForVisibleClusterHz)) { + massTooSmall = true; + } } boolean hitEdge = c.hasHitEdge(); if ((t0 > t) || massTooSmall || (timeSinceSupport < 0) || hitEdge) { @@ -834,7 +842,7 @@ protected void updateClusterList(int t) { private void updateVisibilities(int t) { visibleClusters.clear(); for (Cluster c : clusters) { - if (c.checkAndSetClusterVisibilityFlag(t)) { + if (c.updateVisibility(t)) { visibleClusters.add(c); } } @@ -1197,7 +1205,8 @@ public class Cluster implements ClusterInterface { // TODO badly needs a cheap c * is updated when cluster becomes visible. * lastEventTimestamp is the last time the cluster was * touched either by an event or by some other timestamped update, e.g. - * null null null null null null null null null null null null null {@link #updateClusterList(net.sf.jaer.event.EventPacket, int) + * null null null null null null null null null null null null null null + * null null null null {@link #updateClusterList(net.sf.jaer.event.EventPacket, int) * }. * * @see #isVisible() @@ -1239,11 +1248,11 @@ public class Cluster implements ClusterInterface { // TODO badly needs a cheap c */ private int clusterNumber; /** - * Average event rate as computed using mixingFactor. + * Average event rate as computed using eventRatePerPixelLowpassTauS. * - * @see #mixingFactor + * @see #eventRatePerPixelLowpassTauS */ - private float avgEventRate = 0; + private float avgEventRateHz = 0; private float radius; // in chip chip pixels protected float aspectRatio, radiusX, radiusY; protected LinkedList path = new LinkedList<>(); @@ -1425,7 +1434,7 @@ protected void mergeTwoClustersToThis(Cluster one, Cluster two) { velocityValid = stronger.velocityValid; vxFilter = stronger.vxFilter; vyFilter = stronger.vyFilter; - avgEventRate = stronger.avgEventRate; + avgEventRateHz = stronger.avgEventRateHz; avgISI = stronger.avgISI; hasObtainedSupport = one.hasObtainedSupport || two.hasObtainedSupport; // if either was ever visible then mark merged wasEverVisible visibilityFlag = one.visibilityFlag || two.visibilityFlag; // make it visible if either visible @@ -1561,10 +1570,10 @@ protected void updateEventRate(BasicEvent event, float m) { if (instantaneousISI <= 0) { instantaneousISI = 1; } - float m1 = 1 - m; - avgISI = (m1 * avgISI) + (m * instantaneousISI); - instantaneousEventRate = 1f / instantaneousISI; - avgEventRate = (m1 * avgEventRate) + (m * instantaneousEventRate); + instantaneousEventRate = 1e6f / instantaneousISI; // in Hz + float lowpassEps = 1e-6f * instantaneousISI / eventRatePerPixelLowpassTauS; + + avgEventRateHz = ((1 - lowpassEps) * avgEventRateHz) + (lowpassEps * instantaneousEventRate); } /** @@ -1581,7 +1590,9 @@ protected void updateMass(int t) { // float dmass = normDistance <= 1 ? 1 : -1; float dmass = normDistance <= 1 ? 1 : -surroundInhibitionCost; mass = dmass + (mass * (float) Math.exp((float) (lastEventTimestamp - t) / clusterMassDecayTauUs)); - if(mass<0) mass=0; + if (mass < 0) { + mass = 0; + } } else { boolean wasInfinite = Float.isInfinite(mass); // don't worry about distance, just increment @@ -1607,8 +1618,8 @@ protected boolean hasHitEdge() { if (!enableClusterExitPurging) { return false; } - - if(isPurgeIfClusterOverlapsBorder()){ + + if (isPurgeIfClusterOverlapsBorder()) { return isOverlappingBorder(); } @@ -1733,10 +1744,10 @@ public void draw(GLAutoDrawable drawable) { DrawGL.drawCircle(gl, 0, 0, radiusX, 15); if (updateClustersOnlyFromEventsNearEdge) { gl.glPushAttrib(GL2.GL_CURRENT_BIT); - float lw=radius*ellipticalClusterEdgeThickness; - rgb[3]=.5f; + float lw = radius * ellipticalClusterEdgeThickness; + rgb[3] = .5f; gl.glColor4fv(rgb, 0); - DrawGL.drawCircle(gl, 0, 0, radius-lw, 15); + DrawGL.drawCircle(gl, 0, 0, radius - lw, 15); gl.glPopAttrib(); } } else { @@ -1767,20 +1778,24 @@ public void draw(GLAutoDrawable drawable) { // text annoations on clusters, setup final int font = GLUT.BITMAP_HELVETICA_18; - if (showClusterMass || showClusterEps || showClusterNumber || showClusterVelocity) { - gl.glColor3f(1, 1, 1); + if (showClusterMass || showClusterEps || showClusterNumber || showClusterVelocity || showClusterEpsPerPx) { +// gl.glColor3f(1, 1, 1); gl.glRasterPos3f(location.x, location.y, 0); } // cGLUT.glutBitmapString(font, String.format("%.0fdeg", instantaneousAngle*180/Math.PI)); // annotate with // instantaneousAngle (debug) if (showClusterVelocity) { - cGLUT.glutBitmapString(font, String.format("v(vx,vy)=%.0f(%.0f,%.0f) pps", getSpeedPPS(), getVelocityPPS().x, getVelocityPPS().y)); + cGLUT.glutBitmapString(font, String.format("v(vx,vy)=%.0f(%.0f,%.0f) pps ", getSpeedPPS(), getVelocityPPS().x, getVelocityPPS().y)); } // if (showClusterRadius) cGLUT.glutBitmapString(font, String.format("rad=%.1f ", getRadius())); if (showClusterEps) { - cGLUT.glutBitmapString(font, String.format("eps=%.0fk ", (getAvgEventRate() / (AEConstants.TICK_DEFAULT_US)) * 1e3f)); // annotate - // the cluster with the event rate computed as 1/(avg ISI) in keps + cGLUT.glutBitmapString(font, String.format("eps=%sHz ", fmt.format(getAvgEventRateHz()))); // annotate + // the cluster with the event rate + } + if (showClusterEpsPerPx) { + cGLUT.glutBitmapString(font, String.format("eps/px=%sHz ", fmt.format(getAvgEventRateHz() / getArea()))); // annotate + // the cluster with the event rate per pixel } if (showClusterNumber) { cGLUT.glutBitmapString(font, String.format("#=%d ", hashCode())); // annotate the cluster with hash ID @@ -1924,7 +1939,7 @@ protected void updatePosition(final BasicEvent event, final float m) { if (!smoothMove) { if (useEllipticalClusters && updateClustersOnlyFromEventsNearEdge) { float dist = distanceTo(event); - if (dist>radius || dist < radius * (1-ellipticalClusterEdgeThickness)) { + if (dist > radius || dist < radius * (1 - ellipticalClusterEdgeThickness)) { return; } } @@ -2170,6 +2185,18 @@ public final float getRadius() { return radius; } + /** + * The effective area of the cluster depends on its fixed size or + * dynamically-determined radius, which might depend on + * surroundInhibitionEnabled. + * + * @return the cluster area in pixels: radius*radius*4. + */ + @Override + public final float getArea() { + return radius * radius * 4; + } + /** * This method sets the radius field according to the * highwayPerspectiveEnabled and perspective point, along with the @@ -2209,16 +2236,22 @@ public void setRadius(float r) { * @param t the current timestamp * @return true if cluster is visible */ - public boolean checkAndSetClusterVisibilityFlag(int t) { + public boolean updateVisibility(int t) { boolean ret = true; // TODO: In the tooltip it is promised that the thresholdMassForVisibleCluster is // checking the MASS of the cluster to determine if its visible. However as far // as I see here this is not the case! Instead we check only for the number of Events this cluster has // gathered - if (getMassNow(t) < thresholdMassForVisibleCluster) { - ret = false; + if (!useEventRatePerPixelForVisibilty) { + if (getMassNow(t) < thresholdMassForVisibleCluster) { + ret = false; + } + } else { // based on average event rate per pixel + if (getAvgEventRateHz() / getArea() < thresholdEventRatePerPixelForVisibleClusterHz) { + ret = false; + } } - if (useVelocity && thresholdVelocityForVisibleCluster>0) { + if (useVelocity && thresholdVelocityForVisibleCluster > 0) { double speed = (sqrt((velocityPPT.x * velocityPPT.x) + (velocityPPT.y * velocityPPT.y)) * 1e6) / AEConstants.TICK_DEFAULT_US; // speed is in pixels/sec if (speed < thresholdVelocityForVisibleCluster) { @@ -2230,7 +2263,7 @@ public boolean checkAndSetClusterVisibilityFlag(int t) { birthLocation.y = location.y; // reset location of birth to presumably less noisy current location. } hasObtainedSupport = (hasObtainedSupport || ret); - if(ret && !visibilityFlag){ + if (ret && !visibilityFlag) { onBecomingVisible(); } visibilityFlag = ret; @@ -2239,11 +2272,11 @@ public boolean checkAndSetClusterVisibilityFlag(int t) { /** * Returns the flag that marks cluster visibility. This flag is set by - * checkAndSetClusterVisibilityFlag. This flag flags - * whether cluster has gotten enough support. + * updateVisibility. This flag flags whether cluster has + * gotten enough support. * * @return true if cluster has obtained enough support. - * @see #checkAndSetClusterVisibilityFlag + * @see #updateVisibility */ @Override final public boolean isVisible() { @@ -2425,7 +2458,7 @@ public float getMeasuredRadius() { } public float getMeasuredAverageEventRate() { - return avgEventRate / radius; + return avgEventRateHz / radius; } /** @@ -2499,13 +2532,13 @@ protected void onPruning() { } /** - * onBecomingVisible is called when the cluster has first become visible. - * By default no special action is taken. Subclasses - * can override this method to take a special action on pruning. + * onBecomingVisible is called when the cluster has first become + * visible. By default no special action is taken. Subclasses can + * override this method to take a special action on pruning. */ protected void onBecomingVisible() { } - + /** * Determines if this cluster overlaps the center of another cluster. * @@ -2762,17 +2795,18 @@ public void setAvgISI(float avgISI) { // /** - * @return average event rate in spikes per timestamp tick. Average is - * computed using location mixing factor. Note that this measure - * emphasizes the high spike rates because a few events in rapid - * succession can rapidly push up the average rate. + * @return average event rate in Hz. Average is computed using + * eventRatePerPixelLowpassTauS. + *

+ * Note that this measure emphasizes the high spike rates because a few + * events in rapid succession can rapidly push up the average rate. */ - public float getAvgEventRate() { - return avgEventRate; + public float getAvgEventRateHz() { + return avgEventRateHz; } - public void setAvgEventRate(float avgEventRate) { - this.avgEventRate = avgEventRate; + public void setAvgEventRateHz(float avgEventRateHz) { + this.avgEventRateHz = avgEventRateHz; } // @@ -2917,6 +2951,7 @@ synchronized public void annotate(GLAutoDrawable drawable) { return; } + fmt.setPrecision(1); // digits after decimel point GL2 gl = drawable.getGL().getGL2(); // when we getString this we are already set up with updateShape 1=1 pixel, // at LL corner if (gl == null) { @@ -3716,8 +3751,23 @@ public void setShowClusterEps(boolean showClusterEps) { this.showClusterEps = showClusterEps; putBoolean("showClusterEps", showClusterEps); } - // + /** + * @return the showClusterEps + */ + public boolean isShowClusterEpsPerPx() { + return showClusterEpsPerPx; + } + + /** + * @param showClusterEps the showClusterEps to set + */ + public void setShowClusterEpsPerPx(boolean showClusterEpsPerPx) { + this.showClusterEpsPerPx = showClusterEpsPerPx; + putBoolean("showClusterEpsPerPx", showClusterEpsPerPx); + } + + // // /** * @return the showClusterVelocity @@ -4218,21 +4268,24 @@ public float getEllipticalClusterEdgeThickness() { } /** - * @param ellipticalClusterEdgeThickness the ellipticalClusterEdgeThickness to set + * @param ellipticalClusterEdgeThickness the ellipticalClusterEdgeThickness + * to set */ public void setEllipticalClusterEdgeThickness(float ellipticalClusterEdgeThickness) { this.ellipticalClusterEdgeThickness = ellipticalClusterEdgeThickness; - putFloat("ellipticalClusterEdgeThickness",ellipticalClusterEdgeThickness); + putFloat("ellipticalClusterEdgeThickness", ellipticalClusterEdgeThickness); } - - /** faster sqrt, from https://stackoverflow.com/questions/13263948/fast-sqrt-in-java-at-the-expense-of-accuracy - * + + /** + * faster sqrt, from + * https://stackoverflow.com/questions/13263948/fast-sqrt-in-java-at-the-expense-of-accuracy + * * @param d - * @return approx sqrt(d) + * @return approx sqrt(d) */ - private double sqrt(double d){ + private double sqrt(double d) { // double realsqrt=Math.sqrt(d); - double sqrt = Double.longBitsToDouble( ( ( Double.doubleToLongBits( d )-(1l<<52) )>>1 ) + ( 1l<<61 ) ); + double sqrt = Double.longBitsToDouble(((Double.doubleToLongBits(d) - (1l << 52)) >> 1) + (1l << 61)); // double actOverReal=sqrt/realsqrt; return sqrt; @@ -4246,11 +4299,12 @@ public boolean isPurgeIfClusterOverlapsBorder() { } /** - * @param purgeIfClusterOverlapsBorder the purgeIfClusterOverlapsBorder to set + * @param purgeIfClusterOverlapsBorder the purgeIfClusterOverlapsBorder to + * set */ public void setPurgeIfClusterOverlapsBorder(boolean purgeIfClusterOverlapsBorder) { this.purgeIfClusterOverlapsBorder = purgeIfClusterOverlapsBorder; - putBoolean("purgeIfClusterOverlapsBorder",purgeIfClusterOverlapsBorder); + putBoolean("purgeIfClusterOverlapsBorder", purgeIfClusterOverlapsBorder); } /** @@ -4265,6 +4319,54 @@ public float getMaxSizeScaleRatio() { */ public void setMaxSizeScaleRatio(float maxSizeScaleRatio) { this.maxSizeScaleRatio = maxSizeScaleRatio; - putFloat("maxSizeScaleRatio",maxSizeScaleRatio); + putFloat("maxSizeScaleRatio", maxSizeScaleRatio); + } + + /** + * @return the useEventRatePerPixelForVisibilty + */ + public boolean isUseEventRatePerPixelForVisibilty() { + return useEventRatePerPixelForVisibilty; + } + + /** + * @param useEventRatePerPixelForVisibilty the + * useEventRatePerPixelForVisibilty to set + */ + public void setUseEventRatePerPixelForVisibilty(boolean useEventRatePerPixelForVisibilty) { + this.useEventRatePerPixelForVisibilty = useEventRatePerPixelForVisibilty; + putBoolean("useEventRatePerPixelForVisibilty", useEventRatePerPixelForVisibilty); + } + + /** + * @return the eventRatePerPixelLowpassTauS + */ + public float getEventRatePerPixelLowpassTauS() { + return eventRatePerPixelLowpassTauS; + } + + /** + * @param eventRatePerPixelLowpassTauS the eventRatePerPixelLowpassTauS to + * set + */ + public void setEventRatePerPixelLowpassTauS(float eventRatePerPixelLowpassTauS) { + this.eventRatePerPixelLowpassTauS = eventRatePerPixelLowpassTauS; + putFloat("eventRatePerPixelLowpassTauS", eventRatePerPixelLowpassTauS); + } + + /** + * @return the thresholdEventRatePerPixelForVisibleClusterHz + */ + public float getThresholdEventRatePerPixelForVisibleClusterHz() { + return thresholdEventRatePerPixelForVisibleClusterHz; + } + + /** + * @param thresholdEventRatePerPixelForVisibleClusterHz the + * thresholdEventRatePerPixelForVisibleClusterHz to set + */ + public void setThresholdEventRatePerPixelForVisibleClusterHz(float thresholdEventRatePerPixelForVisibleClusterHz) { + this.thresholdEventRatePerPixelForVisibleClusterHz = thresholdEventRatePerPixelForVisibleClusterHz; + putFloat("thresholdEventRatePerPixelForVisibleClusterHz", thresholdEventRatePerPixelForVisibleClusterHz); } }