Base
This commit is contained in:
49
mask-color-descriptors/build.gradle
Normal file
49
mask-color-descriptors/build.gradle
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This generated file contains a sample Java library project to get you started.
|
||||
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
|
||||
* User Manual available at https://docs.gradle.org/7.5/userguide/building_java_projects.html
|
||||
*/
|
||||
|
||||
plugins {
|
||||
// Apply the java-library plugin for API and implementation separation.
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
compileJava.options.encoding = 'UTF-8'
|
||||
compileTestJava.options.encoding = 'UTF-8'
|
||||
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Use JUnit Jupiter for testing.
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
|
||||
|
||||
// This dependency is exported to consumers, that is to say found on their compile classpath.
|
||||
implementation 'org.apache.commons:commons-math3:3.6.1'
|
||||
|
||||
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
|
||||
//implementation 'com.google.guava:guava:31.0.1-jre'
|
||||
|
||||
// https://mvnrepository.com/artifact/org.knowm.xchart/xchart
|
||||
implementation 'org.knowm.xchart:xchart:3.5.4'
|
||||
|
||||
|
||||
//testImplementation("org.openpnp:opencv:3.4.2-0")
|
||||
implementation("org.openpnp:opencv:3.4.2-0")
|
||||
|
||||
//testImplementation("org.opencv:opencv:4.9.0")
|
||||
//implementation("org.opencv:opencv:4.9.0")
|
||||
|
||||
implementation project(":utils")
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
// Use JUnit Platform for unit tests.
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* This Java source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package ru.delkom07;
|
||||
|
||||
public class Library {
|
||||
public boolean someLibraryMethod() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package ru.delkom07.improc.color;
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.knowm.xchart.BitmapEncoder;
|
||||
import org.knowm.xchart.BitmapEncoder.BitmapFormat;
|
||||
import org.knowm.xchart.CategoryChart;
|
||||
import org.knowm.xchart.CategoryChartBuilder;
|
||||
import org.knowm.xchart.PieChart;
|
||||
import org.knowm.xchart.PieChartBuilder;
|
||||
import org.knowm.xchart.SwingWrapper;
|
||||
import org.knowm.xchart.internal.chartpart.Chart;
|
||||
import org.knowm.xchart.style.Styler.LegendPosition;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
|
||||
import ru.delkom07.improc.color.descriptors.ColorLayoutDescriptor;
|
||||
import ru.delkom07.improc.color.descriptors.DominantColor;
|
||||
import ru.delkom07.improc.color.descriptors.DominantColorDescriptor;
|
||||
import ru.delkom07.improc.color.descriptors.GlobalColorHistogram;
|
||||
import ru.delkom07.improc.color.descriptors.MeanColorDescriptor;
|
||||
import ru.delkom07.improc.color.descriptors.SimpleHistograms;
|
||||
import ru.delkom07.util.Pair;
|
||||
|
||||
|
||||
public class ColorDescriptorsDemo {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
|
||||
if(0 == args.length) {
|
||||
printHelp();
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
File imgFile = new File(args[0]);
|
||||
Mat img = Imgcodecs.imread(imgFile.getCanonicalPath());
|
||||
|
||||
ColorDescriptorsDemo cd = new ColorDescriptorsDemo();
|
||||
|
||||
Chart mcChart = cd.calculateMeanColorDescriptor(img, false);
|
||||
Chart gchChart = cd.calculateGCH(img, false);
|
||||
Chart histChart = cd.calculateHistogram(img, false);
|
||||
Chart dcdChart = cd.calculateDCD(img, false);
|
||||
Chart cldChart = cd.calculateCLD(img, false);
|
||||
|
||||
File outputDir = new File(imgFile.getParent(), "output");
|
||||
if(!outputDir.exists()) {
|
||||
outputDir.mkdirs();
|
||||
}
|
||||
|
||||
BitmapEncoder.saveBitmapWithDPI(mcChart, outputDir.getCanonicalPath() + "/mc", BitmapFormat.PNG, 300);
|
||||
BitmapEncoder.saveBitmapWithDPI(gchChart, outputDir.getCanonicalPath() + "/gch", BitmapFormat.PNG, 300);
|
||||
BitmapEncoder.saveBitmapWithDPI(histChart, outputDir.getCanonicalPath() + "/hist", BitmapFormat.PNG, 300);
|
||||
BitmapEncoder.saveBitmapWithDPI(dcdChart, outputDir.getCanonicalPath() + "/dcd", BitmapFormat.PNG, 300);
|
||||
BitmapEncoder.saveBitmapWithDPI(cldChart, outputDir.getCanonicalPath() + "/cld", BitmapFormat.PNG, 300);
|
||||
}
|
||||
|
||||
|
||||
public Chart calculateMeanColorDescriptor(Mat img, boolean show) {
|
||||
int rejectionSigma = 3;
|
||||
MeanColorDescriptor meanColorDescriptor = new MeanColorDescriptor(rejectionSigma).calculate(img);
|
||||
double[] meanColor = meanColorDescriptor.getMeanColorWithoutSigma();
|
||||
|
||||
// Create Chart
|
||||
Color[] sliceColors = new Color[1];
|
||||
sliceColors[0] = new Color((int)meanColor[2], (int)meanColor[1], (int)meanColor[0]);
|
||||
|
||||
|
||||
// Create Chart
|
||||
PieChart chart = new PieChartBuilder().width(800).height(600).title("Mean color descriptor").build();
|
||||
|
||||
// Customize Chart
|
||||
chart.getStyler().setSeriesColors(sliceColors);
|
||||
|
||||
// Series
|
||||
chart.addSeries(Integer.toString(1), 100);
|
||||
|
||||
if(show) {
|
||||
new SwingWrapper<PieChart>(chart).displayChart();
|
||||
}
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
public Chart calculateGCH(Mat img, boolean show) {
|
||||
int dimension = 3;
|
||||
GlobalColorHistogram gch = new GlobalColorHistogram(dimension).calculate(img);
|
||||
double[] flattenGCH = gch.getFlattenGCH();
|
||||
|
||||
// Create Chart
|
||||
CategoryChart chart = new CategoryChartBuilder().width(800).height(600).title("Global Color Histogram").xAxisTitle("Score").yAxisTitle("Number").build();
|
||||
|
||||
// Customize Chart
|
||||
chart.getStyler().setLegendPosition(LegendPosition.InsideNW);
|
||||
//chart.getStyler().setHasAnnotations(true);
|
||||
|
||||
double[] labels = new double[dimension*dimension*dimension];
|
||||
|
||||
for(int i=0; i<dimension*dimension*dimension; i++) {
|
||||
labels[i] = i;
|
||||
}
|
||||
|
||||
// Series
|
||||
chart.addSeries("Values", labels, flattenGCH);
|
||||
|
||||
if(show) {
|
||||
new SwingWrapper<CategoryChart>(chart).displayChart();
|
||||
}
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
public Chart calculateCLD(Mat img, boolean show) {
|
||||
ColorLayoutDescriptor cld = new ColorLayoutDescriptor(12).calculate(img, null, null);
|
||||
double[] descriptorB = cld.getBlueDescriptor();
|
||||
double[] descriptorG = cld.getGreenDescriptor();
|
||||
double[] descriptorR = cld.getRedDescriptor();
|
||||
|
||||
// Create Chart
|
||||
CategoryChart chart = new CategoryChartBuilder().width(800).height(600).title("Color Layout Descriptor").xAxisTitle("Score").yAxisTitle("Number").build();
|
||||
|
||||
// Customize Chart
|
||||
chart.getStyler().setLegendPosition(LegendPosition.InsideNW);
|
||||
//chart.getStyler().setHasAnnotations(true);
|
||||
|
||||
double[] labels = new double[] {1,2,3,4,5,6,7,8,9,10,11,12};
|
||||
|
||||
// Series
|
||||
chart.addSeries("B", labels, descriptorB);
|
||||
chart.addSeries("G", labels, descriptorG);
|
||||
chart.addSeries("R", labels, descriptorR);
|
||||
|
||||
if(show) {
|
||||
new SwingWrapper<CategoryChart>(chart).displayChart();
|
||||
}
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
public Chart calculateDCD(Mat img, boolean show) {
|
||||
int colorsAmount = 3;
|
||||
|
||||
DominantColorDescriptor dcd = new DominantColorDescriptor(colorsAmount, 5, 5, DominantColorDescriptor.SORT_BY_BRIGHTNESS).calculate(img, null, null);
|
||||
List<DominantColor> dominantColors = dcd.getDominantColors();
|
||||
|
||||
System.out.println(dcd.getSparialCoherency());
|
||||
|
||||
Color[] sliceColors = new Color[colorsAmount];
|
||||
for(int i=0; i<colorsAmount; i++) {
|
||||
double[] color = dominantColors.get(i).color;
|
||||
sliceColors[i] = new Color((int)color[2], (int)color[1], (int)color[0]);
|
||||
}
|
||||
|
||||
// Create Chart
|
||||
PieChart chart = new PieChartBuilder().width(800).height(600).title("Dominant Colors Descriptor").build();
|
||||
|
||||
// Customize Chart
|
||||
chart.getStyler().setSeriesColors(sliceColors);
|
||||
|
||||
// Series
|
||||
for(int i=0; i<dominantColors.size(); i++) {
|
||||
chart.addSeries(Integer.toString(i+1), dominantColors.get(i).persent*100);
|
||||
}
|
||||
|
||||
if(show) {
|
||||
new SwingWrapper<PieChart>(chart).displayChart();
|
||||
}
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
public Chart calculateHistogram(Mat img, boolean show) {
|
||||
SimpleHistograms hists = new SimpleHistograms(new Pair<Integer, Integer>(0, 256), 64, SimpleHistograms.ImgType.COLOR).calculate(img, null, null);
|
||||
double[] dataB = hists.getBlueHistogram();
|
||||
double[] dataG = hists.getGreenHistogram();
|
||||
double[] dataR = hists.getRedHistogram();
|
||||
|
||||
double[] labels = new double[dataB.length];
|
||||
for(int i=0; i<dataB.length; i++) {
|
||||
labels[i] = i;
|
||||
}
|
||||
|
||||
// Create Chart
|
||||
CategoryChart chart = new CategoryChartBuilder().width(800).height(600).title("Simple Histograms").xAxisTitle("Score").yAxisTitle("Number").build();
|
||||
|
||||
// Customize Chart
|
||||
chart.getStyler().setLegendPosition(LegendPosition.InsideNW);
|
||||
//chart.getStyler().setHasAnnotations(true);
|
||||
|
||||
// Series
|
||||
chart.addSeries("B", labels, dataB);
|
||||
chart.addSeries("G", labels, dataG);
|
||||
chart.addSeries("R", labels, dataR);
|
||||
|
||||
if(show) {
|
||||
new SwingWrapper<CategoryChart>(chart).displayChart();
|
||||
}
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
|
||||
public static void printHelp() {
|
||||
System.out.println("Usage: java -jar program.jar inputImage.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.delkom07.improc.color;
|
||||
|
||||
public class GCH_ColorSpacies_Demo {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package ru.delkom07.improc.color;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import ru.delkom07.improc.color.descriptors.GlobalColorHistogram;
|
||||
|
||||
public class GCH_Demo {
|
||||
private static final int rectHeight = 50;
|
||||
private static final int rectWidth = 250;
|
||||
private static final int gap = 20;
|
||||
private static final int startX0 = 160;
|
||||
private static final int startY0 = 30;
|
||||
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
|
||||
File outputDir = new File("../data/berners_seeds");
|
||||
|
||||
int dimension = 8;
|
||||
GlobalColorHistogram gch = new GlobalColorHistogram(dimension);
|
||||
|
||||
PrintWriter writer = new PrintWriter(new File(outputDir, "gch8.tsv"), "UTF-8");
|
||||
|
||||
int imgHeight = 2*startY0 + (gap+rectHeight)*dimension*dimension*dimension;
|
||||
Mat gchImg = Mat.zeros(new Size(500, imgHeight), CvType.CV_8UC3);
|
||||
|
||||
for(int i=0; i<dimension*dimension*dimension; i++) {
|
||||
double[] currentColor = gch.getBinColor(i);
|
||||
|
||||
String hex = String.format("#%02x%02x%02x", (int)currentColor[2], (int)currentColor[1], (int)currentColor[0]);
|
||||
writer.print("GCH8_" + (i+1) + " " + currentColor[0] + " " + currentColor[1] + " " + currentColor[2] + " " + hex);
|
||||
writer.println();
|
||||
|
||||
int startX1 = startX0+rectWidth;
|
||||
int startY1 = startY0+rectHeight;
|
||||
|
||||
int step = rectHeight + gap;
|
||||
|
||||
Imgproc.putText(gchImg, "GCH8_" + (i+1), new Point(startX0-100, startY0+rectHeight/2 + i*step), 1, 1, new Scalar(255, 255, 255));
|
||||
Imgproc.rectangle(gchImg, new Point(startX0, startY0 + i*step), new Point(startX1, startY1 + i*step),
|
||||
new Scalar(currentColor[0], currentColor[1], currentColor[2]), -1);
|
||||
}
|
||||
|
||||
writer.close();
|
||||
|
||||
Mat bgr = new Mat();
|
||||
Imgproc.cvtColor(gchImg, bgr, Imgproc.COLOR_RGB2BGR);
|
||||
|
||||
Imgcodecs.imwrite(new File(outputDir, "RGB_gch"+dimension+".png").getCanonicalPath(), bgr);
|
||||
|
||||
|
||||
Mat hsv = new Mat();
|
||||
Imgproc.cvtColor(gchImg, hsv, Imgproc.COLOR_HSV2BGR);
|
||||
|
||||
Imgcodecs.imwrite(new File(outputDir, "HSV_gch"+dimension+".png").getCanonicalPath(), hsv);
|
||||
|
||||
|
||||
Mat Lab = new Mat();
|
||||
Imgproc.cvtColor(gchImg, Lab, Imgproc.COLOR_Lab2BGR);
|
||||
|
||||
Imgcodecs.imwrite(new File(outputDir, "Lab_gch"+dimension+".png").getCanonicalPath(), Lab);
|
||||
|
||||
|
||||
Mat YCbCr = new Mat();
|
||||
Imgproc.cvtColor(gchImg, YCbCr, Imgproc.COLOR_YCrCb2BGR);
|
||||
|
||||
Imgcodecs.imwrite(new File(outputDir, "YCbCr_gch"+dimension+".png").getCanonicalPath(), YCbCr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package ru.delkom07.improc.color.descriptors;
|
||||
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Rect;
|
||||
import org.opencv.core.Size;
|
||||
|
||||
|
||||
/**
|
||||
* Дескриптор цветового распределения.
|
||||
* Исходное изображение (в MPEG-7 этот дескриптор вычисляется в цветовом пространстве Y/Cb/Cr) разбивается на блоки (8*8=64 блока).
|
||||
* Для каждого блока вычисляется средний цвет и записывается в соответствующий пиксель матрицы 8*8.
|
||||
* Далее производится дискретное косинусное преобразование и в результирующий массив записываются самые левые верхние цвета этой матрицы в зигзагообразном порядке.
|
||||
* @author KomyshevEG
|
||||
*
|
||||
*/
|
||||
public class ColorLayoutDescriptor {
|
||||
private static final int[] zigzagScanInds = new int[] {0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56};
|
||||
private static final int MAX_DESCRIPTOR_SIZE = zigzagScanInds.length;
|
||||
|
||||
private static final int GRID_DIMENSION = 8;
|
||||
|
||||
private Mat blocksColorsR = new Mat(new Size(8, 8), CvType.CV_8UC1);
|
||||
private Mat blocksColorsG = new Mat(new Size(8, 8), CvType.CV_8UC1);
|
||||
private Mat blocksColorsB = new Mat(new Size(8, 8), CvType.CV_8UC1);
|
||||
|
||||
private double[] descriptorR;
|
||||
private double[] descriptorG;
|
||||
private double[] descriptorB;
|
||||
|
||||
private int descriptorSize;
|
||||
|
||||
private boolean calculated = false;
|
||||
|
||||
|
||||
|
||||
public ColorLayoutDescriptor(int descriptorSize) {
|
||||
if(descriptorSize > MAX_DESCRIPTOR_SIZE) {
|
||||
descriptorSize = MAX_DESCRIPTOR_SIZE;
|
||||
}
|
||||
|
||||
this.descriptorSize = descriptorSize;
|
||||
|
||||
descriptorR = new double[descriptorSize];
|
||||
descriptorG = new double[descriptorSize];
|
||||
descriptorB = new double[descriptorSize];
|
||||
}
|
||||
|
||||
|
||||
public ColorLayoutDescriptor calculate(Mat img, Mat mask, Rect roi) {
|
||||
if(null == roi) {
|
||||
roi = new Rect(0, 0, img.cols(), img.rows());
|
||||
}
|
||||
|
||||
int xBlockSize = roi.width/GRID_DIMENSION;
|
||||
int yBlockSize = roi.height/GRID_DIMENSION;
|
||||
|
||||
for(int xBlock=0; xBlock<GRID_DIMENSION; xBlock++) {
|
||||
for(int yBlock=0; yBlock<GRID_DIMENSION; yBlock++) {
|
||||
Rect rect = new Rect(roi.x + xBlock*xBlockSize, roi.y + yBlock*yBlockSize, xBlockSize, yBlockSize);
|
||||
|
||||
double[] color = (null != mask) ? getMiddleColor(img, mask, rect) : getMiddleColor(img, rect);
|
||||
|
||||
blocksColorsR.put(yBlock, xBlock, color[0]);
|
||||
blocksColorsG.put(yBlock, xBlock, color[1]);
|
||||
blocksColorsB.put(yBlock, xBlock, color[2]);
|
||||
}
|
||||
}
|
||||
|
||||
Mat convertedR = new Mat();
|
||||
Mat convertedG = new Mat();
|
||||
Mat convertedB = new Mat();
|
||||
|
||||
blocksColorsR.convertTo(convertedR, CvType.CV_32FC3);
|
||||
blocksColorsG.convertTo(convertedG, CvType.CV_32FC3);
|
||||
blocksColorsB.convertTo(convertedB, CvType.CV_32FC3);
|
||||
|
||||
Mat transformedR = new Mat(convertedR.size(), convertedR.type());
|
||||
Mat transformedG = new Mat(convertedG.size(), convertedG.type());
|
||||
Mat transformedB = new Mat(convertedB.size(), convertedB.type());
|
||||
|
||||
Core.dct(convertedR, transformedR);
|
||||
Core.dct(convertedG, transformedG);
|
||||
Core.dct(convertedB, transformedB);
|
||||
|
||||
for(int i=0; i<descriptorSize; i++) {
|
||||
int x = zigzagScanInds[i]%GRID_DIMENSION;
|
||||
int y = (int)zigzagScanInds[i]/GRID_DIMENSION;
|
||||
|
||||
descriptorR[i] = blocksColorsR.get(y, x)[0];
|
||||
descriptorG[i] = blocksColorsG.get(y, x)[0];
|
||||
descriptorB[i] = blocksColorsB.get(y, x)[0];
|
||||
}
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public double[] getBlueDescriptor() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("Descriptor was not calculated");
|
||||
}
|
||||
|
||||
return descriptorB;
|
||||
}
|
||||
|
||||
|
||||
public double[] getGreenDescriptor() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("Descriptor was not calculated");
|
||||
}
|
||||
|
||||
return descriptorG;
|
||||
}
|
||||
|
||||
|
||||
public double[] getRedDescriptor() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("Descriptor was not calculated");
|
||||
}
|
||||
|
||||
return descriptorR;
|
||||
}
|
||||
|
||||
|
||||
private double[] getMiddleColor(Mat img, Mat mask, Rect rect) {
|
||||
double[] middleColor = new double[3];
|
||||
|
||||
int count = 0;
|
||||
for(int i=rect.y; i<rect.y+rect.height; i++) {
|
||||
for(int j=rect.x; j<rect.x+rect.width; j++) {
|
||||
if(mask.get(i, j)[0] == 255) {
|
||||
double[] color = img.get(i, j);
|
||||
|
||||
middleColor[0] += color[0];
|
||||
middleColor[1] += color[1];
|
||||
middleColor[2] += color[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
middleColor[0] = middleColor[0] / ((double)count);
|
||||
middleColor[1] = middleColor[1] / ((double)count);
|
||||
middleColor[2] = middleColor[2] / ((double)count);
|
||||
|
||||
return middleColor;
|
||||
}
|
||||
|
||||
|
||||
private double[] getMiddleColor(Mat img, Rect rect) {
|
||||
double[] middleColor = new double[3];
|
||||
|
||||
int count = 0;
|
||||
for(int i=rect.y; i<rect.y+rect.height; i++) {
|
||||
for(int j=rect.x; j<rect.x+rect.width; j++) {
|
||||
double[] color = img.get(i, j);
|
||||
|
||||
middleColor[0] += color[0];
|
||||
middleColor[1] += color[1];
|
||||
middleColor[2] += color[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
middleColor[0] = middleColor[0] / ((double)count);
|
||||
middleColor[1] = middleColor[1] / ((double)count);
|
||||
middleColor[2] = middleColor[2] / ((double)count);
|
||||
|
||||
return middleColor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = "";
|
||||
for(int i=0; i<descriptorSize; i++) {
|
||||
if(i<descriptorR.length-1) {
|
||||
result += "[" + descriptorR[i] + ", " + descriptorG[i] + ", " + descriptorB[i] + "]";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public String toTsvCsvString(String sep) {
|
||||
String result = "";
|
||||
|
||||
for(int i=0; i<descriptorSize; i++) {
|
||||
if(i<descriptorR.length-1) {
|
||||
result += descriptorR[i] + sep + descriptorG[i] + sep + descriptorB[i] + sep;
|
||||
} else {
|
||||
result += descriptorR[i] + sep + descriptorG[i] + sep + descriptorB[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.delkom07.improc.color.descriptors;
|
||||
import org.apache.commons.math3.ml.clustering.Clusterable;
|
||||
|
||||
|
||||
|
||||
public class ColorPixelContainer implements Clusterable {
|
||||
private double[] pixel;
|
||||
|
||||
|
||||
public ColorPixelContainer(double[] pixel) {
|
||||
this.pixel = pixel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] getPoint() {
|
||||
return pixel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.delkom07.improc.color.descriptors;
|
||||
|
||||
public class DominantColor {
|
||||
public double[] color;
|
||||
public double persent;
|
||||
public double variance;
|
||||
|
||||
|
||||
|
||||
public DominantColor(double[] color, double persent, double variance) {
|
||||
this.color = color;
|
||||
this.persent = persent;
|
||||
this.variance = variance;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return color[0] + ", " + color[1] + ", " + color[2] + ", " + persent + ", " + variance;
|
||||
}
|
||||
|
||||
|
||||
public String toTsvCsvString(String sep) {
|
||||
return color[0] + sep + color[1] + sep + color[2] + sep + persent + sep + variance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
package ru.delkom07.improc.color.descriptors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.math3.ml.clustering.CentroidCluster;
|
||||
import org.apache.commons.math3.ml.clustering.KMeansPlusPlusClusterer;
|
||||
import org.apache.commons.math3.ml.clustering.MultiKMeansPlusPlusClusterer;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Rect;
|
||||
import org.opencv.core.Size;
|
||||
|
||||
import ru.delkom07.improc.geometry.GeometricFunctions;
|
||||
|
||||
public class DominantColorDescriptor {
|
||||
private List<DominantColor> dominantColors = new ArrayList<DominantColor>();
|
||||
private double spatialCoherency;
|
||||
|
||||
private int descriptorSize;
|
||||
|
||||
private int xStepSize;
|
||||
private int yStepSize;
|
||||
|
||||
private boolean calculated = false;
|
||||
|
||||
public static final short SORT_BY_PERSENT = 0;
|
||||
public static final short SORT_BY_BRIGHTNESS = 1;
|
||||
|
||||
private short sortBy = 0;
|
||||
|
||||
public DominantColorDescriptor(int descriptorSize) {
|
||||
this.descriptorSize = descriptorSize;
|
||||
|
||||
this.xStepSize = 1;
|
||||
this.yStepSize = 1;
|
||||
}
|
||||
|
||||
|
||||
public DominantColorDescriptor(int descriptorSize, int xStepSize, int yStepSize, short sortBy) {
|
||||
this.descriptorSize = descriptorSize;
|
||||
|
||||
this.xStepSize = xStepSize > 0 ? xStepSize : 1;
|
||||
this.yStepSize = yStepSize > 0 ? yStepSize : 1;
|
||||
|
||||
this.sortBy = sortBy;
|
||||
}
|
||||
|
||||
|
||||
public DominantColorDescriptor calculate(Mat img, Mat mask, Rect roi) {
|
||||
if(null == roi) {
|
||||
roi = new Rect(0, 0, img.cols(), img.rows());
|
||||
}
|
||||
|
||||
List<CentroidCluster<ColorPixelContainer>> clusters = null != mask ? clusterizeColors(img, mask, roi, descriptorSize) : clusterizeColors(img, roi, descriptorSize);
|
||||
|
||||
int clustersSumSize = 0;
|
||||
for(CentroidCluster<ColorPixelContainer> cluster : clusters) {
|
||||
clustersSumSize += cluster.getPoints().size();
|
||||
}
|
||||
|
||||
for(CentroidCluster<ColorPixelContainer> cluster : clusters) {
|
||||
List<ColorPixelContainer> clusterColors = cluster.getPoints();
|
||||
|
||||
double[] clusterCenterColor = cluster.getCenter().getPoint();
|
||||
int clusterSize = cluster.getPoints().size();
|
||||
double variance = calculateColorVariance(clusterColors, clusterCenterColor);
|
||||
|
||||
dominantColors.add(new DominantColor(clusterCenterColor, clusterSize/(double)clustersSumSize, variance));
|
||||
}
|
||||
|
||||
switch(sortBy) {
|
||||
case 1: {
|
||||
dominantColors.sort(new Comparator<DominantColor>() {
|
||||
|
||||
@Override
|
||||
public int compare(DominantColor o1, DominantColor o2) {
|
||||
double val1 = o1.color[0]+o1.color[1]+o1.color[2];
|
||||
double val2 = o2.color[0]+o2.color[1]+o2.color[2];
|
||||
|
||||
if(val1<val2) {
|
||||
return -1;
|
||||
}
|
||||
if(val1>val2) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
dominantColors.sort(new Comparator<DominantColor>() {
|
||||
|
||||
@Override
|
||||
public int compare(DominantColor o1, DominantColor o2) {
|
||||
if(o1.persent<o2.persent) {
|
||||
return -1;
|
||||
}
|
||||
if(o1.persent>o2.persent) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
spatialCoherency = calculatespatialCoherency(img.size(), clusters, clustersSumSize);
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public List<DominantColor> getDominantColors() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("Descriptor was not calculated");
|
||||
}
|
||||
|
||||
return dominantColors;
|
||||
}
|
||||
|
||||
|
||||
public double getSparialCoherency() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("Descriptor was not calculated");
|
||||
}
|
||||
|
||||
return spatialCoherency;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private double calculateColorVariance(List<ColorPixelContainer> colors, double[] clusterCenterColor) {
|
||||
double sumSqrDeviance = 0;
|
||||
for(ColorPixelContainer container : colors) {
|
||||
sumSqrDeviance += GeometricFunctions.euclideanDistance(container.getPoint(), clusterCenterColor);
|
||||
}
|
||||
|
||||
return sumSqrDeviance/colors.size();
|
||||
}
|
||||
|
||||
private double calculatespatialCoherency(Size size, List<CentroidCluster<ColorPixelContainer>> clusters, int clustersSumSize) {
|
||||
double coherency = 0;
|
||||
|
||||
for(CentroidCluster<ColorPixelContainer> cluster : clusters) {
|
||||
List<ColorPixelContainer> clusterColors = cluster.getPoints();
|
||||
|
||||
if(0 != clusterColors.size()) {
|
||||
|
||||
double percentage = clusterColors.size() / clustersSumSize;
|
||||
|
||||
Mat clusterImprint = Mat.zeros(size, CvType.CV_8UC1);
|
||||
|
||||
for(ColorPixelContainer container : clusterColors) {
|
||||
double[] point = container.getPoint();
|
||||
clusterImprint.put((int)point[1], (int)point[0], 255);
|
||||
}
|
||||
|
||||
int neighbors = 0;
|
||||
for(int i=0; i<clusterImprint.rows(); i++) {
|
||||
for(int j=0; j<clusterImprint.cols(); j++) {
|
||||
double pixel = clusterImprint.get(i, j)[0];
|
||||
if(0 != pixel) {
|
||||
|
||||
if(i-1 >= 0 ? (0 != clusterImprint.get(i-1, j)[0]) : false) {
|
||||
neighbors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(j+1 < clusterImprint.cols() ? (0 != clusterImprint.get(i, j+1)[0]) : false) {
|
||||
neighbors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if(i+1 < clusterImprint.rows() ? (0 != clusterImprint.get(i+1, j)[0]) : false) {
|
||||
neighbors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(j-1 >= 0 ? (0 != clusterImprint.get(i, j-1)[0]) : false) {
|
||||
neighbors++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
coherency += (neighbors / (double) clusterColors.size()) * percentage;
|
||||
|
||||
clusterImprint.release();
|
||||
}
|
||||
}
|
||||
|
||||
return coherency;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private List<CentroidCluster<ColorPixelContainer>> clusterizeColors(Mat img, Mat mask, Rect roi, int clusterNumber) {
|
||||
KMeansPlusPlusClusterer<ColorPixelContainer> kmeansClusterer = new KMeansPlusPlusClusterer<ColorPixelContainer>(clusterNumber);
|
||||
MultiKMeansPlusPlusClusterer<ColorPixelContainer> mkmeansClusterer = new MultiKMeansPlusPlusClusterer<>(kmeansClusterer, 1);
|
||||
|
||||
List<ColorPixelContainer> points = new LinkedList<ColorPixelContainer>();
|
||||
|
||||
for(int i=roi.y; i<roi.y+roi.height; i+= yStepSize) {
|
||||
for(int j=roi.x; j<roi.x+roi.width; j+= xStepSize) {
|
||||
if(mask.get(i, j)[0] == 255) {
|
||||
double[] pixel = img.get(i, j);
|
||||
points.add(new ColorPixelContainer(new double[] {pixel[0], pixel[1], pixel[2]}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mkmeansClusterer.cluster(points);
|
||||
}
|
||||
|
||||
|
||||
private List<CentroidCluster<ColorPixelContainer>> clusterizeColors(Mat img, Rect roi, int clusterNumber) {
|
||||
KMeansPlusPlusClusterer<ColorPixelContainer> kmeansClusterer = new KMeansPlusPlusClusterer<ColorPixelContainer>(clusterNumber);
|
||||
MultiKMeansPlusPlusClusterer<ColorPixelContainer> mkmeansClusterer = new MultiKMeansPlusPlusClusterer<>(kmeansClusterer, 1);
|
||||
|
||||
List<ColorPixelContainer> points = new LinkedList<ColorPixelContainer>();
|
||||
|
||||
for(int i=roi.y; i<roi.y+roi.height; i+= yStepSize) {
|
||||
for(int j=roi.x; j<roi.x+roi.width; j+= xStepSize) {
|
||||
double[] pixel = img.get(i, j);
|
||||
points.add(new ColorPixelContainer(new double[] {pixel[0], pixel[1], pixel[2]}));
|
||||
}
|
||||
}
|
||||
|
||||
return mkmeansClusterer.cluster(points);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = "[";
|
||||
|
||||
for(DominantColor dColors : dominantColors) {
|
||||
result += dColors.toString() + ", ";
|
||||
}
|
||||
|
||||
result += spatialCoherency + "]";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toTsvCsvString(String sep) {
|
||||
String result = "";
|
||||
|
||||
for(DominantColor dColors : dominantColors) {
|
||||
result += dColors.toTsvCsvString(sep) + sep;
|
||||
}
|
||||
|
||||
result += spatialCoherency;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package ru.delkom07.improc.color.descriptors;
|
||||
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
|
||||
/**
|
||||
* Глобальная цветовая гистограмма.
|
||||
* Цветовое пространство разбивается на трехмерный куб, размера D*D*D (D - dimension).
|
||||
* Размер бина равен 256/D.
|
||||
* В каждый бин записывается доля пикселей, попавших в него из изображения (n/count).
|
||||
* @author KomyshevEG
|
||||
*
|
||||
*/
|
||||
public class GlobalColorHistogram {
|
||||
private double[][][] gch; // GlobalColorHistogram
|
||||
|
||||
private int ch1RangeSize = 256; // Default channel 1 range size
|
||||
private int ch2RangeSize = 256; // Default channel 2 range size
|
||||
private int ch3RangeSize = 256; // Default channel 3 range size
|
||||
|
||||
private int dimension;
|
||||
|
||||
private float ch1BinSize; // Channel 1 bin size
|
||||
private float ch2BinSize; // Channel 2 bin size
|
||||
private float ch3BinSize; // Channel 3 bin size
|
||||
|
||||
private boolean calculated = false;
|
||||
|
||||
|
||||
|
||||
public GlobalColorHistogram(int dimension) {
|
||||
this.dimension = dimension;
|
||||
|
||||
ch1BinSize = ch1RangeSize/(float)dimension;
|
||||
ch2BinSize = ch2RangeSize/(float)dimension;
|
||||
ch3BinSize = ch3RangeSize/(float)dimension;
|
||||
|
||||
gch = new double[dimension][dimension][dimension];
|
||||
}
|
||||
|
||||
|
||||
public GlobalColorHistogram(int dimension, int channel1RangeSize, int channel2RangeSize, int channel3RangeSize) {
|
||||
this.dimension = dimension;
|
||||
|
||||
this.ch1RangeSize = channel1RangeSize;
|
||||
this.ch2RangeSize = channel2RangeSize;
|
||||
this.ch3RangeSize = channel3RangeSize;
|
||||
|
||||
ch1BinSize = channel1RangeSize/(float)dimension;
|
||||
ch2BinSize = channel2RangeSize/(float)dimension;
|
||||
ch3BinSize = channel3RangeSize/(float)dimension;
|
||||
|
||||
gch = new double[dimension][dimension][dimension];
|
||||
}
|
||||
|
||||
|
||||
public GlobalColorHistogram calculate(Mat img) {
|
||||
long pixelsAmount = 0;
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] pix = img.get(i, j);
|
||||
|
||||
gch[(int)(pix[0]/ch1BinSize)][(int)(pix[1]/ch2BinSize)][(int)(pix[2]/ch3BinSize)]++;
|
||||
pixelsAmount++;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<dimension; i++) {
|
||||
for(int j=0; j<dimension; j++) {
|
||||
for(int k=0; k<dimension; k++) {
|
||||
gch[i][j][k] = gch[i][j][k] / ((double)pixelsAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public GlobalColorHistogram calculate(Mat img, Mat mask) {
|
||||
long pixelsAmount = 0;
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] pix = img.get(i, j);
|
||||
|
||||
if((int)pix[0]/ch1BinSize >= 8) {
|
||||
System.out.println("FFF");
|
||||
}
|
||||
|
||||
if(mask.get(i, j)[0] != 0) {
|
||||
gch[(int)(pix[0]/ch1BinSize)][(int)(pix[1]/ch2BinSize)][(int)(pix[2]/ch3BinSize)]++;
|
||||
pixelsAmount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<dimension; i++) {
|
||||
for(int j=0; j<dimension; j++) {
|
||||
for(int k=0; k<dimension; k++) {
|
||||
gch[i][j][k] = gch[i][j][k] / ((double)pixelsAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public double[][][] getGCH() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("Descriptor was not calculated");
|
||||
}
|
||||
|
||||
return gch;
|
||||
}
|
||||
|
||||
|
||||
public double[] getFlattenGCH() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("Descriptor was not calculated");
|
||||
}
|
||||
|
||||
double[] flatten = new double[dimension*dimension*dimension];
|
||||
|
||||
for(int i=0; i<dimension; i++) {
|
||||
for(int j=0; j<dimension; j++) {
|
||||
for(int k=0; k<dimension; k++) {
|
||||
flatten[i + j*dimension + k*dimension*dimension] = gch[i][j][k]; // i - R; j - G; k - B
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flatten;
|
||||
}
|
||||
|
||||
|
||||
public String toTsvCsvString(String sep) {
|
||||
double[] flattenGCH = getFlattenGCH();
|
||||
|
||||
String result = "";
|
||||
for(int i=0; i<flattenGCH.length; i++) {
|
||||
double val = flattenGCH[i];
|
||||
if(i != flattenGCH.length-1) {
|
||||
result = result + Double.toString(val) + sep;
|
||||
} else {
|
||||
result = result + Double.toString(val);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public double[] getBinColor(int i, int j, int k) {
|
||||
double toMiddleShiftCh1 = (ch1RangeSize/dimension)/2;
|
||||
double toMiddleShiftCh2 = (ch2RangeSize/dimension)/2;
|
||||
double toMiddleShiftCh3 = (ch3RangeSize/dimension)/2;
|
||||
|
||||
return new double[] {(ch1RangeSize/dimension)*i + toMiddleShiftCh1, (ch2RangeSize/dimension)*j + toMiddleShiftCh2, (ch3RangeSize/dimension)*k + toMiddleShiftCh3};
|
||||
}
|
||||
|
||||
|
||||
public double[] getBinColor(int n) {
|
||||
// n = i + j*dimension + k*dimension*dimension
|
||||
|
||||
int k = (int)(n / (dimension*dimension));
|
||||
int j = (int)(n - k*dimension*dimension) / dimension;
|
||||
int i = n - k*dimension*dimension - j*dimension;
|
||||
|
||||
return getBinColor(i, j, k);
|
||||
}
|
||||
|
||||
|
||||
public Mat markPixels(Mat img, Mat mask, int binNumber) {
|
||||
Mat markMask = Mat.zeros(mask.size(), CvType.CV_8UC1);
|
||||
|
||||
double ch1HalfOfBinSize = ch1BinSize/2.0;
|
||||
double ch2HalfOfBinSize = ch2BinSize/2.0;
|
||||
double ch3HalfOfBinSize = ch3BinSize/2.0;
|
||||
|
||||
double[] binColor = getBinColor(binNumber);
|
||||
double[] from = new double[] {binColor[0]-ch1HalfOfBinSize, binColor[1]-ch2HalfOfBinSize, binColor[2]-ch3HalfOfBinSize};
|
||||
double[] to = new double[] {binColor[0]+ch1HalfOfBinSize, binColor[1]+ch2HalfOfBinSize, binColor[2]+ch3HalfOfBinSize};
|
||||
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double maskPix = mask.get(i, j)[0];
|
||||
|
||||
if(0 != maskPix) {
|
||||
double[] pix = img.get(i, j);
|
||||
|
||||
if((pix[0]>from[0] && pix[0]<to[0]) && (pix[1]>from[1] && pix[1]<to[1]) && (pix[2]>from[2] && pix[2]<to[2])) {
|
||||
markMask.put(i, j, new double[] {255});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return markMask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
package ru.delkom07.improc.color.descriptors;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Rect;
|
||||
|
||||
public class MeanColorDescriptor {
|
||||
private double rejectionSigma;
|
||||
private double[] meanColor;
|
||||
private double sigma;
|
||||
private double[] meanColorExceptDevPixels;
|
||||
|
||||
|
||||
|
||||
public MeanColorDescriptor(double rejectionSigma) {
|
||||
this.rejectionSigma = rejectionSigma;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculation of mean color except pixels with greater deviation then sigma * rejectionSigma.
|
||||
* @param img - source image.
|
||||
* @param mask - mask.
|
||||
* @return - mean color.
|
||||
*/
|
||||
public MeanColorDescriptor calculate(Mat img, Mat mask) {
|
||||
meanColor = calculateMeanColor(img, mask);
|
||||
sigma = calculateSigma(img, mask, meanColor);
|
||||
meanColorExceptDevPixels = calculateMeanColorExceptDevPixels(img, mask, meanColor, sigma, rejectionSigma);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public MeanColorDescriptor calculate(Mat img, Rect rect) {
|
||||
meanColor = calculateMeanColor(img, rect);
|
||||
sigma = calculateSigma(img, rect, meanColor);
|
||||
meanColorExceptDevPixels = calculateMeanColorExceptDevPixels(img, rect, meanColor, sigma, rejectionSigma);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculation of mean color except pixels with greater deviation then sigma * rejectionSigma.
|
||||
* Without mask.
|
||||
* @param img - source image.
|
||||
* @param mask - mask.
|
||||
* @return - mean color.
|
||||
*/
|
||||
public MeanColorDescriptor calculate(Mat img) {
|
||||
meanColor = calculateMeanColor(img);
|
||||
sigma = calculateSigma(img, meanColor);
|
||||
meanColorExceptDevPixels = calculateMeanColorExceptDevPixels(img, meanColor, sigma, rejectionSigma);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// Calculation
|
||||
private double[] calculateMeanColor(Mat img, Mat mask) {
|
||||
double[] meanColor = new double[3];
|
||||
int counter = 0;
|
||||
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] maskPix = mask.get(i, j);
|
||||
|
||||
if(0 != maskPix[0]) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
meanColor[0] += imgPix[0];
|
||||
meanColor[1] += imgPix[1];
|
||||
meanColor[2] += imgPix[2];
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meanColor[0] = meanColor[0] / ((double)counter);
|
||||
meanColor[1] = meanColor[1] / ((double)counter);
|
||||
meanColor[2] = meanColor[2] / ((double)counter);
|
||||
|
||||
return meanColor;
|
||||
}
|
||||
|
||||
|
||||
private double[] calculateMeanColor(Mat img, Rect rect) {
|
||||
double[] meanColor = new double[3];
|
||||
int counter = 0;
|
||||
|
||||
for(int i=rect.y; i<rect.y+rect.height; i++) {
|
||||
for(int j=rect.x; j<rect.x+rect.width; j++) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
meanColor[0] += imgPix[0];
|
||||
meanColor[1] += imgPix[1];
|
||||
meanColor[2] += imgPix[2];
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
meanColor[0] = meanColor[0] / ((double)counter);
|
||||
meanColor[1] = meanColor[1] / ((double)counter);
|
||||
meanColor[2] = meanColor[2] / ((double)counter);
|
||||
|
||||
return meanColor;
|
||||
}
|
||||
|
||||
|
||||
// without mask
|
||||
private double[] calculateMeanColor(Mat img) {
|
||||
double[] meanColor = new double[3];
|
||||
int counter = 0;
|
||||
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
meanColor[0] += imgPix[0];
|
||||
meanColor[1] += imgPix[1];
|
||||
meanColor[2] += imgPix[2];
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
meanColor[0] = meanColor[0] / ((double)counter);
|
||||
meanColor[1] = meanColor[1] / ((double)counter);
|
||||
meanColor[2] = meanColor[2] / ((double)counter);
|
||||
|
||||
return meanColor;
|
||||
}
|
||||
|
||||
|
||||
private double calculateSigma(Mat img, Mat mask, double[] meanColor) {
|
||||
double meanDeviationSquare = 0;
|
||||
int counter = 0;
|
||||
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] maskPix = mask.get(i, j);
|
||||
|
||||
if(0 != maskPix[0]) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
meanDeviationSquare += deviationSquare(meanColor, imgPix);
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meanDeviationSquare = meanDeviationSquare / ((double)counter);
|
||||
|
||||
return Math.sqrt(meanDeviationSquare);
|
||||
}
|
||||
|
||||
|
||||
private double calculateSigma(Mat img, Rect rect, double[] meanColor) {
|
||||
double meanDeviationSquare = 0;
|
||||
int counter = 0;
|
||||
|
||||
for(int i=rect.y; i<rect.y+rect.height; i++) {
|
||||
for(int j=rect.x; j<rect.x+rect.width; j++) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
meanDeviationSquare += deviationSquare(meanColor, imgPix);
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
meanDeviationSquare = meanDeviationSquare / ((double)counter);
|
||||
|
||||
return Math.sqrt(meanDeviationSquare);
|
||||
}
|
||||
|
||||
|
||||
// without mask
|
||||
private double calculateSigma(Mat img, double[] meanColor) {
|
||||
double meanDeviationSquare = 0;
|
||||
int counter = 0;
|
||||
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
meanDeviationSquare += deviationSquare(meanColor, imgPix);
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
meanDeviationSquare = meanDeviationSquare / ((double)counter);
|
||||
|
||||
return Math.sqrt(meanDeviationSquare);
|
||||
}
|
||||
|
||||
|
||||
private double[] calculateMeanColorExceptDevPixels(Mat img, Mat mask, double[] meanColor, double sigma, double rejectionSigma) {
|
||||
double[] meanColorExceptDevPixels = new double[3];
|
||||
int counter = 0;
|
||||
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] maskPix = mask.get(i, j);
|
||||
|
||||
if(0 != maskPix[0]) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
double deviation = colorDistance(meanColor, imgPix);
|
||||
if(deviation < sigma * rejectionSigma) {
|
||||
meanColorExceptDevPixels[0] += imgPix[0];
|
||||
meanColorExceptDevPixels[1] += imgPix[1];
|
||||
meanColorExceptDevPixels[2] += imgPix[2];
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meanColorExceptDevPixels[0] = meanColorExceptDevPixels[0] / ((double)counter);
|
||||
meanColorExceptDevPixels[1] = meanColorExceptDevPixels[1] / ((double)counter);
|
||||
meanColorExceptDevPixels[2] = meanColorExceptDevPixels[2] / ((double)counter);
|
||||
|
||||
return meanColorExceptDevPixels;
|
||||
}
|
||||
|
||||
|
||||
private double[] calculateMeanColorExceptDevPixels(Mat img, Rect rect, double[] meanColor, double sigma, double rejectionSigma) {
|
||||
double[] meanColorExceptDevPixels = new double[3];
|
||||
int counter = 0;
|
||||
|
||||
for(int i=rect.y; i<rect.y + rect.height; i++) {
|
||||
for(int j=rect.x; j<rect.x+rect.width; j++) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
double deviation = colorDistance(meanColor, imgPix);
|
||||
if(deviation < sigma * rejectionSigma) {
|
||||
meanColorExceptDevPixels[0] += imgPix[0];
|
||||
meanColorExceptDevPixels[1] += imgPix[1];
|
||||
meanColorExceptDevPixels[2] += imgPix[2];
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meanColorExceptDevPixels[0] = meanColorExceptDevPixels[0] / ((double)counter);
|
||||
meanColorExceptDevPixels[1] = meanColorExceptDevPixels[1] / ((double)counter);
|
||||
meanColorExceptDevPixels[2] = meanColorExceptDevPixels[2] / ((double)counter);
|
||||
|
||||
return meanColorExceptDevPixels;
|
||||
}
|
||||
|
||||
|
||||
// without mask
|
||||
private double[] calculateMeanColorExceptDevPixels(Mat img, double[] meanColor, double sigma, double rejectionSigma) {
|
||||
double[] meanColorExceptDevPixels = new double[3];
|
||||
int counter = 0;
|
||||
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double[] imgPix = img.get(i, j);
|
||||
|
||||
double deviation = colorDistance(meanColor, imgPix);
|
||||
if(deviation < sigma * rejectionSigma) {
|
||||
meanColorExceptDevPixels[0] += imgPix[0];
|
||||
meanColorExceptDevPixels[1] += imgPix[1];
|
||||
meanColorExceptDevPixels[2] += imgPix[2];
|
||||
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
meanColorExceptDevPixels[0] = meanColorExceptDevPixels[0] / ((double)counter);
|
||||
meanColorExceptDevPixels[1] = meanColorExceptDevPixels[1] / ((double)counter);
|
||||
meanColorExceptDevPixels[2] = meanColorExceptDevPixels[2] / ((double)counter);
|
||||
|
||||
return meanColorExceptDevPixels;
|
||||
}
|
||||
|
||||
|
||||
private double deviationSquare(double[] color1, double[] color2) {
|
||||
return ((color1[0]-color2[0])*(color1[0]-color2[0]) +
|
||||
(color1[1]-color2[1])*(color1[1]-color2[1]) +
|
||||
(color1[2]-color2[2])*(color1[2]-color2[2]));
|
||||
}
|
||||
|
||||
|
||||
private double colorDistance(double[] color1, double[] color2) {
|
||||
return Math.sqrt((color1[0]-color2[0])*(color1[0]-color2[0]) +
|
||||
(color1[1]-color2[1])*(color1[1]-color2[1]) +
|
||||
(color1[2]-color2[2])*(color1[2]-color2[2]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Getters
|
||||
public double[] getMeanColorWithoutSigma() {
|
||||
return meanColorExceptDevPixels;
|
||||
}
|
||||
|
||||
public double getSigma() {
|
||||
return sigma;
|
||||
}
|
||||
|
||||
public String toTsvCsvString(String sep) {
|
||||
return meanColorExceptDevPixels[0] + sep + meanColorExceptDevPixels[1] + sep + meanColorExceptDevPixels[2];
|
||||
}
|
||||
|
||||
public double[] toArray() {
|
||||
return new double[] {meanColorExceptDevPixels[0], meanColorExceptDevPixels[1], meanColorExceptDevPixels[2]};
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static void markPixels(Mat img, Mat mask, int channel, double from, double to, double[] color) {
|
||||
for(int i=0; i<img.rows(); i++) {
|
||||
for(int j=0; j<img.cols(); j++) {
|
||||
double maskPix = mask.get(i, j)[0];
|
||||
|
||||
if(0 != maskPix) {
|
||||
double[] pix = img.get(i, j);
|
||||
|
||||
if(pix[channel] > from && pix[channel] < to) {
|
||||
img.put(i, j, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package ru.delkom07.improc.color.descriptors;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfFloat;
|
||||
import org.opencv.core.MatOfInt;
|
||||
import org.opencv.core.Rect;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import ru.delkom07.util.Pair;
|
||||
|
||||
public class SimpleHistograms {
|
||||
private Pair<Integer, Integer> range;
|
||||
private int histogramSize;
|
||||
|
||||
private Mat histGray = new Mat();
|
||||
|
||||
private Mat histB = new Mat();
|
||||
private Mat histG = new Mat();
|
||||
private Mat histR = new Mat();
|
||||
|
||||
private ImgType imgType;
|
||||
|
||||
private boolean calculated = false;
|
||||
|
||||
public enum ImgType {
|
||||
GRAY, COLOR
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SimpleHistograms(Pair<Integer, Integer> range, int histSize, ImgType imgType) {
|
||||
this.range = range;
|
||||
this.histogramSize = histSize;
|
||||
|
||||
this.imgType = imgType;
|
||||
}
|
||||
|
||||
|
||||
public SimpleHistograms calculate(Mat img, Mat mask, Rect roi) {
|
||||
if(null == mask) {
|
||||
mask = new Mat();
|
||||
}
|
||||
|
||||
if(null != roi) {
|
||||
img = img.submat(roi);
|
||||
mask = mask.submat(roi);
|
||||
}
|
||||
|
||||
MatOfFloat ranges = new MatOfFloat(range.getLeft(), range.getRight());
|
||||
MatOfInt histSize = new MatOfInt(histogramSize);
|
||||
|
||||
if(imgType == ImgType.COLOR) {
|
||||
Imgproc.calcHist(Arrays.asList(img), new MatOfInt(0), mask, histB, histSize, ranges);
|
||||
Imgproc.calcHist(Arrays.asList(img), new MatOfInt(1), mask, histG, histSize, ranges);
|
||||
Imgproc.calcHist(Arrays.asList(img), new MatOfInt(2), mask, histR, histSize, ranges);
|
||||
} else {
|
||||
Imgproc.calcHist(Arrays.asList(img), new MatOfInt(), new Mat(), histGray, histSize, ranges);
|
||||
}
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Mat getGrayHistogramMat() {
|
||||
if(imgType != ImgType.GRAY || !calculated) {
|
||||
throw new RuntimeException("Gray histogram mat isn't calculated");
|
||||
}
|
||||
|
||||
return histGray;
|
||||
}
|
||||
|
||||
public Mat getBlueColorHistogramMat() {
|
||||
if(imgType != ImgType.COLOR || !calculated) {
|
||||
throw new RuntimeException("Color histogram mats isn't calculated");
|
||||
}
|
||||
|
||||
return histB;
|
||||
}
|
||||
|
||||
public Mat getGreenColorHistogramMat() {
|
||||
if(imgType != ImgType.COLOR || !calculated) {
|
||||
throw new RuntimeException("Color histogram mats isn't calculated");
|
||||
}
|
||||
|
||||
return histG;
|
||||
}
|
||||
|
||||
public Mat getRedColorHistogramMat() {
|
||||
if(imgType != ImgType.COLOR || !calculated) {
|
||||
throw new RuntimeException("Color histogram mats isn't calculated");
|
||||
}
|
||||
|
||||
return histR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public double[] getGrayHistogram() {
|
||||
if(imgType != ImgType.GRAY || !calculated) {
|
||||
throw new RuntimeException("Gray histogram mat isn't calculated");
|
||||
}
|
||||
|
||||
double[] dataGray = new double[histGray.cols()*histGray.rows()];
|
||||
|
||||
int k = 0;
|
||||
for(int i=0; i<histGray.rows(); i++) {
|
||||
for(int j=0; j<histGray.cols(); j++) {
|
||||
dataGray[k] = histGray.get(i, j)[0];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
return dataGray;
|
||||
}
|
||||
|
||||
|
||||
public double[] getBlueHistogram() {
|
||||
if(imgType != ImgType.COLOR || !calculated) {
|
||||
throw new RuntimeException("Color histogram mats isn't calculated");
|
||||
}
|
||||
|
||||
double[] dataB = new double[histB.cols()*histB.rows()];
|
||||
|
||||
int k = 0;
|
||||
for(int i=0; i<histB.rows(); i++) {
|
||||
for(int j=0; j<histB.cols(); j++) {
|
||||
dataB[k] = histB.get(i, j)[0];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
return dataB;
|
||||
}
|
||||
|
||||
|
||||
public double[] getGreenHistogram() {
|
||||
if(imgType != ImgType.COLOR || !calculated) {
|
||||
throw new RuntimeException("Color histogram mats isn't calculated");
|
||||
}
|
||||
|
||||
double[] dataG = new double[histG.cols()*histG.rows()];
|
||||
|
||||
int k = 0;
|
||||
for(int i=0; i<histG.rows(); i++) {
|
||||
for(int j=0; j<histG.cols(); j++) {
|
||||
dataG[k] = histG.get(i, j)[0];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
return dataG;
|
||||
}
|
||||
|
||||
|
||||
public double[] getRedHistogram() {
|
||||
if(imgType != ImgType.COLOR || !calculated) {
|
||||
throw new RuntimeException("Color histogram mats isn't calculated");
|
||||
}
|
||||
|
||||
double[] dataR = new double[histR.cols()*histR.rows()];
|
||||
|
||||
int k = 0;
|
||||
for(int i=0; i<histR.rows(); i++) {
|
||||
for(int j=0; j<histR.cols(); j++) {
|
||||
dataR[k] = histR.get(i, j)[0];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
return dataR;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
package ru.delkom07.improc.geometry;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.opencv.core.MatOfPoint;
|
||||
import org.opencv.core.Point;
|
||||
|
||||
import ru.delkom07.util.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Komyshev.
|
||||
* v.0.1
|
||||
*/
|
||||
public class GeometricFunctions {
|
||||
|
||||
public static double euclideanDistance(double[] p1, double[] p2) {
|
||||
double sum = 0;
|
||||
for(int i=0; i<p1.length || i<p2.length; i++) {
|
||||
sum += Math.pow(p1[i]-p2[i], 2);
|
||||
}
|
||||
|
||||
return Math.sqrt(sum);
|
||||
}
|
||||
|
||||
|
||||
public static double distance(Point p1, Point p2) {
|
||||
double dx = Math.abs(p1.x-p2.x);
|
||||
double dy = Math.abs(p1.y-p2.y);
|
||||
return Math.sqrt(dx*dx+dy*dy);
|
||||
}
|
||||
|
||||
|
||||
public static double length(Vector vector) {
|
||||
double dx = Math.abs(vector.x1()-vector.x2());
|
||||
double dy = Math.abs(vector.y1()-vector.y2());
|
||||
return Math.sqrt(dx*dx+dy*dy);
|
||||
}
|
||||
|
||||
|
||||
public static Point getMiddlePoint(Point p1, Point p2) {
|
||||
return new Point((p1.x+p2.x)/2, (p1.y+p2.y)/2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static double[] getMassCenter(MatOfPoint contour) {
|
||||
// mass center
|
||||
double[] massCenter = new double[2];
|
||||
massCenter[0] = 0.0;
|
||||
massCenter[1] = 0.0;
|
||||
int count = 0;
|
||||
for(int i=0; i<contour.rows(); i++) {
|
||||
for(int j=0; j<contour.cols(); j++) {
|
||||
double[] point = contour.get(i, j);
|
||||
massCenter[0] += point[0];
|
||||
massCenter[1] += point[1];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
massCenter[0] = massCenter[0]/(double)count;
|
||||
massCenter[1] = massCenter[1]/(double)count;
|
||||
|
||||
return massCenter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Vector[] getMainComponents(MatOfPoint contour) {
|
||||
double[] massCenter = getMassCenter(contour);
|
||||
|
||||
Vector minAxis = new Vector(massCenter[0], massCenter[1], contour.get(0, 0)[0], contour.get(0, 0)[1]);
|
||||
double[] minInertionMoment = new double[2];
|
||||
minInertionMoment[0] = Double.MAX_VALUE;
|
||||
minInertionMoment[1] = Double.MAX_VALUE;
|
||||
|
||||
for(int i=0; i<contour.rows(); i++) {
|
||||
for(int j=0; j<contour.cols(); j++) {
|
||||
Vector potentialAxis = new Vector(massCenter[0], massCenter[1], contour.get(i, j)[0], contour.get(i, j)[1]);
|
||||
|
||||
double A = potentialAxis.y1()-potentialAxis.y2();
|
||||
double B = potentialAxis.x2()-potentialAxis.x1();
|
||||
double C = potentialAxis.x1()*potentialAxis.y2() - potentialAxis.x2()*potentialAxis.y1();
|
||||
|
||||
double[] inertionMoment = new double[2];
|
||||
for(int ii=0; ii<contour.rows(); ii++) {
|
||||
for(int jj=0; jj<contour.cols(); jj++) {
|
||||
double[] point = contour.get(ii, jj);
|
||||
|
||||
double[] pointOnAxis = new double[2];
|
||||
pointOnAxis[0] = ( B*(B*point[0]-A*point[1] )-A*C) / (A*A+B*B);
|
||||
pointOnAxis[1] = ( A*(-1*B*point[0]+A*point[1] )-B*C) / (A*A+B*B);
|
||||
|
||||
double d = Math.abs(A*point[0] + B*point[1] + C) / Math.sqrt(A*A + B*B);
|
||||
|
||||
inertionMoment[0] += d*d*pointOnAxis[0];
|
||||
inertionMoment[1] += d*d*pointOnAxis[1];
|
||||
}
|
||||
}
|
||||
|
||||
if(Math.sqrt(inertionMoment[0]*inertionMoment[0]+inertionMoment[1]*inertionMoment[1])
|
||||
< Math.sqrt(minInertionMoment[0]*minInertionMoment[0]+minInertionMoment[1]*minInertionMoment[1])) {
|
||||
minAxis = potentialAxis;
|
||||
minInertionMoment[0] = inertionMoment[0];
|
||||
minInertionMoment[1] = inertionMoment[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Vector[]{minAxis, new Vector(minAxis.x1(), minAxis.y1(), minAxis.x1()+minAxis.y(), minAxis.y1()-minAxis.x())};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Point[] findMaxDistantPoints(Point[] pointArr) {
|
||||
Point maxP1 = pointArr[0];
|
||||
Point maxP2 = pointArr[0];
|
||||
double maxDistance = 0;
|
||||
|
||||
for(Point p1: pointArr) {
|
||||
for(Point p2: pointArr) {
|
||||
if(GeometricFunctions.distance(p1, p2) > maxDistance) {
|
||||
maxP1 = p1;
|
||||
maxP2 = p2;
|
||||
maxDistance = GeometricFunctions.distance(p1, p2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Point[]{maxP1, maxP2};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Вернить две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
|
||||
* Функция сначала находит список точек претендентов: наиболее близкие к линии точки по pointBufferSize справа и слева.
|
||||
* После этого для каждой стороны выбирается наиболее близкая точка к нормали.
|
||||
* @param listOfPoints - списко точек.
|
||||
* @param line - данная линия.
|
||||
* @param normal - нормаль.
|
||||
* @return две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
|
||||
*/
|
||||
/*
|
||||
public static Point[] getTwoPointThatLayOnGivenLine(List<Point> listOfPoints, Vector line, Vector normal) {
|
||||
short pointBufferSize = 5;
|
||||
|
||||
// наборы правых и левых точек контура относительно normal, лежащие близко к line
|
||||
List<Pair<Point, Double>> leftPoints = new LinkedList<Pair<Point, Double>>();
|
||||
List<Pair<Point, Double>> rightPoints = new LinkedList<Pair<Point, Double>>();
|
||||
|
||||
for(Point point : listOfPoints) {
|
||||
double xDiff = (line.x2() - line.x1());
|
||||
double yDiff = (line.y2() - line.y1());
|
||||
|
||||
xDiff = xDiff == 0 ? 0.0001 : xDiff; // STUB
|
||||
yDiff = yDiff == 0 ? 0.0001 : yDiff; // STUB
|
||||
|
||||
// расстояние от точки до прямой line
|
||||
double closenessToLine = Math.abs((point.x - line.x1())/xDiff - (point.y - line.y1())/yDiff);
|
||||
//double closenessToLine = Math.abs((point.x - line.x1())/(line.x2() - line.x1()) - (point.y - line.y1())/(line.y2()-line.y1()));
|
||||
|
||||
// position relative to the normal
|
||||
// (y1-y2)*x+(x2-x1)*y+(x1*y2-x2*y1) > or < 0
|
||||
double position = (normal.y1()-normal.y2())*point.x + (normal.x2()-normal.x1()) * point.y + (normal.x1()*normal.y2()-normal.x2()*normal.y1());
|
||||
|
||||
// компаратор для пары Точка-Значение. сравнивает пары по значению.
|
||||
Comparator<Pair<Point, Double>> comparator = new Comparator<Pair<Point, Double>>() {
|
||||
|
||||
@Override
|
||||
public int compare(Pair<Point, Double> arg0, Pair<Point, Double> arg1) {
|
||||
if(arg0.getValue() > arg1.getValue()) {
|
||||
return 1;
|
||||
}
|
||||
if(arg0.getValue() < arg1.getValue()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
if(position <= 0) { // right position
|
||||
if(rightPoints.size() < pointBufferSize && !Double.isNaN(closenessToLine) && Double.isFinite(closenessToLine)) {
|
||||
rightPoints.add(new Pair<Point, Double>(point, closenessToLine));
|
||||
|
||||
if(rightPoints.size() == pointBufferSize) {
|
||||
rightPoints.sort(comparator);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int j=0; j<rightPoints.size(); j++) {
|
||||
Pair<Point, Double> rightPoint = rightPoints.get(j);
|
||||
|
||||
if(closenessToLine < rightPoint.getValue()) {
|
||||
rightPoints.add(rightPoints.indexOf(rightPoint), new Pair<Point, Double>(point, closenessToLine));
|
||||
rightPoints.remove(rightPoints.size()-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else { // left position
|
||||
if(leftPoints.size() < pointBufferSize && !Double.isNaN(closenessToLine) && Double.isFinite(closenessToLine)) {
|
||||
leftPoints.add(new Pair<Point, Double>(point, closenessToLine));
|
||||
|
||||
if(leftPoints.size() == pointBufferSize) {
|
||||
leftPoints.sort(comparator);
|
||||
}
|
||||
} else {
|
||||
for(int j=0; j<leftPoints.size(); j++) {
|
||||
Pair<Point, Double> leftPoint = leftPoints.get(j);
|
||||
|
||||
if(closenessToLine < leftPoint.getValue()) {
|
||||
leftPoints.add(leftPoints.indexOf(leftPoint), new Pair<Point, Double>(point, closenessToLine));
|
||||
leftPoints.remove(leftPoints.size()-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Point rightPoint = null;
|
||||
double rightBestDist = Double.MAX_VALUE;
|
||||
for(Pair<Point, Double> rightPointPair : rightPoints) {
|
||||
Point point = rightPointPair.getKey();
|
||||
|
||||
double xDiff = (normal.x2() - normal.x1());
|
||||
double yDiff = (normal.y2() - normal.y1());
|
||||
|
||||
xDiff = xDiff == 0 ? 0.0001 : xDiff;
|
||||
yDiff = yDiff == 0 ? 0.0001 : yDiff;
|
||||
|
||||
double closenessToNormal = Math.abs((point.x - normal.x1())/xDiff - (point.y - normal.y1())/yDiff);
|
||||
//double closenessToNormal = Math.abs((point.x - normal.x1())/(normal.x2() - normal.x1()) - (point.y - normal.y1())/(normal.y2()-normal.y1()));
|
||||
|
||||
if(closenessToNormal < rightBestDist) {
|
||||
rightPoint = point;
|
||||
rightBestDist = closenessToNormal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Point leftPoint = null;
|
||||
double leftBestDist = Double.MAX_VALUE;
|
||||
for(Pair<Point, Double> leftPointPair : leftPoints) {
|
||||
Point point = leftPointPair.getKey();
|
||||
|
||||
double xDiff = (normal.x2() - normal.x1());
|
||||
double yDiff = (normal.y2()- normal.y1());
|
||||
|
||||
xDiff = xDiff == 0 ? 0.0001 : xDiff;
|
||||
yDiff = yDiff == 0 ? 0.0001 : yDiff;
|
||||
|
||||
double closenessToNormal = Math.abs((point.x - normal.x1())/xDiff - (point.y - normal.y1())/yDiff);
|
||||
//double closenessToNormal = Math.abs((point.x - normal.x1())/(normal.x2() - normal.x1()) - (point.y - normal.y1())/(normal.y2()-normal.y1()));
|
||||
|
||||
if(closenessToNormal < leftBestDist) {
|
||||
leftPoint = point;
|
||||
leftBestDist = closenessToNormal;
|
||||
}
|
||||
}
|
||||
|
||||
return new Point[]{leftPoint, rightPoint};
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
public static Point[] getTwoPointThatLayOnGivenLine(List<Point> listOfPoints, Vector line, Vector normal) {
|
||||
// правые и левые точки контура относительно normal, лежащие близко к line
|
||||
Pair<Point, Double> rightPoint = null;
|
||||
Pair<Point, Double> leftPoint = null;
|
||||
|
||||
for(Point point : listOfPoints) {
|
||||
double xDiff = (line.x2() - line.x1());
|
||||
double yDiff = (line.y2() - line.y1());
|
||||
|
||||
//xDiff = xDiff == 0 ? 0.0001 : xDiff; // STUB
|
||||
//yDiff = yDiff == 0 ? 0.0001 : yDiff; // STUB
|
||||
|
||||
// расстояние от точки до прямой line
|
||||
//double closenessToLine = Math.abs((point.x - line.x1())/xDiff - (point.y - line.y1())/yDiff);
|
||||
|
||||
double closenessToLineX = xDiff != 0 ? (point.x - line.x1())/xDiff : 0;
|
||||
double closenessToLineY = yDiff != 0 ? (point.y - line.y1())/yDiff : 0;
|
||||
double closenessToLine = Math.abs(closenessToLineX - closenessToLineY);
|
||||
|
||||
//double closenessToLine = Math.abs((point.x - line.x1())/(line.x2() - line.x1()) - (point.y - line.y1())/(line.y2()-line.y1()));
|
||||
|
||||
// position relative to the normal
|
||||
// (y1-y2)*x+(x2-x1)*y+(x1*y2-x2*y1) > or < 0
|
||||
double position = (normal.y1()-normal.y2())*point.x + (normal.x2()-normal.x1()) * point.y + (normal.x1()*normal.y2()-normal.x2()*normal.y1());
|
||||
|
||||
if(Double.isNaN(closenessToLine) || !Double.isFinite(closenessToLine)) {
|
||||
continue;
|
||||
} else {
|
||||
if(position <= 0) { // right position
|
||||
if(null == rightPoint) {
|
||||
rightPoint = new Pair<Point, Double>(point, closenessToLine);
|
||||
} else {
|
||||
|
||||
if(closenessToLine < rightPoint.getRight()) {
|
||||
rightPoint = new Pair<Point, Double>(point, closenessToLine);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
if(null == leftPoint) {
|
||||
leftPoint = new Pair<Point, Double>(point, closenessToLine);
|
||||
} else {
|
||||
|
||||
if(closenessToLine < leftPoint.getRight()) {
|
||||
leftPoint = new Pair<Point, Double>(point, closenessToLine);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Point[]{leftPoint != null ? leftPoint.getLeft() : null, rightPoint != null ? rightPoint.getLeft() : null};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Vector normalize(Vector vector) {
|
||||
double length = length(vector);
|
||||
|
||||
double newX = vector.x()/length;
|
||||
double newY = vector.y()/length;
|
||||
|
||||
return new Vector(vector.x1(), vector.y1(), vector.x1()+newX, vector.y1()+newY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate of angle between given lines in degrees.
|
||||
* To calculate radians: *pi/180.
|
||||
* @param vect1 - first vector.
|
||||
* @param vect2 - second vector.
|
||||
* @return angle between given lines.
|
||||
*/
|
||||
public static double angle(Vector vect1, Vector vect2) {
|
||||
return Math.acos((vect1.x()*vect2.x()+vect1.y()*vect2.y())/(vect1.length()*vect2.length())) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Angle between two lines (without mean directions).
|
||||
* @param vect1 - first line.
|
||||
* @param vect2 - second line.
|
||||
* @return Angle between two lines.
|
||||
*/
|
||||
public static double absLineAngle(Vector vect1, Vector vect2) {
|
||||
double val = Math.abs(Math.acos((vect1.x()*vect2.x()+vect1.y()*vect2.y())/(vect1.length()*vect2.length())) * 180 / Math.PI);
|
||||
|
||||
return val < 90 ? val : 180 - val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate of angle between given lines in degrees.
|
||||
* To calculate radians: *pi/180.
|
||||
* @param line1 - first line.
|
||||
* @param line2 - second line.
|
||||
* @return angle between given lines.
|
||||
*/
|
||||
public static double angle(double[] line1, double[] line2) {
|
||||
double vect1X = Math.abs(line1[0]-line1[2]); // |x1 - x2|
|
||||
double vect1Y = Math.abs(line1[1]-line1[3]); // |y1 - y2|
|
||||
double length1 = vectLenght(vect1X, vect1Y);
|
||||
|
||||
double vect2X = Math.abs(line2[0]-line2[2]); // |x1 - x2|
|
||||
double vect2Y = Math.abs(line2[1]-line2[3]); // |y1 - y2|
|
||||
double length2 = vectLenght(vect2X, vect2Y);
|
||||
|
||||
return Math.acos((vect1X*vect2X+vect1Y*vect2Y)/(length1*length2)) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate of angle between given vector in degrees.
|
||||
* To calculate radians: *pi/180.
|
||||
* @param vect1 - first vector.
|
||||
* @param vect2 - second vector.
|
||||
* @return angle between given lines.
|
||||
*/
|
||||
public static double vectorAngle(double[] vect1, double[] vect2) {
|
||||
double vect1X = vect1[2]-vect1[0]; // x2 - x1
|
||||
double vect1Y = vect1[3]-vect1[1]; // y2 - y1
|
||||
double length1 = vectLenght(vect1X, vect1Y);
|
||||
|
||||
double vect2X = vect2[2]-vect2[0]; // x2 - x1
|
||||
double vect2Y = vect2[3]-vect2[1]; // y2 - y1
|
||||
double length2 = vectLenght(vect2X, vect2Y);
|
||||
|
||||
return Math.acos((vect1X*vect2X+vect1Y*vect2Y)/(length1*length2)) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
|
||||
public static double vectLenght(double x, double y) {
|
||||
return Math.sqrt(x*x+y*y);
|
||||
}
|
||||
|
||||
public static Point[] getTopLeftAndBottomRight(List<Point> points) {
|
||||
double minX = Integer.MAX_VALUE;
|
||||
double minY = Integer.MAX_VALUE;
|
||||
|
||||
double maxX = -1;
|
||||
double maxY = -1;
|
||||
for(Point point : points) {
|
||||
if(point.x < minX) {
|
||||
minX = point.x;
|
||||
}
|
||||
|
||||
if(point.y < minY) {
|
||||
minY = point.y;
|
||||
}
|
||||
|
||||
if(point.x > maxX) {
|
||||
maxX = point.x;
|
||||
}
|
||||
|
||||
if(point.y > maxY) {
|
||||
maxY = point.y;
|
||||
}
|
||||
}
|
||||
|
||||
return new Point[] {new Point(minX, minY), new Point(maxX, maxY)};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* public static Point[] getTwoPointThatLayOnGivenLine_(List<Point> listOfPoints, Vector line) {
|
||||
Point point1 = null;
|
||||
Point point2 = null;
|
||||
Point prevPoint = listOfPoints.get(0);
|
||||
|
||||
boolean rele = false;
|
||||
|
||||
double minEstimation = Double.MAX_VALUE;
|
||||
for(Point point : listOfPoints) {
|
||||
double estimation = Math.abs((point.x - line.x1())/(line.x2() - line.x1()) - (point.y - line.y1())/(line.y2()-line.y1()));
|
||||
|
||||
if(estimation < minEstimation && distance(prevPoint, point)>10) { // ?????? distance it is not good decision
|
||||
minEstimation= estimation;
|
||||
|
||||
if(rele) {
|
||||
point1 = point;
|
||||
} else {
|
||||
point2 = point;
|
||||
}
|
||||
rele = !rele;
|
||||
prevPoint = point;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Point[]{point1, point2};
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,103 @@
|
||||
package ru.delkom07.improc.geometry;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
public class Vector {
|
||||
private Double x;
|
||||
private Double y;
|
||||
|
||||
private Double x1;
|
||||
private Double y1;
|
||||
private Double x2;
|
||||
private Double y2;
|
||||
|
||||
boolean coordinateAssigned = false;
|
||||
|
||||
|
||||
|
||||
public Vector(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
coordinateAssigned = false;
|
||||
}
|
||||
|
||||
public Vector(double x1, double y1, double x2, double y2) {
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
|
||||
this.x = x2-x1;
|
||||
this.y = y2-y1;
|
||||
|
||||
coordinateAssigned = true;
|
||||
}
|
||||
|
||||
public Double x() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public Double y() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public Double x1() {
|
||||
if(!coordinateAssigned)
|
||||
throw new RuntimeException("Coordinate not assigned");
|
||||
|
||||
return x1;
|
||||
}
|
||||
|
||||
public Double y1() {
|
||||
if(!coordinateAssigned)
|
||||
throw new RuntimeException("Coordinate not assigned");
|
||||
|
||||
return y1;
|
||||
}
|
||||
|
||||
public Double x2() {
|
||||
if(!coordinateAssigned)
|
||||
throw new RuntimeException("Coordinate not assigned");
|
||||
|
||||
return x2;
|
||||
}
|
||||
|
||||
public Double y2() {
|
||||
if(!coordinateAssigned)
|
||||
throw new RuntimeException("Coordinate not assigned");
|
||||
|
||||
return y2;
|
||||
}
|
||||
|
||||
public void setPoint1(Point p1) {
|
||||
coordinateAssigned = true;
|
||||
this.x1 = p1.x;
|
||||
this.y1 = p1.y;
|
||||
|
||||
this.x = x2-x1;
|
||||
this.y = y2-y1;
|
||||
}
|
||||
|
||||
public void setPoint2(Point p2) {
|
||||
coordinateAssigned = true;
|
||||
|
||||
this.x2 = p2.x;
|
||||
this.y2 = p2.y;
|
||||
|
||||
this.x = x2-x1;
|
||||
this.y = y2-y1;
|
||||
}
|
||||
|
||||
public Double length() {
|
||||
return Math.sqrt(x*x+y*y);
|
||||
}
|
||||
|
||||
public void draw(Mat img, Scalar color) {
|
||||
Imgproc.line(img, new Point(x1(), y1()), new Point(x2(), y2()), color, 1);
|
||||
//Imgproc.circle(img, new Point(x2(), y2()), 10, color, -1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
package ru.delkom07.improc.texture;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
* Realization of Gray level co-occurrence matrix.
|
||||
*
|
||||
* В текущей версии вычисляется матрица для всех основных принципиальных направлений (в определении матрицы GLCM это параметр, как и d).
|
||||
*
|
||||
* @author Komyshev
|
||||
*/
|
||||
public class GLCM {
|
||||
int Nx; // ширина изображения (параметр матрицы GLCM)
|
||||
int Ny; // высота изображения (параметр матрицы GLCM)
|
||||
|
||||
int Ng = 256; // квантификация уровней серого (параметр матрицы GLCM)
|
||||
int d = 1; // расстояние (параметр матрицы GLCM)
|
||||
|
||||
int[][] P; // ненормализованная матрица GLCM
|
||||
double[][] p; // нормализованная матрица GLCM
|
||||
|
||||
boolean calculated = false;
|
||||
|
||||
// вычисление
|
||||
// все вычисляемые направления (параметр матрицы GLCM)
|
||||
static final int directions[][] = {
|
||||
{0, 1}, {0, -1}, // by vertical
|
||||
{1, 0}, {-1, 0}, // by horizontal
|
||||
|
||||
{1, 1}, {-1, -1}, // 1 diagonal
|
||||
{1, -1}, {-1, 1}, // 2 diagonal
|
||||
};
|
||||
|
||||
|
||||
public GLCM() {}
|
||||
|
||||
public GLCM(int d) {
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
public GLCM(int Ng, int d) {
|
||||
this.Ng = Ng;
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
|
||||
public GLCM calculate(Mat gray) {
|
||||
P = new int[Ng][Ng];
|
||||
p = new double[Ng][Ng];
|
||||
|
||||
Nx = gray.cols();
|
||||
Ny = gray.rows();
|
||||
|
||||
initP(gray);
|
||||
|
||||
// (3*) нормализация
|
||||
int C = 2*Nx*(Ny - 1) + 2*Ny*(Nx - 1) + 4*(Nx - 1)*(Ny - 1);
|
||||
normalizeP(C);
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* (1*) Инициализировать матрицу P (ненормализованную) - посчитать число соотвутствующих пар пикселей.
|
||||
* @param gray
|
||||
*/
|
||||
private void initP(Mat gray) {
|
||||
for(int x=0; x<Nx; x++) {
|
||||
for(int y=0; y<Ny; y++) {
|
||||
int iPix = (int) gray.get(y, x)[0];
|
||||
|
||||
// loop for directions
|
||||
for(int directInd=0; directInd<directions.length; directInd++) {
|
||||
int nextX = x + d*directions[directInd][0];
|
||||
int nextY = y + d*directions[directInd][1];
|
||||
|
||||
if(nextX >= 0 && nextX < Nx && nextY >= 0 && nextY < Ny) {
|
||||
int jPix = (int) gray.get(nextY, nextX)[0];
|
||||
|
||||
P[iPix][jPix]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (2*) Вычислить нормализованную GLCM матрицу.
|
||||
*/
|
||||
void normalizeP(int C) {
|
||||
for(int i=0; i<Ng; i++) {
|
||||
for(int j=0; j<Ng; j++) {
|
||||
p[i][j] = P[i][j] / (double) C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Получить не нормализованную GLCM матрицу (числа сооветствующих пар пикселей).
|
||||
*/
|
||||
public int[][] getNonNormilizedP() {
|
||||
initCheck();
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Получить нормализованную GLCM матрицу.
|
||||
*/
|
||||
public double[][] getNormilizedP() {
|
||||
initCheck();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void initCheck() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("The descriptor is not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Texture characteristics based on GLCM.
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* (4*) Mean.
|
||||
* @return
|
||||
*/
|
||||
public double mean() {
|
||||
initCheck();
|
||||
|
||||
double mu = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
mu += i*p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return mu;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (5*) Variance.
|
||||
* @return
|
||||
*/
|
||||
public double variance() {
|
||||
initCheck();
|
||||
|
||||
double sigma2 = 0;
|
||||
double mu = mean();
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
sigma2 += Math.pow(i-mu, 2) * p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return sigma2;
|
||||
}
|
||||
|
||||
/**
|
||||
* (6*) Uniformity.
|
||||
* @return
|
||||
*/
|
||||
public double uniformity() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
res += p[i-1][j-1] * p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* (7*) Entropy.
|
||||
* @return
|
||||
*/
|
||||
public double entropy() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
if(0 != p[i-1][j-1]) // *quantification check
|
||||
res -= p[i-1][j-1] * Math.log(p[i-1][j-1]);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* (8*) Maximum probability.
|
||||
* @return
|
||||
*/
|
||||
public double maxProbability() {
|
||||
initCheck();
|
||||
|
||||
double max = p[0][0];
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
if(p[i-1][j-1] > max) {
|
||||
max = p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* (9*) Correlation.
|
||||
* @return
|
||||
*/
|
||||
public double correlation() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
double mu = mean();
|
||||
double sigma2 = variance();
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
res += ( ( (i-mu)*(j-mu) )/sigma2 ) * p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (10*) Homogeneity.
|
||||
* @return
|
||||
*/
|
||||
public double homogeneity() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
res += ( 1 / (1 + Math.pow(i-j, 2) ) ) * p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* (11*) Inertia.
|
||||
* @return
|
||||
*/
|
||||
public double inertia() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
res += Math.pow(i-j, 2) * p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* (12*) Cluster shade.
|
||||
* @return
|
||||
*/
|
||||
public double clusterShade() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
double mu = mean();
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
res += Math.pow(i + j - 2*mu, 3) * p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* (13*) Cluster prominance.
|
||||
* @return
|
||||
*/
|
||||
public double clusterProminance() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
double mu = mean();
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Ng; j++) {
|
||||
res += Math.pow(i + j - 2*mu, 4) * p[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// == Вывод ==
|
||||
public static String[] ChNames() {
|
||||
return new String[] {
|
||||
"mean", "variance",
|
||||
"uniformity", "entropy", "maxProbability", "correlation",
|
||||
"homogeneity", "inertia", "clusterShade", "clusterProminance"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Вывести дескриптор как массив характеристик.
|
||||
* @return массив характеристик дескриптора.
|
||||
*/
|
||||
public double[] toArray() {
|
||||
return new double[] {
|
||||
mean(), variance(),
|
||||
uniformity(), entropy(), maxProbability(), correlation(),
|
||||
homogeneity(), inertia(), clusterShade(), clusterProminance()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Вывести дескриптор как строку.
|
||||
* @return строковое представление дескриптора.
|
||||
*/
|
||||
public String toTsvCsvString(String sep) {
|
||||
List<Double> list = Arrays.stream(toArray()).boxed().collect(Collectors.toList());
|
||||
|
||||
String joined = list.stream()
|
||||
.map(d -> d.toString())
|
||||
.collect(Collectors.joining(sep));
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
package ru.delkom07.improc.texture;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
* Realization of Gray level run length matrix.
|
||||
*
|
||||
* @author Komyshev
|
||||
*/
|
||||
public class GLRM {
|
||||
int Ng = 256; // квантификация уровней серого
|
||||
int Nr = 4; // максимальная длина серий уровней серого, определяемая в данной матрице
|
||||
|
||||
int[][] q; // матрица GLRM
|
||||
int R; // сумма матрицы GLRM
|
||||
|
||||
boolean calculated = false; // флаг инициализации дескриптора для проверки перед запросом характеристик или матриц
|
||||
|
||||
|
||||
/**
|
||||
* Основной конструктор.
|
||||
* @param maxLength - максимальная учитываемая длина серии.
|
||||
*/
|
||||
public GLRM(int maxLength) {
|
||||
this.Nr = maxLength;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Конструктор с указанием квантификации уровня серого.
|
||||
* @param Ng - квантификация уровней серого.
|
||||
* @param maxLength - максимальная учитываемая длина серии.
|
||||
*/
|
||||
public GLRM(int Ng, int maxLength) {
|
||||
this.Ng = Ng;
|
||||
this.Nr = maxLength;
|
||||
}
|
||||
|
||||
|
||||
public GLRM calculate(Mat gray) {
|
||||
q = new int[Ng][Nr];
|
||||
|
||||
countRunsOn_0(gray);
|
||||
countRunsOn_90(gray);
|
||||
countRunsOn_135(gray);
|
||||
countRunsOn_45(gray);
|
||||
|
||||
R = calculateR();
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Посчитать сериии по горизонтали (слева на право).
|
||||
* @param gray - изображение в градациях серого.
|
||||
*/
|
||||
private void countRunsOn_0(Mat gray) {
|
||||
for(int y=0; y<gray.rows(); y++) { // for rows
|
||||
|
||||
int curLvl = (int) gray.get(y, 0)[0]; // base point
|
||||
int curLen = 1;
|
||||
|
||||
for(int x=1;; x++) { // for cols
|
||||
if(x >= gray.cols()) {
|
||||
count(curLvl, curLen);
|
||||
break;
|
||||
}
|
||||
|
||||
int nextPoint = (int) gray.get(y, x)[0];
|
||||
|
||||
if(curLvl != nextPoint) {
|
||||
count(curLvl, curLen);
|
||||
|
||||
// flash
|
||||
curLvl = nextPoint;
|
||||
curLen = 1;
|
||||
|
||||
} else {
|
||||
curLen++;
|
||||
}
|
||||
|
||||
} // end for cols
|
||||
|
||||
} // end for rows
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Посчитать серии по вертикали (сверху вниз).
|
||||
* @param gray - изображение в градациях серого.
|
||||
*/
|
||||
private void countRunsOn_90(Mat gray) {
|
||||
for(int x=0; x<gray.cols(); x++) { // for cols
|
||||
|
||||
int curLvl = (int) gray.get(0, x)[0]; // base point
|
||||
int curLen = 1;
|
||||
|
||||
for(int y=1;; y++) { // for rows
|
||||
if(y>=gray.rows()) {
|
||||
count(curLvl, curLen);
|
||||
break;
|
||||
}
|
||||
|
||||
int nextPoint = (int) gray.get(y, x)[0];
|
||||
|
||||
if(curLvl != nextPoint) {
|
||||
count(curLvl, curLen);
|
||||
|
||||
// flash
|
||||
curLvl = nextPoint;
|
||||
curLen = 1;
|
||||
} else {
|
||||
curLen++;
|
||||
}
|
||||
|
||||
} // end for rows
|
||||
|
||||
} // end for cols
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* По диагонали слева-сверху -> вправо-вниз
|
||||
* @param gray
|
||||
*/
|
||||
private void countRunsOn_135(Mat gray) {
|
||||
for(int c=0; c<gray.cols(); c++) { // for cols
|
||||
int curX = c;
|
||||
int curY = 0;
|
||||
|
||||
countDiag(gray, curX, curY, 1, 1);
|
||||
}
|
||||
|
||||
for(int r=1; r<gray.rows(); r++) { // for rows
|
||||
int curX = 0;
|
||||
int curY = r;
|
||||
|
||||
countDiag(gray, curX, curY, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* По диагонали слева-снизу -> вправо-вверх
|
||||
* @param gray
|
||||
*/
|
||||
private void countRunsOn_45(Mat gray) {
|
||||
for(int r=0; r<gray.rows(); r++) { // for rows
|
||||
int curX = 0;
|
||||
int curY = r;
|
||||
|
||||
countDiag(gray, curX, curY, 1, -1);
|
||||
}
|
||||
|
||||
for(int c=1; c<gray.cols(); c++) { // for cols
|
||||
int curX = c;
|
||||
int curY = gray.rows()-1;
|
||||
|
||||
countDiag(gray, curX, curY, 1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Посчитать серии по диагонали.
|
||||
* @param gray - изображение в градациях серого.
|
||||
* @param curX - стартовый пиксель (X).
|
||||
* @param curY - стартовый пиксель (Y).
|
||||
* @param dx - смещение по X.
|
||||
* @param dy - смещение по Y.
|
||||
*/
|
||||
private void countDiag(Mat gray, int curX, int curY, int dx, int dy) {
|
||||
int curLvl = (int) gray.get(curY, curX)[0]; // base point
|
||||
int curLen = 1;
|
||||
|
||||
for(;;) { // for diag
|
||||
int nextX = curX + dx*curLen;
|
||||
int nextY = curY + dy*curLen;
|
||||
|
||||
// выход за пределы изображения
|
||||
if(nextX < 0 || nextX >= gray.cols() || nextY < 0 || nextY >= gray.rows()) {
|
||||
count(curLvl, curLen);
|
||||
break;
|
||||
}
|
||||
|
||||
int nextLvl = (int) gray.get(nextY, nextX)[0];
|
||||
|
||||
if(curLvl != nextLvl) { // уровень серого изменился
|
||||
count(curLvl, curLen);
|
||||
|
||||
// flash
|
||||
curLvl = nextLvl;
|
||||
curLen = 1;
|
||||
|
||||
curX = nextX;
|
||||
curY = nextY;
|
||||
} else { // новый уровень серого
|
||||
curLen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void count(int lvl, int len) {
|
||||
int lenPos = len <= Nr ? len-1 : Nr-1;
|
||||
q[lvl][lenPos]++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Вычислить делитель нормализации.
|
||||
* По сути просто суммирует матрицу q.
|
||||
* (20*)
|
||||
* @return делитель нормализации.
|
||||
*/
|
||||
int calculateR() {
|
||||
|
||||
int res = 0;
|
||||
|
||||
for(int i=0; i<Ng; i++) {
|
||||
for(int j=0; j<Nr; j++) {
|
||||
res += q[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Получить матрицу GLRM.
|
||||
* @return матрица GLRM
|
||||
*/
|
||||
public int[][] getQ() {
|
||||
initCheck();
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Триггер на обращение к методам неинициализированного класса.
|
||||
*/
|
||||
private void initCheck() {
|
||||
if(!calculated) {
|
||||
throw new RuntimeException("The descriptor is not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Texture characteristics based on GLRM.
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* (14*)
|
||||
* @return
|
||||
*/
|
||||
public double shortRun() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Nr; j++) {
|
||||
res += q[i-1][j-1]/(j*j);
|
||||
}
|
||||
}
|
||||
|
||||
return res / (double) R;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (15*)
|
||||
* @return
|
||||
*/
|
||||
public double longRun() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Nr; j++) {
|
||||
res += (j*j) * q[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return res / (double) R;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (16*)
|
||||
* @return
|
||||
*/
|
||||
public double grayLevelNonUniformity() {
|
||||
initCheck();
|
||||
|
||||
double sumI = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
double sumJ = 0;
|
||||
|
||||
for(int j=1; j<=Nr; j++) {
|
||||
sumJ += q[i-1][j-1];
|
||||
}
|
||||
|
||||
sumI += sumJ*sumJ;
|
||||
}
|
||||
|
||||
return sumI / (double) R;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (17*)
|
||||
* @return
|
||||
*/
|
||||
public double runLengthNonUniformity() {
|
||||
initCheck();
|
||||
|
||||
double sumJ = 0;
|
||||
|
||||
for(int j=1; j<=Nr; j++) {
|
||||
double sumI = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
sumI += q[i-1][j-1];
|
||||
}
|
||||
|
||||
sumJ += sumI*sumI;
|
||||
}
|
||||
|
||||
return sumJ / (double) R;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (18*)
|
||||
* @return
|
||||
*/
|
||||
public double runRatio() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Nr; j++) {
|
||||
res += j*q[i-1][j-1];
|
||||
}
|
||||
}
|
||||
|
||||
return (double) R / res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (19*)
|
||||
* @return
|
||||
*/
|
||||
public double entropy() {
|
||||
initCheck();
|
||||
|
||||
double res = 0;
|
||||
|
||||
for(int i=1; i<=Ng; i++) {
|
||||
for(int j=1; j<=Nr; j++) {
|
||||
if(0 != q[i-1][j-1]) // *quantification check
|
||||
res += q[i-1][j-1] * Math.log(q[i-1][j-1]);
|
||||
}
|
||||
}
|
||||
|
||||
return res / (double) R;
|
||||
}
|
||||
|
||||
|
||||
// == Вывод ==
|
||||
public static String[] ChNames() {
|
||||
return new String[] {
|
||||
"shortRun", "longRun", "grayLevelNonUniformity", "runLengthNonUniformity", "runRatio", "entropy"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Вывести дескриптор как массив характеристик.
|
||||
* @return массив характеристик дескриптора.
|
||||
*/
|
||||
public double[] toArray() {
|
||||
return new double[] {
|
||||
shortRun(), longRun(), grayLevelNonUniformity(), runLengthNonUniformity(), runRatio(), entropy()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Вывести дескриптор как строку.
|
||||
* @return строковое представление дескриптора.
|
||||
*/
|
||||
public String toTsvCsvString(String sep) {
|
||||
List<Double> list = Arrays.stream(toArray()).boxed().collect(Collectors.toList());
|
||||
|
||||
String joined = list.stream()
|
||||
.map(d -> d.toString())
|
||||
.collect(Collectors.joining(sep));
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package ru.delkom07.improc.texture;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
* Realization of Gray level co-occurrence matrix with masks.
|
||||
* При вычислении учитываются только пиксели соотвествующие не нулевым пикселя маски.
|
||||
* При встрече нулевого пикселя маски рассматривается такое же поведение, как выход за границы изображения.
|
||||
*
|
||||
* В текущей версии вычисляется матрица для всех основных принципиальных направлений (в определении матрицы GLCM это параметр, как и d).
|
||||
*
|
||||
* @author Komyshev
|
||||
*/
|
||||
public class MaskedGLCM extends GLCM {
|
||||
Mat mask;
|
||||
|
||||
public MaskedGLCM() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public MaskedGLCM(int d) {
|
||||
super(d);
|
||||
}
|
||||
|
||||
|
||||
public MaskedGLCM(int Ng, int d) {
|
||||
super(Ng, d);
|
||||
}
|
||||
|
||||
|
||||
public MaskedGLCM calculate(Mat gray, Mat mask) {
|
||||
P = new int[Ng][Ng];
|
||||
p = new double[Ng][Ng];
|
||||
|
||||
Nx = gray.cols();
|
||||
Ny = gray.rows();
|
||||
|
||||
initP(gray, mask);
|
||||
|
||||
// (3*) нормализация
|
||||
int C = 2*Nx*(Ny - 1) + 2*Ny*(Nx - 1) + 4*(Nx - 1)*(Ny - 1);
|
||||
normalizeP(C);
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* (1*) Инициализировать матрицу P (ненормализованную) - посчитать число соотвутствующих пар пикселей.
|
||||
* @param gray
|
||||
*/
|
||||
void initP(Mat gray, Mat mask) {
|
||||
for(int x=0; x<Nx; x++) {
|
||||
for(int y=0; y<Ny; y++) {
|
||||
if(0 != mask.get(y, x)[0]) { // only mask pixels
|
||||
|
||||
int iPix = (int) gray.get(y, x)[0];
|
||||
|
||||
// loop for directions
|
||||
for(int directInd=0; directInd<directions.length; directInd++) {
|
||||
int nextX = x + d*directions[directInd][0];
|
||||
int nextY = y + d*directions[directInd][1];
|
||||
|
||||
if(nextX >= 0 && nextX < Nx && nextY >= 0 && nextY < Ny) {
|
||||
|
||||
if(0 != mask.get(nextY, nextX)[0]) { // only mask pixels
|
||||
int jPix = (int) gray.get(nextY, nextX)[0];
|
||||
|
||||
P[iPix][jPix]++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package ru.delkom07.improc.texture;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
* Realization of Gray level run length matrix with masks.
|
||||
* При вычислении учитываются только пиксели соотвествующие не нулевым пикселя маски.
|
||||
* При встрече нулевого пикселя маски рассматривается такое же поведение, как выход за границы изображения.
|
||||
* @author Komyshev
|
||||
*/
|
||||
public class MaskedGLRM extends GLRM {
|
||||
/**
|
||||
* Основной конструктор.
|
||||
* @param maxLength - максимальная учитываемая длина серии.
|
||||
*/
|
||||
public MaskedGLRM(int maxLength) {
|
||||
super(maxLength);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Конструктор с указанием квантификации уровня серого.
|
||||
* @param Ng - квантификация уровней серого.
|
||||
* @param maxLength - максимальная учитываемая длина серии.
|
||||
*/
|
||||
public MaskedGLRM(int Ng, int maxLength) {
|
||||
super(Ng, maxLength);
|
||||
}
|
||||
|
||||
|
||||
public MaskedGLRM calculate(Mat gray, Mat mask) {
|
||||
q = new int[Ng][Nr];
|
||||
|
||||
countRunsOn_0(gray, mask);
|
||||
countRunsOn_90(gray, mask);
|
||||
countRunsOn_135(gray, mask);
|
||||
countRunsOn_45(gray, mask);
|
||||
|
||||
R = calculateR();
|
||||
|
||||
calculated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Посчитать сериии по горизонтали (слева на право).
|
||||
* @param gray - изображение в градациях серого.
|
||||
*/
|
||||
private void countRunsOn_0(Mat gray, Mat mask) {
|
||||
for(int y=0; y<gray.rows(); y++) { // for rows
|
||||
|
||||
if(0 == mask.get(y, 0)[0]) { continue; } // only mask pixels
|
||||
|
||||
int curLvl = (int) gray.get(y, 0)[0]; // base point
|
||||
int curLen = 1;
|
||||
|
||||
for(int x=1;; x++) { // for cols
|
||||
if(x >= gray.cols()) {
|
||||
count(curLvl, curLen);
|
||||
break;
|
||||
}
|
||||
|
||||
int nextPoint = (int) gray.get(y, x)[0];
|
||||
int nextMaskPoint = (int) mask.get(y, x)[0]; // only mask pixels
|
||||
|
||||
if(0 == nextMaskPoint || curLvl != nextPoint) { // only mask pixels
|
||||
count(curLvl, curLen);
|
||||
|
||||
// flash
|
||||
curLvl = nextPoint;
|
||||
curLen = 1;
|
||||
|
||||
} else {
|
||||
curLen++;
|
||||
}
|
||||
|
||||
} // end for cols
|
||||
|
||||
} // end for rows
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Посчитать серии по вертикали (сверху вниз).
|
||||
* @param gray - изображение в градациях серого.
|
||||
*/
|
||||
private void countRunsOn_90(Mat gray, Mat mask) {
|
||||
for(int x=0; x<gray.cols(); x++) { // for cols
|
||||
|
||||
if(0 == mask.get(0, x)[0]) { continue; } // only mask pixels
|
||||
|
||||
int curLvl = (int) gray.get(0, x)[0]; // base point
|
||||
int curLen = 1;
|
||||
|
||||
for(int y=1;; y++) { // for rows
|
||||
if(y>=gray.rows()) {
|
||||
count(curLvl, curLen);
|
||||
break;
|
||||
}
|
||||
|
||||
int nextPoint = (int) gray.get(y, x)[0];
|
||||
int nextMaskPoint = (int) mask.get(y, x)[0]; // only mask pixels
|
||||
|
||||
if(0 == nextMaskPoint || curLvl != nextPoint) { // only mask pixels
|
||||
count(curLvl, curLen);
|
||||
|
||||
// flash
|
||||
curLvl = nextPoint;
|
||||
curLen = 1;
|
||||
} else {
|
||||
curLen++;
|
||||
}
|
||||
|
||||
} // end for rows
|
||||
|
||||
} // end for cols
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* По диагонали слева-сверху -> вправо-вниз
|
||||
* @param gray
|
||||
*/
|
||||
private void countRunsOn_135(Mat gray, Mat mask) {
|
||||
for(int c=0; c<gray.cols(); c++) { // for cols
|
||||
int curX = c;
|
||||
int curY = 0;
|
||||
|
||||
countDiag(gray, mask, curX, curY, 1, 1);
|
||||
}
|
||||
|
||||
for(int r=1; r<gray.rows(); r++) { // for rows
|
||||
int curX = 0;
|
||||
int curY = r;
|
||||
|
||||
countDiag(gray, mask, curX, curY, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* По диагонали слева-снизу -> вправо-вверх
|
||||
* @param gray
|
||||
*/
|
||||
private void countRunsOn_45(Mat gray, Mat mask) {
|
||||
for(int r=0; r<gray.rows(); r++) { // for rows
|
||||
int curX = 0;
|
||||
int curY = r;
|
||||
|
||||
countDiag(gray, mask, curX, curY, 1, -1);
|
||||
}
|
||||
|
||||
for(int c=1; c<gray.cols(); c++) { // for cols
|
||||
int curX = c;
|
||||
int curY = gray.rows()-1;
|
||||
|
||||
countDiag(gray, mask, curX, curY, 1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Посчитать серии по диагонали.
|
||||
* @param gray - изображение в градациях серого.
|
||||
* @param mask - маска изображения.
|
||||
* @param curX - стартовый пиксель (X).
|
||||
* @param curY - стартовый пиксель (Y).
|
||||
* @param dx - смещение по X.
|
||||
* @param dy - смещение по Y.
|
||||
*/
|
||||
private void countDiag(Mat gray, Mat mask, int curX, int curY, int dx, int dy) {
|
||||
if(0 == mask.get(curY, curX)[0]) return; // only mask pixels
|
||||
|
||||
int curLvl = (int) gray.get(curY, curX)[0]; // base point
|
||||
int curLen = 1;
|
||||
|
||||
for(;;) { // for diag
|
||||
int nextX = curX + dx*curLen;
|
||||
int nextY = curY + dy*curLen;
|
||||
|
||||
// выход за пределы изображения
|
||||
if(nextX < 0 || nextX >= gray.cols() || nextY < 0 || nextY >= gray.rows()) {
|
||||
count(curLvl, curLen);
|
||||
break;
|
||||
}
|
||||
|
||||
int nextLvl = (int) gray.get(nextY, nextX)[0];
|
||||
int nextMaskLvl = (int) mask.get(nextY, nextX)[0];
|
||||
|
||||
if(0 == nextMaskLvl || curLvl != nextLvl) { // уровень серого изменился
|
||||
count(curLvl, curLen);
|
||||
|
||||
// flash
|
||||
curLvl = nextLvl;
|
||||
curLen = 1;
|
||||
|
||||
curX = nextX;
|
||||
curY = nextY;
|
||||
} else { // новый уровень серого
|
||||
curLen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package ru.delkom07.pendent;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Rect;
|
||||
|
||||
public class DiscreteCosineTransform_direct_wrong_realization {
|
||||
private Mat transformed;
|
||||
private static final double oneDivSqrt2 = 1.0/Math.sqrt(2.0);
|
||||
|
||||
public void transform(Mat img, Mat transformed, boolean inv) {
|
||||
this.transformed = transformed;
|
||||
|
||||
int blockSize = 8;
|
||||
int xBlocks = img.cols()/blockSize;
|
||||
int yBlocks = img.rows()/blockSize;
|
||||
|
||||
for(int xBlock=0; xBlock<xBlocks; xBlock++) {
|
||||
for(int yBlock=0; yBlock<yBlocks; yBlock++) {
|
||||
Rect rect = new Rect(xBlock*blockSize, yBlock*blockSize, blockSize, blockSize);
|
||||
|
||||
for(int i=rect.y; i<rect.y+rect.height; i++) {
|
||||
for(int j=rect.x; j<rect.x+rect.width; j++) {
|
||||
|
||||
if(!inv) {
|
||||
transformed.put(i, j, direct(i, j, rect, img));
|
||||
} else {
|
||||
transformed.put(i, j, inverse(i, j, rect, img));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Mat getTransformedImg() {
|
||||
return transformed;
|
||||
}
|
||||
|
||||
|
||||
private double direct(double i, double j, Rect rect, Mat img) {
|
||||
double val = 0;
|
||||
|
||||
double N = rect.height;
|
||||
double M = rect.width;
|
||||
|
||||
for(int y=rect.y; y<rect.y+rect.height; y++) {
|
||||
for(int x=rect.x; x<rect.x+rect.width; x++) {
|
||||
val += Math.cos( Math.PI*j*(2*y + 1) / (2*N) ) * Math.cos( Math.PI*i*(2*x + 1) / (2*M)) * img.get(y, x)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return Math.sqrt(2.0/M) * Math.sqrt(2.0/N) * lamda(i) * lamda(j) * val;
|
||||
}
|
||||
|
||||
|
||||
private double inverse(double x, double y, Rect rect, Mat img) {
|
||||
double val = 0;
|
||||
|
||||
double N = rect.height;
|
||||
double M = rect.width;
|
||||
|
||||
for(int i=rect.y; i<rect.y+rect.height; i++) {
|
||||
for(int j=rect.x; j<rect.x+rect.width; j++) {
|
||||
val += lamda(i) * lamda(j) * Math.cos( Math.PI*j*(2*y + 1.0) / (2*N) ) * Math.cos( Math.PI*i*(2*x + 1.0) / (2*M)) * img.get(i, j)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return Math.sqrt(2.0/M) * Math.sqrt(2.0/N) * val;
|
||||
}
|
||||
|
||||
|
||||
private double lamda(double e) {
|
||||
if(e == 0) {
|
||||
return oneDivSqrt2;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* This Java source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package ru.delkom07;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class LibraryTest {
|
||||
@Test void someLibraryMethodReturnsTrue() {
|
||||
Library classUnderTest = new Library();
|
||||
assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.delkom07;
|
||||
|
||||
public class Test {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This Java source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package ru.delkom07.improc.texture;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import nu.pattern.OpenCV;
|
||||
|
||||
/**
|
||||
* Тестовый класс для Gray level co-occurrence matrix.
|
||||
* @author Komyshev
|
||||
*/
|
||||
class GLCMTest {
|
||||
|
||||
@BeforeAll
|
||||
static void initOpenCV() {
|
||||
OpenCV.loadLocally();
|
||||
}
|
||||
|
||||
@Test void shouldReturnCorrectMatrix() {
|
||||
Mat gray = new Mat(new Size(4, 4), CvType.CV_8UC1);
|
||||
|
||||
byte[] bytes = new byte[] {
|
||||
0, 0, 3, 1,
|
||||
0, 1, 1, 1,
|
||||
2, 2, 3, 3,
|
||||
2, 2, 3, 1,
|
||||
};
|
||||
|
||||
gray.put(0, 0, bytes);
|
||||
|
||||
GLCM glcm = new GLCM(4, 1).calculate(gray);
|
||||
|
||||
int[][] P = glcm.getNonNormilizedP();
|
||||
int[][] expectedP = {
|
||||
{6, 4, 2, 1},
|
||||
{4, 8, 3, 12},
|
||||
{2, 3, 12, 4},
|
||||
{1, 12, 4, 6}
|
||||
};
|
||||
|
||||
assertEquals( Arrays.deepToString(P), Arrays.deepToString(expectedP));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void destroy() {}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This Java source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package ru.delkom07.improc.texture;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Size;
|
||||
import nu.pattern.OpenCV;
|
||||
|
||||
/**
|
||||
* Тестовый класс для Gray level run-length matrix.
|
||||
* @author Komyshev
|
||||
*/
|
||||
class GLRMTest {
|
||||
|
||||
@BeforeAll
|
||||
static void initOpenCV() {
|
||||
OpenCV.loadLocally();
|
||||
}
|
||||
|
||||
@Test void shouldReturnCorrectMatrix() {
|
||||
Mat gray = new Mat(new Size(4, 4), CvType.CV_8UC1);
|
||||
|
||||
byte[] bytes = new byte[] {
|
||||
0, 0, 3, 1,
|
||||
0, 1, 1, 1,
|
||||
2, 2, 3, 3,
|
||||
2, 2, 3, 1,
|
||||
};
|
||||
|
||||
gray.put(0, 0, bytes);
|
||||
|
||||
GLRM glrm = new GLRM(4, 4).calculate(gray);
|
||||
|
||||
int[][] q = glrm.getQ();
|
||||
int[][] expectedQ = {
|
||||
{6, 3, 0, 0},
|
||||
{13, 2, 1, 0},
|
||||
{4, 6, 0, 0},
|
||||
{10, 3, 0, 0}
|
||||
};
|
||||
|
||||
assertEquals( Arrays.deepToString(expectedQ), Arrays.deepToString(q));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void destroy() {}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user