This commit is contained in:
Evgeniy
2024-05-16 15:41:59 +07:00
commit 2dfee5edbe
151 changed files with 17349 additions and 0 deletions

View 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()
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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};
}
*/

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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]++;
}
}
}
}
}
}
}
}

View File

@@ -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++;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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'");
}
}

View File

@@ -0,0 +1,9 @@
package ru.delkom07;
public class Test {
public static void main(String[] args) {
}
}

View File

@@ -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() {}
}

View File

@@ -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() {}
}