/*
* Copyright (C) 2009-2012 Samuel Audet
*
* Licensed either under the Apache License, Version 2.0, or (at your option)
* under the terms of the GNU General Public License as published by
* the Free Software Foundation (subject to the "Classpath" exception),
* either version 2, or any later version (collectively, the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.gnu.org/licenses/
* http://www.gnu.org/software/classpath/license.html
*
* or as provided in the LICENSE.txt file that accompanied this code.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bytedeco.javacv;
import java.nio.ByteBuffer;
import java.util.Arrays;
import static org.bytedeco.javacpp.ARToolKitPlus.*;
import static org.bytedeco.javacpp.opencv_core.*;
import static org.bytedeco.javacpp.opencv_imgproc.*;
/**
*
* @author Samuel Audet
*/
public class Marker implements Cloneable {
public Marker(int id, double[] corners, double confidence) {
this.id = id;
this.corners = corners;
this.confidence = confidence;
}
public Marker(int id, double ... corners) {
this(id, corners, 1.0);
}
@Override public Marker clone() {
return new Marker(id, corners.clone(), confidence);
}
public int id;
public double[] corners;
public double confidence;
@Override public int hashCode() {
int hash = 7;
hash = 37 * hash + this.id;
hash = 37 * hash + (this.corners != null ? this.corners.hashCode() : 0);
return hash;
}
@Override public boolean equals(Object o) {
if (o instanceof Marker) {
Marker m = (Marker)o;
return m.id == id && Arrays.equals(m.corners, corners);
}
return false;
}
public double[] getCenter() {
double x = 0, y = 0;
if (true) {
// the centroid is not what we want as it does not remain at
// the same physical point under projective transformations..
// But it has the advantage of averaging noise better, and does
// give better results
for (int i = 0; i < 4; i++) {
x += corners[2*i ];
y += corners[2*i+1];
}
x /= 4;
y /= 4;
} else {
double x1 = corners[0]; double y1 = corners[1];
double x2 = corners[4]; double y2 = corners[5];
double x3 = corners[2]; double y3 = corners[3];
double x4 = corners[6]; double y4 = corners[7];
double u = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3))/
((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
x = x1 + u*(x2-x1);
y = y1 + u*(y2-y1);
}
return new double[] { x, y };
}
public IplImage getImage() {
return getImage(id);
}
private static IplImage imageCache[] = new IplImage[4096];
public static IplImage getImage(int id) {
if (imageCache[id] == null) {
imageCache[id] = IplImage.create(8, 8, IPL_DEPTH_8U, 1);
createImagePatternBCH(id, imageCache[id].getByteBuffer());
}
return imageCache[id];
}
private static final double[] src = { 0, 0, 8, 0, 8, 8, 0, 8 };
public void draw(IplImage image) {
draw(image, CvScalar.BLACK, 1, null);
}
public void draw(IplImage image, CvScalar color, double scale, CvMat prewarp) {
draw(image, color, scale, scale, prewarp);
}
private static ThreadLocal<CvMat>
H3x3 = CvMat.createThreadLocal(3, 3),
srcPts4x1 = CvMat.createThreadLocal(4, 1, CV_64F, 2),
dstPts4x1 = CvMat.createThreadLocal(4, 1, CV_64F, 2);
public void draw(IplImage image, CvScalar color, double scaleX, double scaleY, CvMat prewarp) {
CvMat H = H3x3.get();
JavaCV.getPerspectiveTransform(src, corners, H);
if (prewarp != null) {
cvGEMM(prewarp, H, 1, null, 0, H, 0);
}
IplImage marker = getImage();
ByteBuffer mbuf = marker.getByteBuffer();
CvMat srcPts = srcPts4x1.get();
CvMat dstPts = dstPts4x1.get();
CvPoint tempPts = new CvPoint(4);
int h = marker.height();
int w = marker.width();
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (mbuf.get(y*w + x) == 0) {
srcPts.put((double)x, y, x+1, y, x+1, y+1, x, y+1);
//System.out.println("srcPts" + srcPts);
cvPerspectiveTransform(srcPts, dstPts, H);
//System.out.println("dstPts" + dstPts);
double centerx = 0, centery = 0;
for (int i = 0; i < 4; i++) {
centerx += dstPts.get(i*2 );
centery += dstPts.get(i*2+1);
}
centerx /= 4;
centery /= 4;
for (int i = 0; i < 4; i++) {
double a = dstPts.get(i*2 );
double b = dstPts.get(i*2+1);
double dx = centerx - a;
double dy = centery - b;
dx = dx < 0 ? -1 : 0;
dy = dy < 0 ? -1 : 0;
tempPts.position(i).x((int)Math.round((a*scaleX + dx) * (1<<16)));
tempPts.position(i).y((int)Math.round((b*scaleY + dy) * (1<<16)));
}
cvFillConvexPoly(image, tempPts.position(0), 4, color, 8/*CV_AA*/, 16);
}
}
}
}
public static class ArraySettings extends BaseChildSettings {
int rows = 8, columns = 12;
double sizeX = 200, sizeY = 200, spacingX = 300, spacingY = 300;
boolean checkered = true;
public int getRows() {
return rows;
}
public void setRows(int rows) {
firePropertyChange("rows", this.rows, this.rows = rows);
}
public int getColumns() {
return columns;
}
public void setColumns(int columns) {
firePropertyChange("columns", this.columns, this.columns = columns);
}
public double getSizeX() {
return sizeX;
}
public void setSizeX(double sizeX) {
firePropertyChange("sizeX", this.sizeX, this.sizeX = sizeX);
}
public double getSizeY() {
return sizeY;
}
public void setSizeY(double sizeY) {
firePropertyChange("sizeY", this.sizeY, this.sizeY = sizeY);
}
public double getSpacingX() {
return spacingX;
}
public void setSpacingX(double spacingX) {
firePropertyChange("spacingX", this.spacingX, this.spacingX = spacingX);
}
public double getSpacingY() {
return spacingY;
}
public void setSpacingY(double spacingY) {
firePropertyChange("spacingY", this.spacingY, this.spacingY = spacingY);
}
public boolean isCheckered() {
return checkered;
}
public void setCheckered(boolean checkered) {
firePropertyChange("checkered", this.checkered, this.checkered = checkered);
}
}
public static Marker[][] createArray(ArraySettings settings) {
return createArray(settings, 0, 0);
}
public static Marker[][] createArray(ArraySettings settings, double marginx, double marginy) {
Marker[] markers = new Marker[settings.rows*settings.columns];
int id = 0;
for (int y = 0; y < settings.rows; y++) {
for (int x = 0; x < settings.columns; x++) {
double sx = settings.sizeX/2;
double sy = settings.sizeY/2;
double cx = x*settings.spacingX + sx + marginx;
double cy = y*settings.spacingY + sy + marginy;
markers[id] = new Marker(id, new double[] {
cx-sx, cy-sy, cx+sx, cy-sy, cx+sx, cy+sy, cx-sx, cy+sy }, 1);
id++;
}
}
if (!settings.checkered) {
return new Marker[][] { markers };
} else {
Marker[] markers1 = new Marker[markers.length/2];
Marker[] markers2 = new Marker[markers.length/2];
for (int i = 0; i < markers.length; i++) {
int x = i%settings.columns;
int y = i/settings.columns;
if (x%2==0 ^ y%2==0) {
markers2[i/2] = markers[i];
} else {
markers1[i/2] = markers[i];
}
}
return new Marker[][] { markers2, markers1 };
}
}
public static Marker[][] createArray(int rows, int columns, double sizeX, double sizeY,
double spacingX, double spacingY, boolean checkered, double marginx, double marginy) {
ArraySettings s = new ArraySettings();
s.rows = rows; s.columns = columns;
s.sizeX = sizeX; s.sizeY = sizeY;
s.spacingX = spacingX; s.spacingY = spacingY;
s.checkered = checkered;
return createArray(s, marginx, marginy);
}
public static void applyWarp(Marker[] markers, CvMat warp) {
CvMat pts = srcPts4x1.get();
for (Marker m : markers) {
cvPerspectiveTransform(pts.put(m.corners), pts, warp);
pts.get(m.corners);
}
}
@Override public String toString() {
String s = "[" + id + ": " +
"(" + corners[0] + ", " + corners[1] + ") " +
"(" + corners[2] + ", " + corners[3] + ") " +
"(" + corners[4] + ", " + corners[5] + ") " +
"(" + corners[6] + ", " + corners[7] + ")]";
return s;
}
}