Skip to content
Snippets Groups Projects
Commit 6d5a67c6 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch '145-fix-landmarks-initialization-from-file' into 'master'

Resolve "Fix landmarks initialization from file"

Closes #145

See merge request grp-fidentis/analyst2!155
parents 10d197ac ff5495ae
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,7 @@ package cz.fidentis.analyst.face; ...@@ -2,6 +2,7 @@ package cz.fidentis.analyst.face;
import cz.fidentis.analyst.face.events.HumanFaceListener; import cz.fidentis.analyst.face.events.HumanFaceListener;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import cz.fidentis.analyst.Logger;
import cz.fidentis.analyst.face.events.HumanFaceEvent; import cz.fidentis.analyst.face.events.HumanFaceEvent;
import cz.fidentis.analyst.feature.FeaturePoint; import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.services.FeaturePointImportService; import cz.fidentis.analyst.feature.services.FeaturePointImportService;
...@@ -75,9 +76,12 @@ public class HumanFace implements Serializable { ...@@ -75,9 +76,12 @@ public class HumanFace implements Serializable {
* Use {@link restoreFromFile} to restore the human face from a dump file. * Use {@link restoreFromFile} to restore the human face from a dump file.
* *
* @param file OBJ file * @param file OBJ file
* @param loadLandmarks If {@code true}, then the constructor aims to load
* landmarks along with the mesh. Use {@link #getFeaturePoints()} to check
* whether the landmarks (feature points) has been loaded.
* @throws IOException on I/O failure * @throws IOException on I/O failure
*/ */
public HumanFace(File file) throws IOException { public HumanFace(File file, boolean loadLandmarks) throws IOException {
meshModel = MeshObjLoader.read(new FileInputStream(file)); meshModel = MeshObjLoader.read(new FileInputStream(file));
meshModel.simplifyModel(); meshModel.simplifyModel();
this.id = file.getCanonicalPath(); this.id = file.getCanonicalPath();
...@@ -87,6 +91,29 @@ public class HumanFace implements Serializable { ...@@ -87,6 +91,29 @@ public class HumanFace implements Serializable {
bbox = visitor.getBoundingBox(); bbox = visitor.getBoundingBox();
eventBus = new EventBus(); eventBus = new EventBus();
if (loadLandmarks) {
File landFile = this.findLandmarks();
if (landFile != null) {
try {
loadFeaturePoints(landFile.getAbsoluteFile().getParent(), landFile.getName());
} catch(IOException ex) {
Logger.print(ex.toString());
}
}
}
}
/**
* Reads a 3D human face from the given OBJ file.
* Also loads landmarks (feature points) if appropriate file is found.
* Use {@link restoreFromFile} to restore the human face from a dump file.
*
* @param file OBJ file
* @throws IOException on I/O failure
*/
public HumanFace(File file) throws IOException {
this(file, true);
} }
/** /**
...@@ -243,21 +270,11 @@ public class HumanFace implements Serializable { ...@@ -243,21 +270,11 @@ public class HumanFace implements Serializable {
} }
/** /**
* Returns short name of the face distilled from the file name. May not be unique. * Returns short name of the face without its path in the name. May not be unique.
* @return short name of the face distilled from the file name
*/
public String getName() {
String name = id.substring(0, id.lastIndexOf('.')); // remove extention
name = name.substring(id.lastIndexOf('/')+1, name.length());
return name;
}
/**
* Returns short name of the face without its path in the name.
* @return short name of the face without its path in the name * @return short name of the face without its path in the name
*/ */
public String getShortName() { public String getShortName() {
String name = this.getName(); String name = id.substring(0, id.lastIndexOf('.')); // remove extention
name = name.substring(name.lastIndexOf(File.separatorChar) + 1, name.length()); name = name.substring(name.lastIndexOf(File.separatorChar) + 1, name.length());
return name; return name;
} }
...@@ -308,6 +325,29 @@ public class HumanFace implements Serializable { ...@@ -308,6 +325,29 @@ public class HumanFace implements Serializable {
return ret; return ret;
} }
/**
* Tries to find a file with landmarks definition based on the name of the face's OBJ file.
* @return The file with landmarks or {@code null}
*/
public final File findLandmarks() {
String filename = getId().split(".obj")[0] + "_landmarks.csv";
if ((new File(filename)).exists()) {
return new File(filename);
}
filename = getId().split("_ECA")[0] + "_landmarks.csv";
if ((new File(filename)).exists()) {
return new File(filename);
}
filename = getId().split("_CA")[0] + "_landmarks.csv";
if ((new File(filename)).exists()) {
return new File(filename);
}
return null;
}
/** /**
* Creates serialized dump of the human face. Event buses are not stored. * Creates serialized dump of the human face. Event buses are not stored.
* Therefore, listeners have to re-register again after recovery. * Therefore, listeners have to re-register again after recovery.
......
...@@ -9,7 +9,6 @@ import cz.fidentis.analyst.face.HumanFaceFactory; ...@@ -9,7 +9,6 @@ import cz.fidentis.analyst.face.HumanFaceFactory;
import cz.fidentis.analyst.icp.IcpTransformer; import cz.fidentis.analyst.icp.IcpTransformer;
import cz.fidentis.analyst.icp.NoUndersampling; import cz.fidentis.analyst.icp.NoUndersampling;
import cz.fidentis.analyst.icp.RandomStrategy; import cz.fidentis.analyst.icp.RandomStrategy;
import cz.fidentis.analyst.icp.UndersamplingStrategy;
import cz.fidentis.analyst.mesh.core.MeshModel; import cz.fidentis.analyst.mesh.core.MeshModel;
import cz.fidentis.analyst.scene.DrawableFace; import cz.fidentis.analyst.scene.DrawableFace;
import cz.fidentis.analyst.visitors.kdtree.AvgFaceConstructor; import cz.fidentis.analyst.visitors.kdtree.AvgFaceConstructor;
......
...@@ -15,8 +15,6 @@ import java.awt.event.ActionEvent; ...@@ -15,8 +15,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -555,18 +553,10 @@ public final class ProjectTopComp extends TopComponent { ...@@ -555,18 +553,10 @@ public final class ProjectTopComp extends TopComponent {
for (File file : files) { for (File file : files) {
HumanFace face = null; HumanFace face = null;
try { try {
face = new HumanFace(file); face = new HumanFace(file, true);
out.printDuration("Loaded model " + face.getShortName() +" with " + face.getMeshModel().getNumVertices() + " vertices"); out.printDuration("Loaded model " + face.getShortName() +" with " + face.getMeshModel().getNumVertices() + " vertices");
// simple hack:
Path path = Paths.get(file.getAbsolutePath());
Path folder = path.getParent();
Path filename = path.getFileName();
String filestr = filename.toString();
filestr = filestr.split("_ECA.obj")[0];
filestr = filestr + "_landmarks.csv";
face.loadFeaturePoints(folder.toString(), filestr);
} catch (IOException ex) { } catch (IOException ex) {
ex.printStackTrace(); Logger.print(ex.toString());
} }
String name = face.getShortName(); String name = face.getShortName();
......
...@@ -14,13 +14,14 @@ import java.util.List; ...@@ -14,13 +14,14 @@ import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
* Class used to import feature points from file of csv format * Class used to import feature points from file of CSV format
* *
* @author Jakub Kolman * @author Jakub Kolman
*/ */
public class FeaturePointCsvLoader { public class FeaturePointCsvLoader {
private static final String COLUMN_DELIMETER = ","; private static final String PRIMARY_COLUMN_DELIMETER = ";";
private static final String SECONDARY_COLUMN_DELIMETER = ",";
private static final String CODE_PREFIX_DELIMETER = " "; private static final String CODE_PREFIX_DELIMETER = " ";
/** /**
...@@ -36,28 +37,45 @@ public class FeaturePointCsvLoader { ...@@ -36,28 +37,45 @@ public class FeaturePointCsvLoader {
try (InputStreamReader streamReader try (InputStreamReader streamReader
= new InputStreamReader(app.getFileAsStream(path, fileName), StandardCharsets.UTF_8); = new InputStreamReader(app.getFileAsStream(path, fileName), StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)) { BufferedReader reader = new BufferedReader(streamReader)) {
String headerLine = reader.readLine();
final String delimiter;
if (headerLine.contains(PRIMARY_COLUMN_DELIMETER)) {
delimiter = PRIMARY_COLUMN_DELIMETER;
} else if (headerLine.contains(SECONDARY_COLUMN_DELIMETER)) {
delimiter = SECONDARY_COLUMN_DELIMETER;
} else {
throw new IOException(String.format("Feature point import file '%s' has wrong format - unknown delimiter", fileName));
}
Stream<String> lines = reader.lines(); Stream<String> lines = reader.lines();
List<List<String>> linesList = new ArrayList<>(); List<List<String>> linesList = new ArrayList<>();
lines linesList.add(Arrays.asList(headerLine.split(delimiter, -1)));
.forEach(line -> { lines.forEach(line -> {
linesList.add(Arrays.asList(line.split(COLUMN_DELIMETER))); linesList.add(Arrays.asList(line.split(delimiter, -1)));
}); });
if (linesList.size() != 2 if (linesList.stream().anyMatch(list -> list.size() != linesList.get(0).size())) {
|| linesList.get(0).size() != linesList.get(1).size()) {
throw new IOException(String.format("Feature point import file '%s' has wrong format", fileName)); throw new IOException(String.format("Feature point import file '%s' has wrong format", fileName));
} }
// TODO: In real data sets, there can be multiple FP collections (lines) in a single file,
// e.g., FPs before and after the face warping. Currently, we ignore them.
List<FeaturePoint> points = new ArrayList<>(); List<FeaturePoint> points = new ArrayList<>();
for (int i = 1; i < linesList.get(0).size(); i += 3) { for (int i = 1; i < linesList.get(0).size(); i += 3) {
if (linesList.get(1).get(i).isBlank()
|| linesList.get(1).get(i+1).isBlank()
|| linesList.get(1).get(i+2).isBlank()) { // skip missing points
continue;
}
FeaturePoint point = new FeaturePoint( FeaturePoint point = new FeaturePoint(
Double.parseDouble(linesList.get(1).get(i)), Double.parseDouble(linesList.get(1).get(i)),
Double.parseDouble(linesList.get(1).get(i + 1)), Double.parseDouble(linesList.get(1).get(i + 1)),
Double.parseDouble(linesList.get(1).get(i + 2)), Double.parseDouble(linesList.get(1).get(i + 2)),
FeaturePointTypeProvider.getInstance().getFeaturePointTypeByCode( FeaturePointTypeProvider.getInstance().getFeaturePointTypeByCode(
getCode(linesList.get(0).get(i))) getCode(linesList.get(0).get(i))
)
); );
points.add(point); points.add(point);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment