/** * Overrides Step getBounds method. * * @param trackerPanel the tracker panel drawing the step * @return the bounding rectangle */ public Rectangle getBounds(TrackerPanel trackerPanel) { Rectangle bounds = getMark(trackerPanel).getBounds(false); Rectangle layoutRect = layoutBounds.get(trackerPanel); if (layoutRect != null) { bounds.add(layoutRect); } return bounds; }
/** * Finds the Interactive located at the specified pixel position. * * @param panel the drawing panel * @param xpix the x pixel position * @param ypix the y pixel position * @return the TPoint that is hit, or null */ public Interactive findInteractive(DrawingPanel panel, int xpix, int ypix) { boolean highlighted = (track.trackerPanel.getFrameNumber() == getFrameNumber()); AutoTracker autoTracker = track.trackerPanel.getAutoTracker(); TrackerPanel trackerPanel = (TrackerPanel) panel; setHitRectCenter(xpix, ypix); for (int i = 0; i < points.length; i++) { if (points[i] == null || Double.isNaN(points[i].getX())) continue; if (hitRect.contains(points[i].getScreenPosition(trackerPanel))) { if (highlighted && autoTracker.isDrawingKeyFrameFor(track, i)) return null; return points[i]; } } return null; }
/** * Overrides Step getMark method. * * @param trackerPanel the tracker panel * @return the mark */ protected Mark getMark(TrackerPanel trackerPanel) { Mark mark = marks.get(trackerPanel); TPoint selection = null; if (mark == null) { selection = trackerPanel.getSelectedPoint(); Point p = null; // draws this step as "selected" shape if not null valid = true; // true if step is not NaN for (int n = 0; n < points.length; n++) { if (!valid) continue; // determine if point is valid (ie not NaN) valid = valid && !Double.isNaN(points[n].getX()) && !Double.isNaN(points[n].getY()); screenPoints[n] = points[n].getScreenPosition(trackerPanel); // step is "selected" if trackerPanel selectedPoint is position or selectedSteps contains // this step if (valid && (selection == points[n] || trackerPanel.selectedSteps.contains(this))) { p = screenPoints[n]; } } if (p == null) { if (footprint instanceof PositionVectorFootprint) { twoPoints[0] = screenPoints[0]; twoPoints[1] = trackerPanel.getSnapPoint().getScreenPosition(trackerPanel); mark = footprint.getMark(twoPoints); } else mark = footprint.getMark(screenPoints); } else { transform.setToTranslation(p.x, p.y); int scale = FontSizer.getIntegerFactor(); if (scale > 1) { transform.scale(scale, scale); } final Color color = footprint.getColor(); final Shape selectedShape = transform.createTransformedShape(selectionShape); mark = new Mark() { public void draw(Graphics2D g, boolean highlighted) { g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Paint gpaint = g.getPaint(); g.setPaint(color); g.fill(selectedShape); g.setPaint(gpaint); } public Rectangle getBounds(boolean highlighted) { return selectedShape.getBounds(); } }; } final Mark theMark = mark; mark = new Mark() { public void draw(Graphics2D g, boolean highlighted) { if (!valid) { return; } theMark.draw(g, highlighted); } public Rectangle getBounds(boolean highlighted) { return theMark.getBounds(highlighted); } }; marks.put(trackerPanel, mark); // get new text layout String s = ""; // $NON-NLS-1$ VideoClip clip = trackerPanel.getPlayer().getVideoClip(); if (clip.getStepCount() != 1) { s += clip.frameToStep(getFrameNumber()); } if (s.length() == 0) s = " "; // $NON-NLS-1$ TextLayout layout = new TextLayout(s, textLayoutFont, frc); textLayouts.put(trackerPanel, layout); // get layout position (bottom left corner of text) p = getLayoutPosition(trackerPanel); Rectangle bounds = layoutBounds.get(trackerPanel); if (bounds == null) { bounds = new Rectangle(); layoutBounds.put(trackerPanel, bounds); } Rectangle2D rect = layout.getBounds(); // set bounds (top left corner and size) bounds.setRect(p.x, p.y - rect.getHeight(), rect.getWidth(), rect.getHeight()); } return mark; }
/** * Centers the hit testing rectangle on the specified screen point. * * @param xpix the x pixel position * @param ypix the y pixel position */ protected void setHitRectCenter(int xpix, int ypix) { hitRect.setLocation(xpix - hitRect.width / 2, ypix - hitRect.height / 2); }
/** * Gets the template location at which the best match occurs in a rectangle and along a line. May * return null. * * @param target the image to search * @param searchRect the rectangle to search within the target image * @param x0 the x-component of a point on the line * @param y0 the y-component of a point on the line * @param slope the slope of the line * @param spread the spread of the line (line width = 1+2*spread) * @return the optimized template location of the best match, if any */ public TPoint getMatchLocation( BufferedImage target, Rectangle searchRect, double x0, double y0, double theta, int spread) { wTarget = target.getWidth(); hTarget = target.getHeight(); // determine insets needed to accommodate template int left = wTemplate / 2, right = left; if (wTemplate % 2 > 0) right++; int top = hTemplate / 2, bottom = top; if (hTemplate % 2 > 0) bottom++; // trim search rectangle if necessary searchRect.x = Math.max(left, Math.min(wTarget - right, searchRect.x)); searchRect.y = Math.max(top, Math.min(hTarget - bottom, searchRect.y)); searchRect.width = Math.min(wTarget - searchRect.x - right, searchRect.width); searchRect.height = Math.min(hTarget - searchRect.y - bottom, searchRect.height); if (searchRect.width <= 0 || searchRect.height <= 0) { peakHeight = Double.NaN; peakWidth = Double.NaN; return null; } // set up test pixels to search (rectangle plus template) int xMin = Math.max(0, searchRect.x - left); int xMax = Math.min(wTarget, searchRect.x + searchRect.width + right); int yMin = Math.max(0, searchRect.y - top); int yMax = Math.min(hTarget, searchRect.y + searchRect.height + bottom); wTest = xMax - xMin; hTest = yMax - yMin; if (target.getType() != BufferedImage.TYPE_INT_RGB) { BufferedImage image = new BufferedImage(wTarget, hTarget, BufferedImage.TYPE_INT_RGB); image.createGraphics().drawImage(target, 0, 0, null); target = image; } targetPixels = new int[wTest * hTest]; target.getRaster().getDataElements(xMin, yMin, wTest, hTest, targetPixels); // get the points to search along the line ArrayList<Point2D> searchPts = getSearchPoints(searchRect, x0, y0, theta); if (searchPts == null) { peakHeight = Double.NaN; peakWidth = Double.NaN; return null; } // collect differences in a map as they are measured HashMap<Point2D, Double> diffs = new HashMap<Point2D, Double>(); // find the point with the minimum difference from template double matchDiff = largeNumber; // larger than typical differences int xMatch = 0, yMatch = 0; double avgDiff = 0; Point2D matchPt = null; for (Point2D pt : searchPts) { int x = (int) pt.getX(); int y = (int) pt.getY(); double diff = getDifferenceAtTestPoint(x, y); diffs.put(pt, diff); avgDiff += diff; if (diff < matchDiff) { matchDiff = diff; xMatch = x; yMatch = y; matchPt = pt; } } avgDiff /= searchPts.size(); peakHeight = avgDiff / matchDiff - 1; peakWidth = Double.NaN; double dl = 0; int matchIndex = searchPts.indexOf(matchPt); // if match is not exact, fit a Gaussian and find peak if (!Double.isInfinite(peakHeight) && matchIndex > 0 && matchIndex < searchPts.size() - 1) { // fill data arrays Point2D pt = searchPts.get(matchIndex - 1); double diff = diffs.get(pt); xValues[0] = -pt.distance(matchPt); yValues[0] = avgDiff / diff - 1; xValues[1] = 0; yValues[1] = peakHeight; pt = searchPts.get(matchIndex + 1); diff = diffs.get(pt); xValues[2] = pt.distance(matchPt); yValues[2] = avgDiff / diff - 1; // determine approximate offset (dl) and width (w) values double pull = -xValues[0] / (yValues[1] - yValues[0]); double push = xValues[2] / (yValues[1] - yValues[2]); if (Double.isNaN(pull)) pull = LARGE_NUMBER; if (Double.isNaN(push)) push = LARGE_NUMBER; dl = 0.3 * (xValues[2] - xValues[0]) * (push - pull) / (push + pull); double ratio = dl > 0 ? peakHeight / yValues[0] : peakHeight / yValues[2]; double w = dl > 0 ? dl - xValues[0] : dl - xValues[2]; w = w * w / Math.log(ratio); // set parameters and fit to x data dataset.clear(); dataset.append(xValues, yValues); double rmsDev = 1; for (int k = 0; k < 3; k++) { double c = k == 0 ? w : k == 1 ? w / 3 : w * 3; f.setParameterValue(0, peakHeight); f.setParameterValue(1, dl); f.setParameterValue(2, c); rmsDev = fitter.fit(f); if (rmsDev < 0.01) { // fitter succeeded (3-point fit should be exact) dl = f.getParameterValue(1); peakWidth = f.getParameterValue(2); break; } } } double dx = dl * Math.cos(theta); double dy = dl * Math.sin(theta); double xImage = xMatch + searchRect.x - left - trimLeft + dx; double yImage = yMatch + searchRect.y - top - trimTop + dy; return new TPoint(xImage, yImage); }
/** * Gets the template location at which the best match occurs in a rectangle. May return null. * * @param target the image to search * @param searchRect the rectangle to search within the target image * @return the optimized template location at which the best match, if any, is found */ public TPoint getMatchLocation(BufferedImage target, Rectangle searchRect) { wTarget = target.getWidth(); hTarget = target.getHeight(); // determine insets needed to accommodate template int left = wTemplate / 2, right = left; if (wTemplate % 2 > 0) right++; int top = hTemplate / 2, bottom = top; if (hTemplate % 2 > 0) bottom++; // trim search rectangle if necessary searchRect.x = Math.max(left, Math.min(wTarget - right, searchRect.x)); searchRect.y = Math.max(top, Math.min(hTarget - bottom, searchRect.y)); searchRect.width = Math.min(wTarget - searchRect.x - right, searchRect.width); searchRect.height = Math.min(hTarget - searchRect.y - bottom, searchRect.height); if (searchRect.width <= 0 || searchRect.height <= 0) { peakHeight = Double.NaN; peakWidth = Double.NaN; return null; } // set up test pixels to search (rectangle plus template) int xMin = Math.max(0, searchRect.x - left); int xMax = Math.min(wTarget, searchRect.x + searchRect.width + right); int yMin = Math.max(0, searchRect.y - top); int yMax = Math.min(hTarget, searchRect.y + searchRect.height + bottom); wTest = xMax - xMin; hTest = yMax - yMin; if (target.getType() != BufferedImage.TYPE_INT_RGB) { BufferedImage image = new BufferedImage(wTarget, hTarget, BufferedImage.TYPE_INT_RGB); image.createGraphics().drawImage(target, 0, 0, null); target = image; } targetPixels = new int[wTest * hTest]; target.getRaster().getDataElements(xMin, yMin, wTest, hTest, targetPixels); // find the rectangle point with the minimum difference double matchDiff = largeNumber; // larger than typical differences int xMatch = 0, yMatch = 0; double avgDiff = 0; for (int x = 0; x <= searchRect.width; x++) { for (int y = 0; y <= searchRect.height; y++) { double diff = getDifferenceAtTestPoint(x, y); avgDiff += diff; if (diff < matchDiff) { matchDiff = diff; xMatch = x; yMatch = y; } } } avgDiff /= (searchRect.width * searchRect.height); peakHeight = avgDiff / matchDiff - 1; peakWidth = Double.NaN; double dx = 0, dy = 0; // if match is not exact, fit a Gaussian and find peak if (!Double.isInfinite(peakHeight)) { // fill data arrays xValues[1] = yValues[1] = peakHeight; for (int i = -1; i < 2; i++) { if (i == 0) continue; double diff = getDifferenceAtTestPoint(xMatch + i, yMatch); xValues[i + 1] = avgDiff / diff - 1; diff = getDifferenceAtTestPoint(xMatch, yMatch + i); yValues[i + 1] = avgDiff / diff - 1; } // estimate peakHeight = peak of gaussian // estimate offset dx of gaussian double pull = 1 / (xValues[1] - xValues[0]); double push = 1 / (xValues[1] - xValues[2]); if (Double.isNaN(pull)) pull = LARGE_NUMBER; if (Double.isNaN(push)) push = LARGE_NUMBER; dx = 0.6 * (push - pull) / (push + pull); // estimate width wx of gaussian double ratio = dx > 0 ? peakHeight / xValues[0] : peakHeight / xValues[2]; double wx = dx > 0 ? dx + 1 : dx - 1; wx = wx * wx / Math.log(ratio); // estimate offset dy of gaussian pull = 1 / (yValues[1] - yValues[0]); push = 1 / (yValues[1] - yValues[2]); if (Double.isNaN(pull)) pull = LARGE_NUMBER; if (Double.isNaN(push)) push = LARGE_NUMBER; dy = 0.6 * (push - pull) / (push + pull); // estimate width wy of gaussian ratio = dy > 0 ? peakHeight / yValues[0] : peakHeight / yValues[2]; double wy = dy > 0 ? dy + 1 : dy - 1; wy = wy * wy / Math.log(ratio); // set x parameters and fit to x data dataset.clear(); dataset.append(pixelOffsets, xValues); double rmsDev = 1; for (int k = 0; k < 3; k++) { double c = k == 0 ? wx : k == 1 ? wx / 3 : wx * 3; f.setParameterValue(0, peakHeight); f.setParameterValue(1, dx); f.setParameterValue(2, c); rmsDev = fitter.fit(f); if (rmsDev < 0.01) { // fitter succeeded (3-point fit should be exact) dx = f.getParameterValue(1); peakWidth = f.getParameterValue(2); break; } } if (!Double.isNaN(peakWidth)) { // set y parameters and fit to y data dataset.clear(); dataset.append(pixelOffsets, yValues); for (int k = 0; k < 3; k++) { double c = k == 0 ? wy : k == 1 ? wy / 3 : wy * 3; f.setParameterValue(0, peakHeight); f.setParameterValue(1, dx); f.setParameterValue(2, c); rmsDev = fitter.fit(f); if (rmsDev < 0.01) { // fitter succeeded (3-point fit should be exact) dy = f.getParameterValue(1); peakWidth = (peakWidth + f.getParameterValue(2)) / 2; break; } } if (rmsDev > 0.01) peakWidth = Double.NaN; } } double xImage = xMatch + searchRect.x - left - trimLeft + dx; double yImage = yMatch + searchRect.y - top - trimTop + dy; return new TPoint(xImage, yImage); }