在阅读了无数的文档并尝试了解有关 opencv/javacv 的用于提取关键点的示例之后,使用一些 DescriptorExtractor 计算特征以将输入图像与一堆图像进行匹配,以查看输入图像是其中之一还是这些图像之一的一部分,我认为,我们应该在计算完 Mat 对象后存储它们。
我将使用 Emily Webb 的代码作为示例:
StringsmallUrl =“rsz_our-mobile-planet-us-infographic_infographics_lg_unberela.jpg”; String largeUrl =“our-mobile-planet-us-infographic_infographics_lg.jpg”;
IplImage image = cvLoadImage(largeUrl,CV_LOAD_IMAGE_UNCHANGED );
IplImage image2 = cvLoadImage(smallUrl,CV_LOAD_IMAGE_UNCHANGED );
CvMat descriptorsA = new CvMat(null);
CvMat descriptorsB = new CvMat(null);
final FastFeatureDetector ffd = new FastFeatureDetector(40, true);
final KeyPoint keyPoints = new KeyPoint();
final KeyPoint keyPoints2 = new KeyPoint();
ffd.detect(image, keyPoints, null);
ffd.detect(image2, keyPoints2, null);
System.out.println("keyPoints.size() : "+keyPoints.size());
System.out.println("keyPoints2.size() : "+keyPoints2.size());
// BRISK extractor = new BRISK();
//BriefDescriptorExtractor extractor = new BriefDescriptorExtractor();
FREAK extractor = new FREAK();
extractor.compute(image, keyPoints, descriptorsA);
extractor.compute(image2, keyPoints2, descriptorsB);
System.out.println("descriptorsA.size() : "+descriptorsA.size());
System.out.println("descriptorsB.size() : "+descriptorsB.size());
DMatch dmatch = new DMatch();
//FlannBasedMatcher matcher = new FlannBasedMatcher();
//DescriptorMatcher matcher = new DescriptorMatcher();
BFMatcher matcher = new BFMatcher();
matcher.match(descriptorsA, descriptorsB, dmatch, null);
System.out.println(dmatch.capacity());
我的问题是: 如何将描述符A(或描述符B)存储在DB中——在opencv的java实现中? (它们是 extractor.compute(image, keyPoints,descriptorsA); 后获得的
Mat 对象)
我知道 Mat 对象在 java 实现中不是可序列化的对象,但当然,如果你想将图像与一组存档图像进行匹配,你必须提取存档的描述符并将它们存储在某个位置以获取特征使用..
http://answers.opencv.org/question/8873/best-way-to-store-a-mat-object-in-android/ 中找到了一些链接
虽然答案主要针对 Android 设备,并参考了之前有关保存关键点的问题(使用 OpenCV4Android (java API)保存 ORB 特征向量),但下面代码中的答案“从 Mat 对象到 xml 和 xml 到 Mat 对象”似乎有效:
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.util.Locale;
import java.util.Scanner;
public class TaFileStorage {
// static
public static final int READ = 0;
public static final int WRITE = 1;
// varaible
private File file;
private boolean isWrite;
private Document doc;
private Element rootElement;
public TaFileStorage() {
file = null;
isWrite = false;
doc = null;
rootElement = null;
}
// read or write
public void open(String filePath, int flags ) {
try {
if( flags == READ ) {
open(filePath);
}
else {
create(filePath);
}
} catch(Exception e) {
e.printStackTrace();
}
}
// read only
public void open(String filePath) {
try {
file = new File(filePath);
if( file == null || file.isFile() == false ) {
System.err.println("Can not open file: " + filePath );
}
else {
isWrite = false;
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
doc.getDocumentElement().normalize();
}
} catch(Exception e) {
e.printStackTrace();
}
}
// write only
public void create(String filePath) {
try {
file = new File(filePath);
if( file == null ) {
System.err.println("Can not wrtie file: " + filePath );
}
else {
isWrite = true;
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
rootElement = doc.createElement("opencv_storage");
doc.appendChild(rootElement);
}
} catch(Exception e) {
e.printStackTrace();
}
}
public Mat readMat(String tag) {
if( isWrite ) {
System.err.println("Try read from file with write flags");
return null;
}
NodeList nodelist = doc.getElementsByTagName(tag);
Mat readMat = null;
for( int i = 0 ; i<nodelist.getLength() ; i++ ) {
Node node = nodelist.item(i);
if( node.getNodeType() == Node.ELEMENT_NODE ) {
Element element = (Element)node;
String type_id = element.getAttribute("type_id");
if( "opencv-matrix".equals(type_id) == false) {
System.out.println("Fault type_id ");
}
String rowsStr = element.getElementsByTagName("rows").item(0).getTextContent();
String colsStr = element.getElementsByTagName("cols").item(0).getTextContent();
String dtStr = element.getElementsByTagName("dt").item(0).getTextContent();
String dataStr = element.getElementsByTagName("data").item(0).getTextContent();
int rows = Integer.parseInt(rowsStr);
int cols = Integer.parseInt(colsStr);
int type = CvType.CV_8U;
Scanner s = new Scanner(dataStr);
s.useLocale(Locale.US);
if( "f".equals(dtStr) ) {
type = CvType.CV_32F;
readMat = new Mat( rows, cols, type );
float fs[] = new float[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
if( s.hasNextFloat() ) {
fs[0] = s.nextFloat();
}
else {
fs[0] = 0;
System.err.println("Unmatched number of float value at rows="+r + " cols="+c);
}
readMat.put(r, c, fs);
}
}
}
else if( "i".equals(dtStr) ) {
type = CvType.CV_32S;
readMat = new Mat( rows, cols, type );
int is[] = new int[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
if( s.hasNextInt() ) {
is[0] = s.nextInt();
}
else {
is[0] = 0;
System.err.println("Unmatched number of int value at rows="+r + " cols="+c);
}
readMat.put(r, c, is);
}
}
}
else if( "s".equals(dtStr) ) {
type = CvType.CV_16S;
readMat = new Mat( rows, cols, type );
short ss[] = new short[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
if( s.hasNextShort() ) {
ss[0] = s.nextShort();
}
else {
ss[0] = 0;
System.err.println("Unmatched number of int value at rows="+r + " cols="+c);
}
readMat.put(r, c, ss);
}
}
}
else if( "b".equals(dtStr) ) {
readMat = new Mat( rows, cols, type );
byte bs[] = new byte[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
if( s.hasNextByte() ) {
bs[0] = s.nextByte();
}
else {
bs[0] = 0;
System.err.println("Unmatched number of byte value at rows="+r + " cols="+c);
}
readMat.put(r, c, bs);
}
}
}
}
}
return readMat;
}
public void writeMat(String tag, Mat mat) {
try {
if( isWrite == false) {
System.err.println("Try write to file with no write flags");
return;
}
Element matrix = doc.createElement(tag);
matrix.setAttribute("type_id", "opencv-matrix");
rootElement.appendChild(matrix);
Element rows = doc.createElement("rows");
rows.appendChild( doc.createTextNode( String.valueOf(mat.rows()) ));
Element cols = doc.createElement("cols");
cols.appendChild( doc.createTextNode( String.valueOf(mat.cols()) ));
Element dt = doc.createElement("dt");
String dtStr;
int type = mat.type();
if(type == CvType.CV_32F ) { // type == CvType.CV_32FC1
dtStr = "f";
}
else if( type == CvType.CV_32S ) { // type == CvType.CV_32SC1
dtStr = "i";
}
else if( type == CvType.CV_16S ) { // type == CvType.CV_16SC1
dtStr = "s";
}
else if( type == CvType.CV_8U ){ // type == CvType.CV_8UC1
dtStr = "b";
}
else {
dtStr = "unknown";
}
dt.appendChild( doc.createTextNode( dtStr ));
Element data = doc.createElement("data");
String dataStr = dataStringBuilder( mat );
data.appendChild( doc.createTextNode( dataStr ));
// append all to matrix
matrix.appendChild( rows );
matrix.appendChild( cols );
matrix.appendChild( dt );
matrix.appendChild( data );
} catch(Exception e) {
e.printStackTrace();
}
}
private String dataStringBuilder(Mat mat) {
StringBuilder sb = new StringBuilder();
int rows = mat.rows();
int cols = mat.cols();
int type = mat.type();
if( type == CvType.CV_32F ) {
float fs[] = new float[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
mat.get(r, c, fs);
sb.append( String.valueOf(fs[0]));
sb.append( ' ' );
}
sb.append( '\n' );
}
}
else if( type == CvType.CV_32S ) {
int is[] = new int[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
mat.get(r, c, is);
sb.append( String.valueOf(is[0]));
sb.append( ' ' );
}
sb.append( '\n' );
}
}
else if( type == CvType.CV_16S ) {
short ss[] = new short[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
mat.get(r, c, ss);
sb.append( String.valueOf(ss[0]));
sb.append( ' ' );
}
sb.append( '\n' );
}
}
else if( type == CvType.CV_8U ) {
byte bs[] = new byte[1];
for( int r=0 ; r<rows ; r++ ) {
for( int c=0 ; c<cols ; c++ ) {
mat.get(r, c, bs);
sb.append( String.valueOf(bs[0]));
sb.append( ' ' );
}
sb.append( '\n' );
}
}
else {
sb.append("unknown type\n");
}
return sb.toString();
}
public void release() {
try {
if( isWrite == false) {
System.err.println("Try release of file with no write flags");
return;
}
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(file);
// write to xml file
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
// do it
transformer.transform(source, result);
} catch(Exception e) {
e.printStackTrace();
}
}
}
由于 Thorben 提出的代码在我的情况下速度很慢,所以我使用序列化想出了以下代码。
public final void saveMat(String path, Mat mat) {
File file = new File(path).getAbsoluteFile();
file.getParentFile().mkdirs();
try {
int cols = mat.cols();
float[] data = new float[(int) mat.total() * mat.channels()];
mat.get(0, 0, data);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path))) {
oos.writeObject(cols);
oos.writeObject(data);
oos.close();
}
} catch (IOException | ClassCastException ex) {
System.err.println("ERROR: Could not save mat to file: " + path);
Logger.getLogger(this.class.getName()).log(Level.SEVERE, null, ex);
}
}
public final Mat loadMat(String path) {
try {
int cols;
float[] data;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {
cols = (int) ois.readObject();
data = (float[]) ois.readObject();
}
Mat mat = new Mat(data.length / cols, cols, CvType.CV_32F);
mat.put(0, 0, data);
return mat;
} catch (IOException | ClassNotFoundException | ClassCastException ex) {
System.err.println("ERROR: Could not load mat from file: " + path);
Logger.getLogger(this.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
对于描述符,OpenCV 使用浮点数的垫子,在其他情况下,您必须根据此处:
找到的列表相应地修改代码CV_8U and CV_8S -> byte[]
CV_16U and CV_16S -> short[]
CV_32S -> int[]
CV_32F -> float[]
CV_64F-> double[]
搜索完所有答案后,我编辑了一些代码,看起来可行。我用它来将 Sift 描述符存储到 HBase 中。
public static byte[] serializeMat(Mat mat) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
float[] data = new float[(int) mat.total() * mat.channels()];
mat.get(0, 0, data);
ObjectOutput out = new ObjectOutputStream(bos);
out.writeObject(data);
out.close();
// Get the bytes of the serialized object
byte[] buf = bos.toByteArray();
return buf;
} catch (IOException ioe) {
ioe.printStackTrace();
return null;
}
}
我还面临 Java org.opencv 不具备原生 OpenCV 的文件存储功能的问题。我改编了另一篇文章(在下面代码中的javadoc中)
package net.sf.jaer.util;
import com.esotericsoftware.yamlbeans.YamlException;
import com.esotericsoftware.yamlbeans.YamlReader;
import com.esotericsoftware.yamlbeans.YamlWriter;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
/**
* Adapted from
* https://stackoverflow.com/questions/34297676/how-to-load-opencv-matrices-saved-with-filestorage-in-java
*
* using
* https://github.com/EsotericSoftware/yamlbeans
*
* @author https://stackoverflow.com/users/3303546/cecilia
*/
public class YamlMatFileStorage {
private static final Logger log = Logger.getLogger("YamlMatLoader");
// Mat cannot be used directly because it is a native object and yamlbeans only deals with Java objects
protected static class MatStorage {
public int rows;
public int cols;
public String dt;
public List<String> data;
// The empty constructor is required by YamlReader
public MatStorage() {
}
public double[] getData() {
double[] dataOut = new double[data.size()];
for (int i = 0; i < dataOut.length; i++) {
dataOut[i] = Double.parseDouble(data.get(i));
}
return dataOut;
}
public void putData(Mat m) {
rows = m.rows();
cols = m.cols();
data = new ArrayList();
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
data.add(Double.toString(m.get(r, c)[0]));
}
}
}
}
/**
* Reads a Mat from a YAML file
*
* @param path path to YAML file
* @return the Mat
*/
public Mat readMatYml(String path) throws FileNotFoundException, YamlException, IOException {
Mat m=null;
try (YamlReader reader = new YamlReader(new FileReader(path))) {
MatStorage matStorage = (MatStorage) reader.read();
// Create a new Mat to hold the extracted data
m = new Mat(matStorage.rows, matStorage.cols, CvType.CV_32FC1);
m.put(0, 0, matStorage.getData());
log.info(String.format("From file %s loaded Mat %s", path, m));
}
return m;
}
// Loading function
/**
* Reads a Mat from a YAML file
*
* @param path path to YAML file
* @param m the Mat to write
* @throws java.io.IOException
* @throws com.esotericsoftware.yamlbeans.YamlException
*/
public void writeMatYml(String path, Mat m) throws IOException, YamlException {
try (YamlWriter writer = new YamlWriter(new FileWriter(path))) {
// write the Mat
MatStorage ms = new MatStorage();
ms.putData(m);
writer.write(ms);
}
log.info(String.format("Wrote Mat %s to file %s", m, path));
}
}
存储的文件如下所示:
!net.sf.jaer.util.YamlMatFileStorage$MatStorage
cols: 3
data:
- 1735.0830676085973
- 0.0
- 173.80815300223972
- 0.0
- 1720.885058317215
- 147.99521279976508
- 0.0
- 0.0
- 1.0
rows: 3