通过调研,相似图片判断领域使用较多的算法有“感知哈希”算法(谷歌、360),“直方图”算法(阿里)和“特征提取”算法(百度),下面对这些算法进行了测试与对比,实现上可能还有进一步改进的地方。
测试用的图片附在文末的 附1。
之前未能成功安装的, postgresql 的 imgsmlr 插件,将它的一个简介附在文末的 附2。
目前只完成了“感知哈希”算法和“直方图”算法的比较,“特征提取”算法的量化对比还未完成。
对比速览
特征 | 描述 | 值形式 | 相似度计算 | 计算速度 | 匹配速度 | 准确度 |
---|---|---|---|---|---|---|
ahash | RGB均值哈希 | simhash | 海明距离 | 快 | 快 | 中 |
dHash | RGB差异哈希 | simhash | 海明距离 | 快 | 快 | 中 |
pHash | RGB感知哈希 | simhash | 海明距离 | 中 | 快 | 高 |
直方图 | 颜色分布向量 | 整数数组 | 余弦相似度、欧氏距离等 | 中 | 中 | 中 |
SIFT | 局部特征 | 二维数组,宽64,长度不定 | 匹配点个数 | |||
SURF | SIFT改进 | 同SIFT | 同SIFT |
一、感知哈希算法
感知哈希算法是Neal Krawetz提出的关键技术,其不是以严格的方式计算哈希值,而只是通过判断图像相邻像素的差异,为图像生成一个指纹(字符串格式),对于待比较的两张图像,通过对比其指纹,计算两张图像之间指纹的汉明距离,即可判断出其图像相似度,进而检索相似图像。
对每张图片生成一个simhash,再比较海明距离。海明距离不超过5,表示很相似;海明距离大于10,表明完全不同。
常用的有三种:平均哈希(aHash),感知哈希(pHash),差异值哈希(dHash)
1、平均哈希(aHash)
1)算法步骤
- 缩小尺寸:8*8像素
- 简化色彩:图片灰度化
- 计算平均值:计算所有64个像素的灰度平均值
- 像素二值化:大于或等于平均值,记为1;小于平均值,记为0。
- 生成hash值:将上一步的比较结果,组合成64位的01字符串。
2)测试数据
Origin | Filter | Copyright | LowQuality | Thumbnail | SemiSimilar | Different |
---|---|---|---|---|---|---|
海明距离 | 5 | 5 | 1 | 6 | 34 | 44 |
相似度(%) | 92.19 | 92.19 | 98.44 | 90.63 | 46.88 | 31.25 |
1次读取+1次计算匹配(ms) | 38 | 29 | 27 | 40 | 58 | 25 |
读取后,100次计算匹配(ms) | 4-9 | 5-15 | 7-10 | 5-11 | 4-8 | 10-18 |
3)小结
简单快速,有一定准确率。
2、差异值哈希(dHash)
1)算法步骤
- 缩小尺寸:9*8像素
- 简化色彩:图片灰度化
- 计算差异值:每行9个像素之间产生了8个不同的差异,一共8行。前一个像素大于后一个像素则为1,否则为0。这样会生成8*8的差值
- 生成哈希值(同平均哈希)
2)测试数据
Origin | Filter | Copyright | LowQuality | Thumbnail | SemiSimilar | Different |
---|---|---|---|---|---|---|
海明距离 | 5 | 3 | 7 | 11 | 29 | 33 |
相似度(%) | 92.19 | 95.31 | 89.06 | 82.81 | 54.69 | 48.44 |
1次读取+1次计算匹配(ms) | 38 | 25 | 35 | 25 | 30 | 39 |
读取后,100次计算匹配(ms) | 5-12 | 8-14 | 5-9 | 8-13 | 5-9 | 7-13 |
3)小结
和 aHash 效率类似。
3、感知哈希(pHash)
phash.org 有官方实现
1)算法步骤
- 缩小尺寸:32*32像素
- 简化色彩:图片灰度化
- 计算DCT:对图片进行离散余弦变换,得到32*32的DCT系数矩阵。
- 缩小DCT:取矩阵左上角8*8大小(取图片低频部分)
- 计算平均值,并根据平均值二值化(同平均哈希)
- 生成hash值(同平均哈希)
2)测试数据
Origin | Filter | Copyright | LowQuality | Thumbnail | SemiSimilar | Different |
---|---|---|---|---|---|---|
海明距离 | 0 | 1 | 0 | 0 | 14 | 18 |
相似度(%) | 100 | 98.44 | 100 | 100 | 78.13 | 71.88 |
1次读取+1次计算匹配(ms) | 50 | 44 | 38 | 32 | 36 | 48 |
读取后,100次计算匹配(ms) | 448-528 | 439-558 | 462-599 | 244-298 | 641-804 | 463-520 |
3)小结
相比 aHash 和 dHash, pHash的速度要慢很多,但效果要好很多,改变大小、分辨率、加滤镜,都不会改变哈希值。
二、直方图
1)算法原理
每张图片都可以生成颜色分布的直方图。如果两张图片的直方图很接近,就可以认为它们很相似。
红绿蓝三原色,每种原色可取256个值,计算量太大。要采用简化方法,将255分成四个区统计,总共可以构成64种组合(4的3次方)。
统计每一种组合包含的像素数量,颜色分布组成一个64维向量,这个向量就是这张图片的特征值。
于是,寻找相似图片就变成了找出与其最相似的向量。这可以用皮尔逊相关系数或者余弦相似度算出。
2)测试数据
0.9以上算相似
Origin | Filter | Copyright | LowQuality | Thumbnail | SemiSimilar | Different |
---|---|---|---|---|---|---|
相似度(%) | 89.40 | 99.92 | 85.93 | 99.54 | 79.56 | 63.34 |
1次读取+1次计算匹配(ms) | 41 | 28 | 26 | 38 | 58 | 40 |
读取后,100次计算匹配(ms) | 684-857 | 631-770 | 715-919 | 390-433 | 1103-1679 | 632-762 |
3)小结
由于直方图自身的局限性:仅反映图像像素各灰度值的数量,不能反映图像纹理结构,很明显该方法存在很多误判。
变色后的 Filter 与原图相似度偏低,仅仅颜色相近的 SemiSimilar 和 Different 相似度结果偏高。
效果上不及 PHash。
三、特征提取
1、SIFT
SIFT(Scale-Invariant Feature Transform,尺度不变特征转换)自1999年由David Lowe提出以后被广泛的应用于CV的各种领域:图像识别,图像检索,3D重建等等。
1)算法步骤
Lowe将算法分为了四个分解步骤:
- 尺度空间的极值检测 搜索所有尺度空间上的图像,通过高斯微分函数来识别潜在的对尺度和选择不变的兴趣点。
- 特征点定位 在每个候选的位置上,通过一个拟合精细模型来确定位置尺度,关键点的选取依据他们的稳定程度。
- 特征方向赋值 基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向,后续的所有操作都是对于关键点的方向、尺度和位置进行变换,从而提供这些特征的不变性。
- 特征点描述 在每个特征点周围的邻域内,在选定的尺度上测量图像的局部梯度,这些梯度被变换成一种表示,这种表示允许比较大的局部形状的变形和光照变换。
2)SIFT优点
- 对于不同的图像,有很强的稳定性
- 独特性好,信息量丰富
- 扩展能力强,可以方便的将更强能力的某一单一处理算法引入
3)SIFT缺点
- 计算速度慢
- 原始图像较小的情况下,特征点下降严重
- 对边缘光滑的目标无法准确提取特征点,尤其是圆,完全无能为力
2、SURF
SURF(Speeded Up Robust Features,加速稳健特征),是对 SIFT 的改进,提升了算法的执行效率。SURF在大致流程上与SIFT相似,在尺度空间和关键点描述方面做了优化。
SIFT 和 SURF 算法都受专利保护,要收费。
四、参考资料
- 图片搜索方法及搜索系统 (百度,2010,局部特征,SIFT)
- 相似图像的识别方法和装置(阿里,2011,直方图)
- 一种识别重复图片的方法、图片搜索去重方法及其装置 (360,2014,PHash,分段比较)
- 相似图片搜索的原理
- 相似图片搜索的原理(二)
- 三种基于感知哈希算法的相似图像检索技术
- java版本phash算法优化
- 图像分析之直方图分析
- SIFT特征详解
- 图像特征点检测与匹配评价——量化
- SIFT特征提取及匹配
五、附录
附 1. 测试图片
1)Origin.jpg,429 KB
2)Filter.jpg,165 KB
3)Copyright.jpg,459 KB
4)LowQuality.jpg,59.7 KB
5)Thumbnail.jpg,28.2 KB
6)SemiSimilar.jpg,747 KB
7)Different.jpg,63.2 KB
附 2. postgresql 的 imgsmlr 插件原理
PostgreSQL的图像搜索插件 imgsmlr 使用了 Haar wavelet 技术对图像进行变换后存储。
ImgSmlr 提供了两种数据类型: pattern 和 signature.
Datatype | Storage length | Description |
---|---|---|
pattern | 16388 bytes | 存储图片的哈尔小波变换结果 |
signature | 64 bytes | pattern 的简短表示,用来支持使用 GiST 索引的快速搜索 |
1)计算步骤
- 解压缩图像。
- 使图像变为黑白色。
- 将图像大小调整为64x64像素。
- 将Haar小波变换应用于图像。
2)对比步骤
对比图片的 pattern,返回64位的 simhash,通过 signature 索引
3)查询示例
SELECT
id,
smlr
FROM
(
SELECT
id,
pattern <-> (SELECT pattern FROM pat WHERE id = :id) AS smlr
FROM pat
WHERE id <> :id
ORDER BY
signature <-> (SELECT signature FROM pat WHERE id = :id)
LIMIT 100
) x
ORDER BY x.smlr ASC
LIMIT 10
附 3. Java 调用 OpenCV 包
1)Maven 引包
<!-- https://mvnrepository.com/artifact/org.openpnp/opencv -->
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>2.4.13-0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
2)感知哈希算法
public class PHashUtils {
/**
* 使用之前需要先执行加载语句
*/
static {
nu.pattern.OpenCV.loadShared();
}
/**
* 测试 main
*/
public static void main(String[] args) {
String strUrl1 = "http://testUrl1";
String strUrl2 = "http://testUrl2";
int distance = getDistance(strUrl1, strUrl2);
}
/**
* 计算海明距离
*/
public static int getDistance(String strUrl1, String strUrl2) {
if (null != strUrl1 && null != strUrl2) {
try {
Mat source1 = getMatByUrl(strUrl1);
long hash1 = getPHash(source1);
Mat source2 = getMatByUrl(strUrl2);
long hash2 = getPHash(source2);
return Long.bitCount(hash1 ^ hash2);
} catch (Exception e) {
e.printStackTrace();
}
}
return 64;
}
/**
* 根据 url 计算 Mat,gif 取第一帧
*/
private static Mat getMatByUrl(String strUrl) {
byte[] bytes = null;
try {
URL url = new URL(strUrl);
DataInputStream input = new DataInputStream(url.openStream());
ByteArrayOutputStream output = new ByteArrayOutputStream();
if (strUrl.toLowerCase().endsWith(".gif")) {
MemoryCacheImageInputStream in = new MemoryCacheImageInputStream(input);
GIFImageReader gifReader = new GIFImageReader(new GIFImageReaderSpi());
gifReader.setInput(in);
int num = gifReader.getNumImages(true);
if (num > 0) {
BufferedImage read = gifReader.read(0);
ImageIO.write(read, "jpg", output);
}
} else {
IOUtils.copy(input, output);
}
bytes = output.toByteArray();
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
if (null == bytes) {
return null;
}
return getMatByBytes(bytes);
}
/**
* 根据二进制数据计算 Mat
*/
private static Mat getMatByBytes(byte[] bytes) {
Mat encoded = new Mat(1, bytes.length, CvType.CV_8U);
encoded.put(0, 0, bytes);
//从内存中读,返回Mat形式
Mat decoded = Highgui.imdecode(encoded, -1);
encoded.release();
return decoded;
}
/**
* 根据本地文件地址计算 Mat
*/
private static Mat getMatByPath(String path) {
return Highgui.imread(path);
}
/**
* 计算 phash
*/
private static long getPHash(Mat image) {
Mat grayImage = new Mat();
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);
Size size = new Size(32, 32);
double fx = size.width / grayImage.cols();
double fy = size.height / grayImage.rows();
Mat newImage = new Mat();
Imgproc.resize(grayImage, newImage, size, fx, fy, Imgproc.INTER_AREA);
newImage.convertTo(newImage, CvType.CV_32FC1);
Mat dst = new Mat(newImage.size(), newImage.type());
Core.dct(newImage, dst);
double mean = 0;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
mean = mean + dst.get(i, j)[0];
}
}
mean = mean / 64;
long l = 0;
long x = 1;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (dst.get(i, j)[0] >= mean) {
l |= x << (63 - i * 8 - j);
}
}
}
return l;
}
/**
* 计算 AHash
*/
private static long getAHash(Mat source) {
Mat resizeMat = new Mat();
Imgproc.resize(source, resizeMat, new Size(8, 8));
Mat BGRMat = new Mat();
Imgproc.cvtColor(resizeMat, BGRMat, Imgproc.COLOR_BGR2GRAY);
double sum = 0;
for (int row = 0; row < BGRMat.height(); row++) {
for (int cols = 0; cols < BGRMat.width(); cols++) {
sum += (int) BGRMat.get(row, cols)[0];
}
}
double avg = (float) sum / (BGRMat.height() * BGRMat.width());
long l = 0;
long x = 1;
for (int row = 0; row < BGRMat.height(); row++) {
for (int cols = 0; cols < BGRMat.width(); cols++) {
if (BGRMat.get(row, cols)[0] >= avg) {
l |= x << (63 - row * 8 - cols);
}
}
}
return l;
}
/**
* 计算 DHash
*/
private static long getDHash(Mat source) {
Mat resizeMat = new Mat();
Imgproc.resize(source, resizeMat, new Size(9, 8));
Mat grayMat = new Mat();
Imgproc.cvtColor(resizeMat, grayMat, Imgproc.COLOR_BGR2GRAY);
long l = 0;
long x = 1;
for (int row = 0; row < grayMat.height(); row++) {
for (int cols = 0; cols < grayMat.width() - 1; cols++) {
if((grayMat.get(row, cols)[0]) > grayMat.get(row, cols + 1)[0]){
l |= x << (63 - row * 8 - cols);
}
}
}
return l;
}
}
3)直方图
/**
* 计算 Hist
*/
private static long getHist(Mat source1, Mat source2) {
Mat histImg1 = calcHist(source1);
Mat histImg2 = calcHist(source2);
double distance = Imgproc.compareHist(histImg1, histImg2, Imgproc.CV_COMP_CORREL);
}
/**
* 计算 Hist
*/
private static Mat calcHist(Mat src) {
//1 图片转HSV
Mat hsv = new Mat();
Imgproc.cvtColor(src,hsv,Imgproc.COLOR_BGR2HSV);
//2 计算直方图
List<Mat> matList = new LinkedList<>();
matList.add(hsv);
Mat histogram = new Mat();
Imgproc.calcHist(matList, new MatOfInt(0), new Mat(), histogram, new MatOfInt(255), new MatOfFloat(0, 256));
//3 归一化
Core.normalize(histogram, histogram, 1, histogram.rows(), Core.NORM_MINMAX, -1, new Mat());
//5 绘制几何直方图
// Mat histImage = Mat.zeros(100, (int) histSize.get(0, 0)[0], CvType.CV_8UC1);
// for (int i = 0; i < (int) histSize.get(0, 0)[0]; i++) {
// Imgproc.line(histImage, new org.opencv.core.Point(i, histImage.rows()), new org.opencv.core.Point(i, histImage.rows() - Math.round(histogram.get(i, 0)[0])), new Scalar(255, 255, 255), 1, 8, 0);
// }
// HighGui.imshow("直方图计算"+System.currentTimeMillis(), histImage);
// HighGui.waitKey(0);
return histogram;
}
3)特征提取
SIFT
public class sift_opencv {
public static void main(String[] args) throws IOException {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat blurredImage = new Mat();
Mat hsvImage = new Mat();
Mat mask = new Mat();
Mat morphOutput = new Mat();
Mat img;
Mat maskedImage;
//BufferedImage img;
//img=ImageIO.read(new File("...\\Penguins.png"));
//File f= new File("...\\Penguins.png");
img= Highgui.imread("...\\burj.png");
System.out.println(img);
// remove some noise
Highgui.imwrite("out.png", img);
Imgproc.blur(img, blurredImage, new Size(7, 7));
// convert the frame to HSV
Imgproc.cvtColor(blurredImage, hsvImage, Imgproc.COLOR_BGR2HSV);
//convert to gray
//Mat mat = new Mat(img.width(), img.height(), CvType.CV_8U, new Scalar(4));
Mat gray = new Mat(img.width(), img.height(), CvType.CV_8U, new Scalar(4));
Imgproc.cvtColor(img, gray, Imgproc.COLOR_BGR2GRAY);
FeatureDetector fd = FeatureDetector.create(FeatureDetector.FAST);
MatOfKeyPoint regions = new MatOfKeyPoint();
fd.detect(gray, regions);
Mat output=new Mat();
//int r=regions.rows();
//System.out.println("REGIONS ARE: " + regions);
Features2d.drawKeypoints(gray, regions,output );
Highgui.imwrite("out.png", output);
}
}
SURF
public class SURFDetector {
public static void main(String[] args) {
nu.pattern.OpenCV.loadShared();
String source = "...\\1.jpeg";
String target = "...\\2.jpeg";
Mat objectImage = Highgui.imread(source, Highgui.CV_LOAD_IMAGE_COLOR);
Mat sceneImage = Highgui.imread(target, Highgui.CV_LOAD_IMAGE_COLOR);
MatOfKeyPoint objectKeyPoints = new MatOfKeyPoint();
FeatureDetector featureDetector = FeatureDetector.create(FeatureDetector.SURF);
System.out.println("Detecting key points...");
featureDetector.detect(objectImage, objectKeyPoints);
KeyPoint[] keypoints = objectKeyPoints.toArray();
System.out.println(keypoints);
MatOfKeyPoint objectDescriptors = new MatOfKeyPoint();
DescriptorExtractor descriptorExtractor = DescriptorExtractor.create(DescriptorExtractor.SURF);
System.out.println("Computing descriptors...");
descriptorExtractor.compute(objectImage, objectKeyPoints, objectDescriptors);
// Create the matrix for output image.
Mat outputImage = new Mat(objectImage.rows(), objectImage.cols(), Highgui.CV_LOAD_IMAGE_COLOR);
Scalar newKeypointColor = new Scalar(255, 0, 0);
System.out.println("Drawing key points on object image...");
Features2d.drawKeypoints(objectImage, objectKeyPoints, outputImage, newKeypointColor, 0);
// Match object image with the scene image
MatOfKeyPoint sceneKeyPoints = new MatOfKeyPoint();
MatOfKeyPoint sceneDescriptors = new MatOfKeyPoint();
System.out.println("Detecting key points in background image...");
featureDetector.detect(sceneImage, sceneKeyPoints);
System.out.println("Computing descriptors in background image...");
descriptorExtractor.compute(sceneImage, sceneKeyPoints, sceneDescriptors);
Mat matchoutput = new Mat(sceneImage.rows() * 2, sceneImage.cols() * 2, Highgui.CV_LOAD_IMAGE_COLOR);
Scalar matchestColor = new Scalar(0, 255, 0);
List<MatOfDMatch> matches = new LinkedList<MatOfDMatch>();
DescriptorMatcher descriptorMatcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
System.out.println("Matching object and scene images...");
descriptorMatcher.knnMatch(objectDescriptors, sceneDescriptors, matches, 2);
System.out.println("Calculating good match list...");
LinkedList<DMatch> goodMatchesList = new LinkedList<DMatch>();
float nndrRatio = 0.7f;
for (int i = 0; i < matches.size(); i++) {
MatOfDMatch matofDMatch = matches.get(i);
DMatch[] dmatcharray = matofDMatch.toArray();
DMatch m1 = dmatcharray[0];
DMatch m2 = dmatcharray[1];
if (m1.distance <= m2.distance * nndrRatio) {
goodMatchesList.addLast(m1);
}
}
System.out.println("goodMatches: "+goodMatchesList.size()+" / " +matches.size());
List<MatOfDMatch> matches2 = new LinkedList<MatOfDMatch>();
String json = keypointsToJson(objectDescriptors);
MatOfKeyPoint matOfKeyPoint = keypointsFromJson(json);
descriptorMatcher.knnMatch(matOfKeyPoint, sceneDescriptors, matches2, 2);
LinkedList<DMatch> goodMatchesList2 = new LinkedList<DMatch>();
for (int i = 0; i < matches2.size(); i++) {
MatOfDMatch matofDMatch = matches2.get(i);
DMatch[] dmatcharray = matofDMatch.toArray();
DMatch m1 = dmatcharray[0];
DMatch m2 = dmatcharray[1];
if (m1.distance <= m2.distance * nndrRatio) {
goodMatchesList2.addLast(m1);
}
}
System.out.println("goodMatches2: "+goodMatchesList2.size()+" / " +matches2.size());
if (goodMatchesList.size() >= 7) {
System.out.println("Object Found!!!");
List<KeyPoint> objKeypointlist = objectKeyPoints.toList();
List<KeyPoint> scnKeypointlist = sceneKeyPoints.toList();
LinkedList<Point> objectPoints = new LinkedList<>();
LinkedList<Point> scenePoints = new LinkedList<>();
for (int i = 0; i < goodMatchesList.size(); i++) {
objectPoints.addLast(objKeypointlist.get(goodMatchesList.get(i).queryIdx).pt);
scenePoints.addLast(scnKeypointlist.get(goodMatchesList.get(i).trainIdx).pt);
}
MatOfPoint2f objMatOfPoint2f = new MatOfPoint2f();
objMatOfPoint2f.fromList(objectPoints);
MatOfPoint2f scnMatOfPoint2f = new MatOfPoint2f();
scnMatOfPoint2f.fromList(scenePoints);
Mat homography = Calib3d.findHomography(objMatOfPoint2f, scnMatOfPoint2f, Calib3d.RANSAC, 3);
Mat obj_corners = new Mat(4, 1, CvType.CV_32FC2);
Mat scene_corners = new Mat(4, 1, CvType.CV_32FC2);
obj_corners.put(0, 0, new double[]{0, 0});
obj_corners.put(1, 0, new double[]{objectImage.cols(), 0});
obj_corners.put(2, 0, new double[]{objectImage.cols(), objectImage.rows()});
obj_corners.put(3, 0, new double[]{0, objectImage.rows()});
System.out.println("Transforming object corners to scene corners...");
Core.perspectiveTransform(obj_corners, scene_corners, homography);
Mat img = Highgui.imread(target, Highgui.CV_LOAD_IMAGE_COLOR);
Core.line(img, new Point(scene_corners.get(0, 0)), new Point(scene_corners.get(1, 0)), new Scalar(0, 255, 0), 4);
Core.line(img, new Point(scene_corners.get(1, 0)), new Point(scene_corners.get(2, 0)), new Scalar(0, 255, 0), 4);
Core.line(img, new Point(scene_corners.get(2, 0)), new Point(scene_corners.get(3, 0)), new Scalar(0, 255, 0), 4);
Core.line(img, new Point(scene_corners.get(3, 0)), new Point(scene_corners.get(0, 0)), new Scalar(0, 255, 0), 4);
System.out.println("Drawing matches image...");
MatOfDMatch goodMatches = new MatOfDMatch();
goodMatches.fromList(goodMatchesList);
Features2d.drawMatches(objectImage, objectKeyPoints, sceneImage, sceneKeyPoints, goodMatches, matchoutput, matchestColor, newKeypointColor, new MatOfByte(), 2);
Highgui.imwrite("...\\output//outputImage.jpg", outputImage);
Highgui.imwrite("...\\output//matchoutput.jpg", matchoutput);
Highgui.imwrite("...\\output//img.jpg", img);
} else {
System.out.println("Object Not Found");
}
System.out.println("Ended....");
}
public static MatOfKeyPoint serialize( MatOfKeyPoint mat) {
byte[] bytes = serializeMat(mat);
int type = mat.type();
int rows = mat.rows();
int cols = mat.cols();
MatOfKeyPoint matOfKeyPoint = new MatOfKeyPoint();
matOfKeyPoint.create(rows, cols, type);
matOfKeyPoint.put(0,0,bytes);
return matOfKeyPoint;
}
public static byte[] serializeMat( MatOfKeyPoint 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;
}
}
public static String keypointsToJson(MatOfKeyPoint mat){
if(mat!=null && !mat.empty()){
Gson gson = new Gson();
JsonArray jsonArr = new JsonArray();
KeyPoint[] array = mat.toArray();
for(int i=0; i<array.length; i++){
KeyPoint kp = array[i];
JsonObject obj = new JsonObject();
obj.addProperty("c", kp.class_id);
obj.addProperty("x", kp.pt.x);
obj.addProperty("y", kp.pt.y);
obj.addProperty("s", kp.size);
obj.addProperty("a", kp.angle);
obj.addProperty("o", kp.octave);
obj.addProperty("r", kp.response);
jsonArr.add(obj);
}
String json = gson.toJson(jsonArr);
return json;
}
return "{}";
}
public static MatOfKeyPoint keypointsFromJson(String json){
MatOfKeyPoint result = new MatOfKeyPoint();
JsonParser parser = new JsonParser();
JsonArray jsonArr = parser.parse(json).getAsJsonArray();
int size = jsonArr.size();
KeyPoint[] kpArray = new KeyPoint[size];
for(int i=0; i<size; i++){
KeyPoint kp = new KeyPoint();
JsonObject obj = (JsonObject) jsonArr.get(i);
Point point = new Point(
obj.get("x").getAsDouble(),
obj.get("y").getAsDouble()
);
kp.pt = point;
kp.class_id = obj.get("c").getAsInt();
kp.size = obj.get("s").getAsFloat();
kp.angle = obj.get("a").getAsFloat();
kp.octave = obj.get("o").getAsInt();
kp.response = obj.get("r").getAsFloat();
kpArray[i] = kp;
}
result.fromArray(kpArray);
return result;
}
}