package photogrammetry.util.model;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import photogrammetry.util.MatrixUtils;
public class Image implements SceneView {
private final File path;
private final Map<Feature, Point2d> imageLocations = new HashMap<>();
/**
* <p>
* Construct a new image using a given path.
* </p>
* <p>
* The image need not yet exist.
* </p>
*
* @param path
*/
public Image(File path) throws IOException {
this.path = path;
loadFeatures();
}
/**
* Get the path where this image is stored.
*
* @return the path of the image
*/
public File getPath() {
return path;
}
/**
* Sets the location of a feature in this image.
*
* @param f the feature whose location to set
* @param location the new location of the feature
*/
public void setLocation(Feature f, HasCoordinates2d location) {
if (imageLocations.containsKey(f)) {
imageLocations.get(f).set(location);
} else {
imageLocations.put(f, new Point2d(location));
}
}
/**
* Removes a feature from this image.
*
* @param f
*/
public void removeFeature(Feature f) {
imageLocations.remove(f);
}
/**
* Determine the feature nearest to a given point (in image coordiantes).
*
* @param pt the point
* @return the nearest feature. If this image has no features, null will be
* returned.
*/
public Feature getNearestFeature(HasCoordinates2d pt) {
double minDistance = Double.MAX_VALUE;
Feature minFeature = null;
for (Feature f : getFeatures()) {
double dist = getLocationInView(f).distanceTo(pt);
if (dist < minDistance) {
minDistance = dist;
minFeature = f;
}
}
return minFeature;
}
@Override
public Set<Feature> getFeatures() {
return imageLocations.keySet();
}
@Override
public HasCoordinates2d getLocationInView(Feature f) {
return imageLocations.get(f);
}
@Override
public Set<Feature> getCommonFeatures(SceneView other) {
Set<Feature> commonFeatures = new LinkedHashSet<>();
Collection<Feature> features2 = other.getFeatures();
Set<Feature> features = getFeatures();
for (Feature f : features2) {
if (features.contains(f)) {
commonFeatures.add(f);
}
}
return commonFeatures;
}
/**
* Looks for a file named path + ".features" and if it exists, tries to load
* features from the file.
*
* @throws IOException if the file is corrupted
*/
public void loadFeatures() throws IOException {
File f = new File(path.getAbsolutePath() + ".features");
if (f.exists()) {
DataInputStream dis = new DataInputStream(new FileInputStream(f));
try {
if (dis.available() < 4) {
return;
}
int count = dis.readInt();
imageLocations.clear();
for (int i = 0; i < count; i++) {
imageLocations.put(new Feature(dis.readLong()), new Point2d(dis.readDouble(),
dis.readDouble()));
}
} finally {
dis.close();
}
} else {
String absolutePath = path.getAbsolutePath();
f = new File(absolutePath.substring(0, absolutePath.length() - 3) + "txt");
if (f.exists()) {
double[] values = MatrixUtils.loadMatrix(f, " ");
for (int i = 0; i < values.length; i += 3) {
imageLocations.put(new Feature((long) values[i]), new Point2d(values[i + 1],
values[i + 2]));
}
}
}
}
/**
* Saves all features to a file named path + ".features"
*
* @throws IOException
*/
public void saveFeatures() throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(path.getAbsolutePath()
+ ".features"));
try {
dos.writeInt(imageLocations.size());
for (Map.Entry<Feature, Point2d> entry : imageLocations.entrySet()) {
dos.writeLong(entry.getKey().id);
dos.writeDouble(entry.getValue().x);
dos.writeDouble(entry.getValue().y);
}
} finally {
dos.close();
}
}
}