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

39
utils/build.gradle Normal file
View File

@@ -0,0 +1,39 @@
/*
* 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'
implementation("org.openpnp:opencv:3.4.2-0")
// This dependency is exported to consumers, that is to say found on their compile classpath.
//api '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'
}
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,104 @@
package ru.delkom07.fileutils;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FileUtilities {
public static FileFilter imageFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.getName().matches(".+\\.(png|PNG|jpg|JPG|bmp|BMP)")) {
return true;
}
return false;
}
};
public static FileFilter jpgImageFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.getName().matches(".+\\.(jpg|JPG)")) {
return true;
}
return false;
}
};
public static FileFilter pngImageFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.getName().matches(".+\\.(png|PNG)")) {
return true;
}
return false;
}
};
public static FileFilter bmpImageFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.getName().matches(".+\\.(bmp|BMP)")) {
return true;
}
return false;
}
};
public static FileFilter fileFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.isFile()) {
return true;
}
return false;
}
};
public static FileFilter dirFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.isDirectory()) {
return true;
}
return false;
}
};
/**
* Получить все файлы, соответствующие fileFilter, из директории и поддиректорий рекурсивно.
* @param folder - корневая директория.
* @param fileFilter (can be null) - фильтр файлов.
* @return
*/
public static List<File> getFilesRecursively(File folder, FileFilter fileFilter) {
if(null == fileFilter) {
fileFilter = FileUtilities.fileFilter;
}
List<File> imagesList = new ArrayList<File>(Arrays.asList(folder.listFiles(fileFilter)));
File[] subFolders = folder.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.isDirectory())
return true;
return false;
}
});
for(File subFolder : subFolders) {
imagesList.addAll(getFilesRecursively(subFolder, fileFilter));
}
return imagesList;
}
}

View File

@@ -0,0 +1,25 @@
package ru.delkom07.fileutils;
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//FileUtils.compareTwoFouldersIgnoreFileNames(new File("../../wwork/2018_09_10_"), new File("../../wwork/2018_09_10_renamed_"));
//FileUtils.searchRepeatsInFolderIgnoreFileNames(new File("../data/img_renaming/renamed"));
//FileUtils.checkFilesForAnnotations(new File("../data/img_renaming/annotations.txt"), new File("../data/img_renaming/renamed"));
//FileUtils.checkAnnotationsForFiles(new File("../data/img_renaming/renamed"), new File[] {new File("../data/img_renaming/annotations.txt")});
//FileUtils.removeFileWithDoublecatesNames();
//FileUtils.renameFilesForAnnotations(new File("../data/img_renaming/annotations.csv"), new File("../data/img_renaming/renamed"));
//FileUtils.findAndCopyImgsFromList(new File("c:\\Users\\KomyshevEG\\Downloads\\renamed_19.12.18"), new File("d:\\workspace\\data\\wheat_ears\\input\\calibrated_problem"), new File("d:\\workspace\\data\\wheat_ears\\input\\calibrated_problem\\list.txt"));
}
}

View File

@@ -0,0 +1,185 @@
package ru.delkom07.geometry;
import java.util.Arrays;
import org.opencv.core.Core;
import org.opencv.core.Core.MinMaxLocResult;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.RotatedRect;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import ru.delkom07.geometry.obj.Circle;
public class AdditionalIndexies {
public static double circularityIndex(MatOfPoint contour) {
double area = Imgproc.contourArea(contour);
MatOfPoint2f contour2f = new MatOfPoint2f( contour.toArray() );
double perimeter = Imgproc.arcLength(contour2f, true);
return ( 4*Math.PI * area ) / (perimeter*perimeter);
}
public static double roundness(MatOfPoint contour) {
MatOfPoint2f forFillEllipse = new MatOfPoint2f();
contour.convertTo(forFillEllipse, CvType.CV_32FC2);
RotatedRect rotatedRect = Imgproc.fitEllipse(forFillEllipse);
double majorAxis = Math.max(rotatedRect.size.height, rotatedRect.size.width);
double area = Imgproc.contourArea(contour);
return (4*area) / (Math.PI * majorAxis*majorAxis); // (4*area) / (pi*[Major axis]^2)
}
public static double solidity(MatOfPoint contour) {
double area = Imgproc.contourArea(contour);
Point[] contourArr = contour.toArray();
MatOfInt hull = new MatOfInt();
Imgproc.convexHull(contour, hull);
int[] hullIntArr = hull.toArray();
Point[] hullArr = new Point[hullIntArr.length];
for(int i=0; i<hullIntArr.length; i++) {
hullArr[i] = contourArr[hullIntArr[i]];
}
MatOfPoint2f hull2f = new MatOfPoint2f(hullArr);
MatOfPoint2f approxContour = new MatOfPoint2f();
Imgproc.approxPolyDP(hull2f, approxContour, 0.001, true);
double hullArea = Imgproc.contourArea(approxContour);
return area/hullArea;
}
public static double rugosity(MatOfPoint contour) {
// perimeter
MatOfPoint2f contour2f = new MatOfPoint2f( contour.toArray() );
double perimeter = Imgproc.arcLength(contour2f, true);
MatOfInt hull = new MatOfInt();
Imgproc.convexHull(contour, hull);
// hull perimeter:
double hullPerimeter = 0;
int[] hullArr = hull.toArray();
Point[] contourArr = contour.toArray();
for(int i=0; i<hullArr.length-1; i++) {
Point curPoint = contourArr[hullArr[i]];
Point nextPoint = contourArr[hullArr[i+1]];
hullPerimeter += SimpleGeometry.distance(curPoint, nextPoint);
}
hullPerimeter += SimpleGeometry.distance(contourArr[contourArr.length-1], contourArr[0]);
return perimeter/hullPerimeter;
}
private static double maxInscribedMinCircumscribedCirclesDiametersRatio(MatOfPoint contour) {
Point center = new Point();
float[] enclosedCircleRadius = new float[1];
MatOfPoint2f contour2f = new MatOfPoint2f();
contour.convertTo(contour2f, CvType.CV_32FC2);
Imgproc.minEnclosingCircle(contour2f, center, enclosedCircleRadius);
Circle inscribedCircle = getMaxInscribedCircle(contour);
double inscribedCircleRadius = inscribedCircle.radius();
return enclosedCircleRadius[0]/inscribedCircleRadius;
}
public static Circle getMinCircumscribedCircle(MatOfPoint contour) {
Point center = new Point();
float[] enclosedCircleRadius = new float[1];
MatOfPoint2f contour2f = new MatOfPoint2f();
contour.convertTo(contour2f, CvType.CV_32FC2);
Imgproc.minEnclosingCircle(contour2f, center, enclosedCircleRadius);
return new Circle(center, (double)enclosedCircleRadius[0]);
}
public static Circle getMaxInscribedCircle(MatOfPoint contour) {
Rect rect = Imgproc.boundingRect(contour);
Mat imprint = Mat.zeros(rect.height, rect.width, CvType.CV_8UC1);
Imgproc.drawContours(imprint, Arrays.asList(contour), -1, new Scalar(255), -1);
Circle circle = getInscribedCircle(contour, imprint);
imprint.release();
return circle;
}
private static Circle getInscribedCircle(MatOfPoint contour, Mat imprint) {
Mat distTransform = new Mat();
Imgproc.distanceTransform(imprint, distTransform, Imgproc.CV_DIST_L2, 0);
MinMaxLocResult result = Core.minMaxLoc(distTransform);
Point maxLoc = result.maxLoc;
//double[] maxLocD = new double[] {maxLoc.x, maxLoc.y};
Point closestPoint = getClosestPoint(contour, maxLoc);
double radius = SimpleGeometry.distance(closestPoint, maxLoc);
return new Circle(maxLoc, radius);
}
private static Point getClosestPoint(MatOfPoint contour, Point point) {
double[] bestPoint = contour.get(0, 0);
double bestDist = SimpleGeometry.distance(point, bestPoint);
for(int i=0; i<contour.rows(); i++) {
for(int j=0; j<contour.cols(); j++) {
double[] curPoint = contour.get(i, j);
double curDist = SimpleGeometry.distance(point, curPoint);
if(curDist < bestDist) {
bestPoint = curPoint;
bestDist = curDist;
}
}
}
return new Point(bestPoint[0], bestPoint[1]);
}
// private static double[] getClosestPoint(MatOfPoint contour, double[] point) {
// double[] bestPoint = contour.get(0, 0);
// double bestDist = SimpleGeometry.distance(bestPoint, point);
//
// for(int i=0; i<contour.rows(); i++) {
// for(int j=0; j<contour.cols(); j++) {
// double[] curPoint = contour.get(i, j);
//
// double curDist = SimpleGeometry.distance(curPoint, point);
// if(curDist < bestDist) {
// bestPoint = curPoint;
// bestDist = curDist;
// }
// }
// }
//
// return bestPoint;
// }
}

View File

@@ -0,0 +1,336 @@
package ru.delkom07.geometry;
import java.util.LinkedList;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.RotatedRect;
import org.opencv.imgproc.Imgproc;
import ru.delkom07.geometry.obj.Vector;
import ru.delkom07.util.Pair;
public class ContoursFunctions {
/**
* Obtain a contour with the maximum area among the given ones.
* @param contours - list of contours.
* @return contour with the maximum area.
*/
public static MatOfPoint getMaxAreaContour(List<MatOfPoint> contours) {
if(null != contours && 0 != contours.size()) {
double maxArea = Imgproc.contourArea(contours.get(0));
MatOfPoint bestContour = contours.get(0);
for(MatOfPoint contour : contours) {
double area = Imgproc.contourArea(contour);
if(area > maxArea) {
maxArea = area;
bestContour = contour;
}
}
return bestContour;
} else {
return null;
}
}
/**
* Get linear sizes of object with elliptic contour.
* @param contour - object contour.
* @return linear sizes.
*/
private static double[] getLengthAndWidth(MatOfPoint contour) {
// length and width
MatOfPoint2f forFillEllipse = new MatOfPoint2f();
contour.convertTo(forFillEllipse, CvType.CV_32FC2);
RotatedRect rotatedRect = Imgproc.fitEllipse(forFillEllipse);
Point[] points = new Point[4];
rotatedRect.points(points);
double length = 0;
double width = 0;
length = SimpleGeometry.distance(points[0], points[1]);
width = SimpleGeometry.distance(points[1], points[2]);
if(length<width) {
double dd = length;
length = width;
width = dd;
}
return new double[]{length, width};
}
/**
* Get center point of MatOfPoint that is point with (middleX, middleY) coordinates.
* @param contour - matrix of point.
* @return center point of given matrix of point.
*/
public static double[] massCenter(MatOfPoint contour) {
double[] acc = new double[] {0.0, 0.0};
List<Point> points = contour.toList();
for(Point p : points) {
acc[0] += p.x;
acc[1] += p.y;
}
return new double[]{acc[0]/points.size(), acc[1]/points.size()};
}
// **Obsolete version
// public static double[] massCenter(MatOfPoint contour) {
// 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;
// }
/**
*
* @param contour
* @return
*/
private static Vector[] mainComponents(MatOfPoint contour) {
double[] massCenter = massCenter(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())};
}
/**
* Вычислить вектора, идущие вдоль контура в заданным (GLOB_VECT_SIZE) шагом.
* @param contour - контур, полученный из функции findContours::Imgproc::OpenCV (матрица точек).
* @return - список векторов, образующих (почти) замкнутый контур.
*/
public static List<Vector> getVectors(MatOfPoint contour, int vectSize) {
List<Vector> vectList = new LinkedList<Vector>(); // список векторов (только направление, без привязки к координатам)
//List<Double[]> vectEndList = new LinkedList<Double[]>(); // список концов векторов (для привязки к координатам)
double[] prevPoint = contour.get(0, 0);
int step = 0;
for(int i=0; i<contour.size().width; i++) {
for(int j=0; j<contour.size().height; j++) {
step++;
if(i==0 && j==0) // пропуск первой точки
continue;
if(step>=vectSize) { // шаг увеличивается пока не достигнет необходимой длины вектора
step=0;
double[] curPoint = contour.get(j, i);
vectList.add(new Vector(prevPoint[0], prevPoint[1], curPoint[0], curPoint[1]));
prevPoint = curPoint;
}
}
}
// Последнй вектор замыкает контур:
double[] curPoint = contour.get(0, 0);
vectList.add(new Vector(prevPoint[0], prevPoint[1], curPoint[0], curPoint[1]));
return vectList;
}
/**
* Вычисление перегибов
* @param vectors - список векторов, идущих друг за другим (каждый последующий выходит из конца предидущего),
* и образующих замкнутый контур.
* @return - списк векторов, на конце которых обнаружился перегиб.
* @throws Exception
*/
public static List<Pair<Point, Vector>> getBendsIndxs(List<Vector> vectors) {
// общая сумма направлений необходима, чтобы определить в какую сторону производится обход контура
double courseSum = 0.0;
// список направлений векторов относительно предыдущего вектора: правее или левее
List<Pair<Double, Double>> courseList = new LinkedList<Pair<Double, Double>>();
Vector prevVector = vectors.get(vectors.size()-1);
for(int i=0; i<vectors.size(); i++) { // only if vectList.size > 1;
Vector curVector = vectors.get(i);
// направление текущего вектора относительно предидущего: правее (course<0) или левее (course>0)
Double relativeCourse = prevVector.x()*curVector.y()-prevVector.y()*curVector.x();
courseList.add(new Pair<Double, Double>(relativeCourse, SimpleGeometry.angle(prevVector, curVector)));
courseSum += relativeCourse;
prevVector = curVector;
}
// выбор индексов векторов, где обнаружен перегиб в обратную сторону относительно направления обхода контура
List<Pair<Point, Vector>> bendListIndxs = new LinkedList<Pair<Point, Vector>>();
for(int i=0; i<courseList.size(); i++) {
Pair<Double, Double> coursePair = courseList.get(i);
double course = coursePair.getLeft();
double angle = coursePair.getRight();
double thresAngle = 0.7;
if(courseSum > 0) {
if(course < 0 && angle > thresAngle) { // Math.abs(course)>thres
Vector v1 = i>0 ? vectors.get(i-1) : vectors.get(vectors.size()-1);
Vector v2 = i<vectors.size() ? vectors.get(i) : vectors.get(0);
Vector inverse = v2.inverse();
Vector deposition = v1.deposition(v1.x2(), v1.y2());
Vector v3 = new Vector(inverse.x1(), inverse.y1(), (inverse.x2()+deposition.x2())/2.0, (inverse.y2()+deposition.y2())/2.0);
bendListIndxs.add(new Pair<Point, Vector>(new Point(vectors.get(i).x1(), vectors.get(i).y1()), v3 ));
}
} else {
if(course > 0 && angle > thresAngle) { // Math.abs(course)>thres
Vector v1 = i>0 ? vectors.get(i-1) : vectors.get(vectors.size()-1);
Vector v2 = i<vectors.size() ? vectors.get(i) : vectors.get(0);
Vector inverse = v2.inverse();
Vector deposition = v1.deposition(v1.x2(), v1.y2());
Vector v3 = new Vector(inverse.x1(), inverse.y1(), (inverse.x2()+deposition.x2())/2.0, (inverse.y2()+deposition.y2())/2.0);
bendListIndxs.add(new Pair<Point, Vector>(new Point(vectors.get(i).x1(), vectors.get(i).y1()), v3 ));
}
}
}
return bendListIndxs;
}
}
// TO CHECK
//private 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;
//}
//
//
//
//private 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())};
//}

View File

@@ -0,0 +1,145 @@
package ru.delkom07.geometry;
import ru.delkom07.geometry.obj.LineFunction;
public class LineFunctions {
/**
* Returns line function of given segment.
* @param p1 - first point of segment.
* @param p2 - second point of segment.
* @return line function of given segment.
* @tested
*/
public static LineFunction getLineFunction(double[] p1, double[] p2) {
final double A = -(p2[1]-p1[1]); // -(y2-y1)
final double B = (p2[0]-p1[0]); // (x2-x1)
final double C = -p1[1]*p2[0] + p1[0]*p2[1]; // -y1x2 + x1y2
final double k = (p2[1]-p1[1])/(p2[0]-p1[0]); // (y2-y1)/(x2-x1)
final double b = (p1[1]*p2[0]-p1[0]*p2[1])/(p2[0]-p1[0]); // (y1x2-x1y2)/(x2-x1)
return new LineFunction() {
@Override
public double value(double x) {
return k*(double)x + b;
}
@Override
public double getA() {return A;}
@Override
public double getB() {return B;}
@Override
public double getC() {return C;}
@Override
public double getk() {return k;}
@Override
public double getb() {return b;}
};
}
/**
* Returns perpendicular line function of given segment.
* @param p1 - first point of segment.
* @param p2 - second point of segment.
* @return line function of given segment.
* @tested
*/
public static LineFunction getNormalLineFunction(double[] p1, double[] p2, double[] pM) {
final double k = -(p2[0]-p1[0])/(p2[1]-p1[1]); // -(x2-x1)/(y2-y1)
final double b = pM[1]+(-k)*pM[0]; // y0+(x2-x1)/(y2-y1)*x0
final double A = k;
final double B = -1;
final double C = b;
return new LineFunction() {
@Override
public double value(double x) {
return k*(double)x + b;
}
@Override
public double getA() {return A;}
@Override
public double getB() {return B;}
@Override
public double getC() {return C;}
@Override
public double getk() {return k;}
@Override
public double getb() {return b;}
};
}
/**
* Returns middle normal line function of given segment.
* @param p1 - first point of segment.
* @param p2 - second point of segment.
* @return middle normal line function of given segment.
* @tested
*/
public static LineFunction getMiddleNormalLineFunction(double[] p1, double[] p2) {
// Middle point
double xMdl = (p1[0]+p2[0])/2.0;
double yMdl = (p1[1]+p2[1])/2.0;
final double k = -(p2[0]-p1[0])/(p2[1]-p1[1]); // -(x2-x1)/(y2-y1)
final double b = yMdl+(-k)*xMdl; // y0+(x2-x1)/(y2-y1)*x0
final double A = k;
final double B = -1;
final double C = b;
return new LineFunction() {
@Override
public double value(double x) {
return k*(double)x + b;
}
@Override
public double getA() {return A;}
@Override
public double getB() {return B;}
@Override
public double getC() {return C;}
@Override
public double getk() {return k;}
@Override
public double getb() {return b;}
};
}
/**
* Calculates distance from point to line.
* @param point - given point.
* @param func - function of line.
* @return distance from point to line.
* @tested
*/
private static double distanceFromPointToLine(double[] point, LineFunction func) {
double A = func.getA();
double B = func.getB();
double C = func.getC();
double tmp = Math.sqrt(A*A + B*B);
return Math.abs(A*point[0] + B*point[1] + C) / tmp;
}
/**
* Calculates distance from point to line.
* @param point - given point.
* @param line - given line.
* @return distance from point to line.
* @tested
*/
private static double distanceFromPointToLine(double[] point, double[] line) {
double A = (line[3]-line[1]);
double B = -(line[2]-line[0]);
double C = (line[1]*line[2] - line[1]*line[0] - line[0]*line[3] + line[0]*line[1]);
double tmp = Math.sqrt(A*A + B*B);
if(0!=tmp) return Math.abs(A*point[0] + B*point[1] + C) / tmp;
return SimpleGeometry.distance(point, new double[]{line[0], line[1]});
}
}

View File

@@ -0,0 +1,469 @@
package ru.delkom07.geometry;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.opencv.core.Point;
import ru.delkom07.geometry.obj.Vector;
import ru.delkom07.util.Pair;
public class SimpleGeometry {
// === Distance ===
/**
* Calculate euclidean distance from point p1 to point p2.
* @param p1 - fisrt point.
* @param p2 - second point.
* @return numerical, unscaled distance.
*/
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);
}
/**
* Calculate euclidean distance from point p1 to point p2.
* @param p1 - fisrt point.
* @param p2 - second point.
* @return numerical, unscaled distance.
*/
public static double distance(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);
}
/**
* Calculate euclidean distance from point p1 to point p2.
* @param p1 - fisrt point.
* @param p2 - second point.
* @return numerical, unscaled distance.
*/
public static double distance(Point p1, double[] p2) {
double dx = Math.abs(p1.x-p2[0]);
double dy = Math.abs(p1.y-p2[1]);
return Math.sqrt(dx*dx+dy*dy);
}
/**
* Calculate a new normalized vector from the given one.
* @param vector - given vector.
* @return a new normalized vector.
*/
public static Vector normalize(Vector vector) {
double length = vector.length();
double newX = vector.x()/length;
double newY = vector.y()/length;
return new Vector(vector.x1(), vector.y1(), vector.x1()+newX, vector.y1()+newY);
}
// === Angles ===
/**
* Calculate of angle between given vectors in radians.
* To calculate degree: *180 / pi.
* @param vect1 - first vector.
* @param vect2 - second vector.
* @return angle between given vectors.
*/
public static double angle(Vector vect1, Vector vect2) {
return Math.acos((vect1.x()*vect2.x()+vect1.y()*vect2.y())/(vect1.length()*vect2.length()));
}
/**
* Calculate of angle between given vectors in degrees.
* To calculate radians: *pi/180.
* @param vect1 - first vector.
* @param vect2 - second vector.
* @return angle between given vector.
*/
public static double angleInDegree(Vector vect1, Vector vect2) {
return angle(vect1, vect2) * 180 / Math.PI;
}
/**
* Angle between two lines (without mean directions).
* @param vect1 - first line.
* @param vect2 - second line.
* @return Angle between two lines.
*/
private static double lineAngle(Vector vect1, Vector vect2) {
double val = angle(vect1, vect2);
return val < Math.PI/2.0 ? val : Math.PI - val;
}
/**
* Angle between two lines (without mean directions).
* @param vect1 - first line.
* @param vect2 - second line.
* @return Angle between two lines.
*/
private static double lineAngleInDegree(Vector vect1, Vector vect2) {
return lineAngle(vect1, vect2) * 180 / Math.PI;
}
/**
* Calculate of angle between given lines in radians.
* To calculate degrees: * 180 / pi.
* @param line1 - first line.
* @param line2 - second line.
* @return angle between given lines.
*/
private static double lineAngle(double[] line1, double[] line2) {
double dx1 = Math.abs(line1[0]-line1[2]); // |x1 - x2|
double dy1 = Math.abs(line1[1]-line1[3]); // |y1 - y2|
double l1 = Math.sqrt(dx1*dx1 + dy1*dy1);
double dx2 = Math.abs(line2[0]-line2[2]); // |x1 - x2|
double dy2 = Math.abs(line2[1]-line2[3]); // |y1 - y2|
double l2 = Math.sqrt(dx2*dx2 + dy2*dy2);
double val = Math.acos((dx1*dx2+dy1*dy2)/(l1*l2));
return val < Math.PI/2.0 ? val : Math.PI - 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.
*/
private static double angleInDegree(double[] line1, double[] line2) {
return lineAngle(line1, line2) * 180 / Math.PI;
}
/**
* Compute intersection of lines.
* @param line1 - first line.
* @param line2 - second line.
* @return point of intersection of given lines.
*/
private static double[] computeIntersect(double[] line1, double[] line2) {
double x1 = line1[0], y1 = line1[1], x2 = line1[2], y2 = line1[3];
double x3 = line2[0], y3 = line2[1], x4 = line2[2], y4 = line2[3];
double d = ((x1-x2) * (y3-y4)) - ((y1-y2) * (x3-x4));
if (0 != d) {
double[] pt = new double[2];
pt[0] = ((x1*y2 - y1*x2) * (x3-x4) - (x1-x2) * (x3*y4 - y3*x4)) / d;
pt[1] = ((x1*y2 - y1*x2) * (y3-y4) - (y1-y2) * (x3*y4 - y3*x4)) / d;
return pt;
} else
return new double[]{-1, -1};
}
/**
* Compute intersection of lines.
* @param p1Line1 - first point of first line.
* @param p2Line1 - second point of first line.
* @param p1Line2 - first point of second line.
* @param p2Line2 - second point of second line.
* @return point of intersection of given lines.
*/
public static double[] computeIntersect(double[] p1Line1, double[] p2Line1, double[] p1Line2, double[] p2Line2) {
double x1 = p1Line1[0], y1 = p1Line1[1], x2 = p2Line1[0], y2 = p2Line1[1];
double x3 = p1Line2[0], y3 = p1Line2[1], x4 = p2Line2[0], y4 = p2Line2[1];
double d = ((x1-x2) * (y3-y4)) - ((y1-y2) * (x3-x4));
if (0 != d) {
double[] pt = new double[2];
pt[0] = ((x1*y2 - y1*x2) * (x3-x4) - (x1-x2) * (x3*y4 - y3*x4)) / d;
pt[1] = ((x1*y2 - y1*x2) * (y3-y4) - (y1-y2) * (x3*y4 - y3*x4)) / d;
return pt;
} else
return new double[]{-1, -1};
}
/**
* Compute intersection of lines.
* @param line1 - first line.
* @param line2 - second line.
* @return point of intersection of given lines.
*/
private static Point computeIntersect(Point[] line1, Point[] line2) {
double x1 = line1[0].x, y1 = line1[0].y, x2 = line1[1].x, y2 = line1[1].y;
double x3 = line2[0].x, y3 = line2[0].y, x4 = line2[1].x, y4 = line2[1].y;
double d = ((x1-x2) * (y3-y4)) - ((y1-y2) * (x3-x4));
if (0 != d) {
Point pt = new Point();
pt.x = ((x1*y2 - y1*x2) * (x3-x4) - (x1-x2) * (x3*y4 - y3*x4)) / d;
pt.y = ((x1*y2 - y1*x2) * (y3-y4) - (y1-y2) * (x3*y4 - y3*x4)) / d;
return pt;
} else
return new Point(-1, -1);
}
/**
* Compute intersection of lines and return Point instance.
* @param line1 - first line.
* @param line2 - second line.
* @return point of intersection of given lines.
*/
private static Point computeIntersectPoint(double[] line1, double[] line2) {
return new Point(computeIntersect(line1, line2));
}
public static double fromLineToPointDistance(double[] line, double[] point) {
return Math.abs((line[3]-line[1])*point[0] - (line[2]-line[0])*point[1] + line[2]*line[1] - line[3]*line[0])
/ Math.sqrt((line[3]-line[1])*(line[3]-line[1]) + (line[2]-line[0])*(line[2]-line[0]));
}
public static double fromLineToPointDistance(Vector line, double[] point) {
return Math.abs((line.y2()-line.y1())*point[0] - (line.x2()-line.x1())*point[1] + line.x2()*line.y1() - line.y2()*line.x1())
/ Math.sqrt((line.y2()-line.y1())*(line.y2()-line.y1()) + (line.x2()-line.x1())*(line.x2()-line.x1()));
}
public static double fromLineToPointDistance(Vector line, Point point) {
return Math.abs((line.y2()-line.y1())*point.x - (line.x2()-line.x1())*point.y + line.x2()*line.y1() - line.y2()*line.x1())
/ Math.sqrt((line.y2()-line.y1())*(line.y2()-line.y1()) + (line.x2()-line.x1())*(line.x2()-line.x1()));
}
/**
*
* @param points
* @return
*/
private 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)};
}
/**
*
* @param pointArr
* @return
*/
private 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(distance(p1, p2) > maxDistance) {
maxP1 = p1;
maxP2 = p2;
maxDistance = distance(p1, p2);
}
}
}
return new Point[]{maxP1, maxP2};
}
/**
* Вернить две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
* Функция сначала находит список точек претендентов: наиболее близкие к линии точки по pointBufferSize справа и слева.
* После этого для каждой стороны выбирается наиболее близкая точка к нормали.
* @param listOfPoints - списко точек.
* @param line - данная линия.
* @param normal - нормаль.
* @return две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
*/
private static Point[] getTwoPointThatLayOnGivenLine(List<Point> listOfPoints, Vector line, Vector normal) {
short pointBufferSize = 5;
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;
yDiff = yDiff == 0 ? 0.0001 : yDiff;
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.getRight() > arg1.getRight()) {
return 1;
}
if(arg0.getRight() < arg1.getRight()) {
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);
Collections.sort(rightPoints, comparator);
}
} else {
for(int j=0; j<rightPoints.size(); j++) {
Pair<Point, Double> rightPoint = rightPoints.get(j);
if(closenessToLine < rightPoint.getRight()) {
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);
Collections.sort(leftPoints, comparator);
}
} else {
for(int j=0; j<leftPoints.size(); j++) {
Pair<Point, Double> leftPoint = leftPoints.get(j);
if(closenessToLine < leftPoint.getRight()) {
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.getLeft();
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.getLeft();
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};
}
private static double distanceBetweenPointsOfElongatedVectors(Vector v1, Vector v2, double len) {
Vector v1Direction = v1.normalizedDirection();
Vector v2Direction = v2.normalizedDirection();
Point p1 = new Point(v1.x1() + v1Direction.x()*len, v1.y1() + v1Direction.y()*len);
Point p2 = new Point(v2.x1() + v2Direction.x()*len, v2.y1() + v2Direction.y()*len);
return distance(p1, p2);
}
}

View File

@@ -0,0 +1,341 @@
package ru.delkom07.geometry;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import ru.delkom07.geometry.obj.LineFunction;
public class SpecificGeometry {
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)};
}
/**
* Разделение контура на два по линии представленной двумя точками данного контура.
* @param contour - исходный контур.
* @param point1 - первая точка линии деления.
* @param point2 - вторая точка линии деления.
* @return - два контура, замкнутые по линии деления.
*/
private static MatOfPoint[] devideCountour(MatOfPoint contour, Point point1, Point point2) {
MatOfPoint contour1 = new MatOfPoint();
MatOfPoint contour2 = new MatOfPoint();
List<Point> pointsOrdered = contour.toList();
List<Point> pointsOrdered1 = new LinkedList<Point>();
List<Point> pointsOrdered2 = new LinkedList<Point>();
// переключатель контура
boolean contourRelay = false;
for(Point point : pointsOrdered) {
// дошли до точки линии деления
if( point.equals(point1) || point.equals(point2)) {
contourRelay = !contourRelay;
// добавить все точки по линии деления для замыкания контура
if(point.equals(point1)) { // from point1 to point2
addMiddlePoints(contourRelay ? pointsOrdered1 : pointsOrdered2, point1, point2);
} else {
addMiddlePoints(contourRelay ? pointsOrdered1 : pointsOrdered2, point2, point1);
}
}
// добавить текущую точку исходного контура в соответствующий новый контур
if(contourRelay) {
pointsOrdered1.add(point);
} else {
pointsOrdered2.add(point);
}
}
contour1.fromList(pointsOrdered1);
contour2.fromList(pointsOrdered2);
return new MatOfPoint[]{contour1, contour2};
}
private static void addMiddlePoints(List<Point> pointsOrdered, Point point1, Point point2) {
double alfa;
int stepCount = (int) SimpleGeometry.distance(point1, point2) * 2;
double step = 1.0 / stepCount;
for(int i=0; i<stepCount; i++) {
alfa = step*i;
Point newPoint = new Point(point1.x*alfa + point2.x*(1.0-alfa), point1.y*alfa + point2.y*(1.0-alfa));
if(!pointsOrdered.contains(newPoint) && !newPoint.equals(point1) && !newPoint.equals(point2)) {
pointsOrdered.add(newPoint);
}
}
}
/**
* Method finds two points on middle normal with maximum distance.
* @param contour - mat of points on contour.
* @param p1 - top point.
* @param p2 - bottom point.
* @return two points on middle normal with maximum distance.
*/
private static double[][] findMaxDistancePointsOnMiddleNormal(MatOfPoint contour, final double[] p1, final double[] p2) {
double bigDistance = SimpleGeometry.distance(p1, p2)/3; // That distance is big for this contour
// Point : distance from point to middle normal
final Map<Double, double[]> distancesWithPoints = new HashMap <Double, double[]>();
// Find all distance for all point to middle normal
for(Point p: contour.toList()) {
double distance = distanceFromPointToMiddleNormal(new double[] {p.x, p.y}, new double[]{p1[0], p1[1], p2[0], p2[1]});
distancesWithPoints.put(distance, new double[] {p.x, p.y});
}
// Utils.forEach(contour,
// (double[] point) -> {
// double distance = distanceFromPointToMiddleNormal(point, new double[]{p1[0], p1[1], p2[0], p2[1]});
// distancesWithPoints.put(distance, point);
// }
// );
// new pair of points
double[][] pair = new double[2][2];
Set<Double> distances = distancesWithPoints.keySet();
pair[0] = distancesWithPoints.remove(Collections.min(distances));
while(!distancesWithPoints.isEmpty()) {
Double minDistance = Collections.min(distances);
double[] next = distancesWithPoints.get(minDistance);
if(SimpleGeometry.distance(pair[0], next) > bigDistance) {
pair[1] = next;
return pair;
}
distancesWithPoints.remove(minDistance);
}
return pair;
}
/**
* Calculate distance from point to middle normal of segment.
* @param p - point of reference.
* @return distance from point to middle normal of segment.
* @tested
*/
private static double distanceFromPointToMiddleNormal(double[] p, double[] line) {
LineFunction func = LineFunctions.getMiddleNormalLineFunction(
new double[]{line[0], line[1]}, new double[]{line[2], line[3]});
double A = func.getA();
double B = func.getB();
double C = func.getC();
double tmp = Math.sqrt(A*A + B*B);
if(0!=tmp) return Math.abs(A*p[0] + B*p[1] + C) / tmp;
else return SimpleGeometry.distance(p, new double[]{line[0], line[1]});
}
}
/**
* Вернить две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
* Функция сначала находит список точек претендентов: наиболее близкие к линии точки по pointBufferSize справа и слева.
* После этого для каждой стороны выбирается наиболее близкая точка к нормали.
* @param listOfPoints - списко точек.
* @param line - данная линия.
* @param normal - нормаль.
* @return две точки лежащие наиболее близко к данной линии и нормали справа и слева от нормали соответственно.
*/
//private static Point[] getTwoPointThatLayOnGivenLine(List<Point> listOfPoints, Vector line, Vector normal) {
//short pointBufferSize = 5;
//
//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;
// yDiff = yDiff == 0 ? 0.0001 : yDiff;
//
// 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.getRight() > arg1.getRight()) {
// return 1;
// }
// if(arg0.getRight() < arg1.getRight()) {
// 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.getRight()) {
// 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.getRight()) {
// 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.getLeft();
//
// 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.getLeft();
//
// 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) {
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,23 @@
package ru.delkom07.geometry.obj;
import org.opencv.core.Point;
public class Circle {
//private double[] center;
private Point center;
private double radius;
public Circle(Point center, double raduis) {
this.center = center;
this.radius = raduis;
}
public Point getCenter() {
return center;
}
public double radius() {
return radius;
}
}

View File

@@ -0,0 +1,10 @@
package ru.delkom07.geometry.obj;
public interface LineFunction {
double value(double x);
double getA();
double getB();
double getC();
double getk();
double getb();
}

View File

@@ -0,0 +1,150 @@
package ru.delkom07.geometry.obj;
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 Vector(Point p1, Point p2) {
this.x1 = p1.x;
this.y1 = p1.y;
this.x2 = p2.x;
this.y2 = p2.y;
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 Point p1() {
return new Point(x1, y1);
}
public Point p2() {
return new Point(x2, 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 Vector normalizedDirection() {
double len = length();
return new Vector(x/len, y/len);
}
public Vector inverse() {
return new Vector(x1, y1, x1-x, y1-y);
}
public Vector deposition(double newX, double newY) {
return new Vector(newX, newY, newX+x, newY+y);
}
public void draw(Mat img, Scalar color) {
double len = length()/15;
Imgproc.line(img, new Point(x1(), y1()), new Point(x2(), y2()), color, 1);
double dxL = (-y + x2 + x1)/2.0 - x2();
double dyL = (x + y2 + y1)/2.0 - y2();
double dxR = (y + x2 + x1)/2.0 - x2();
double dyR = (-x + y2+y1)/2.0 - y2();
Imgproc.line(img, new Point(x2(), y2()), new Point(x2 + dxL/len, y2 + dyL/len), color, 1);
Imgproc.line(img, new Point(x2(), y2()), new Point(x2 + dxR/len, y2 + dyR/len), color, 1);
//Imgproc.line(img, new Point(x2(), y2()), new Point((-y + x2 + x1)/(2.0), (x + y2 + y1)/(2.0)), color, 1);
//Imgproc.line(img, new Point(x2(), y2()), new Point((y + x2 + x1)/(2.0), (-x + y2+y1)/(2.0)), color, 1);
}
}

View File

@@ -0,0 +1,51 @@
package ru.delkom07.util;
public class ArgOption {
private String name;
private String shortName;
private String value;
private boolean isNullValue;
public ArgOption(String name, String shortName, boolean isNullValue) {
this.name = name;
this.shortName = shortName;
this.isNullValue = isNullValue;
}
void assign(String value) {
this.value = value;
}
public String getName() {
return name;
}
public String getShortName() {
return shortName;
}
public String getValue() {
return value;
}
public boolean isNullValue() {
return isNullValue;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ArgOption other = (ArgOption) obj;
if (name != other.name)
return false;
if (shortName != other.shortName) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,223 @@
package ru.delkom07.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Парсер параметров командной строки.
* Формат строки: [--opt1 [--opt2...]] [arg1 [arg2...]]
* Цикл использования: в конструктор передается командная строка, список допустимых опций,
* а так же лимиты по количеству опций и аргументов.
* После:
* - вызов getAcceptedOptions вернет список всех переданных опций.
* - вызовы nextOpt и nextArg вернут следующие переданные по списку опцию и аргумент.
* - вызов flush - позволит сбросить перебор опций и аргументов и начать заново.
* - вызовы getNumOpt и getNumArg возвращают размеры соответствующих списков.
* @author Komyshev
* v.1.0 (B)
*/
public class CommandRepresentation {
// Списки принятых опций и аргументов
private List<ArgOption> acceptedOptions = new LinkedList<ArgOption>();
private List<String> acceptedArguments = new ArrayList<String>();
// Итераторы для быстрого извлечения опций и аргументов методами nextOpt, nextArg.
private Iterator<ArgOption> optionIter;
private Iterator<String> argumentIter;
/**
* Разбор аргументов и опций командной строки.
* @param args - командная строка.
* @param optList - список названий принимаемых опций.
* @param maxOptNum - максимальное количество опций.
* @param minOptNum - минимальное количество опций.
* @param maxArgNum - максимальное количество аргументов.
* @param minArgNum - минимальносе количество аргументов.
* @throws IncorrectCmdException - исключение некорректной команды.
*/
public CommandRepresentation(String[] args, List<ArgOption> optList, int maxOptNum, int minOptNum, int maxArgNum, int minArgNum) throws IncorrectCmdException{
// Проверка на допустимость команды по количеству аргументов;
if ( (minOptNum + minArgNum > args.length) ||
maxOptNum + maxArgNum < args.length){
throw new IncorrectCmdException("Incorrect count of argument.");
}
//Получение параметров
for(int i=0; i<args.length; i++) {
String current = args[i];
if(current.startsWith("-")){ // Параметр опция
if(current.startsWith("--")) { // полная
ArgOption accepted = getOptionFromList(optList, current, true);
if(null != accepted) {
if(!accepted.isNullValue()) {
if(i!=args.length-1)
accepted.assign(args[++i]);
else
throw new IncorrectCmdException("Option "+ accepted.getName()+ "has not value!");
}
acceptedOptions.add(accepted);
}
else
throw new IncorrectCmdException("Incorrect option in command line!");
} else { // сокращение
// опция должна быть не длиннее 4х символов: -x
if(current.length()>4)
throw new IncorrectCmdException("Incorrect short option name.");
ArgOption accepted = getOptionFromList(optList, current, false);
if(null != accepted) {
if(!accepted.isNullValue()) {
if(i!=args.length-1)
accepted.assign(args[++i]);
else
throw new IncorrectCmdException("Option "+ accepted.getName()+ "has not value!");
}
acceptedOptions.add(accepted);
}
else
throw new IncorrectCmdException("Incorrect option in command line!");
}
} else { // Параметр - аргумент
acceptedArguments.add(current.trim());
}
}
optionIter = acceptedOptions.iterator();
argumentIter = acceptedArguments.iterator();
}
/**
* Получить список принятых опций.
* @return список принятых опций.
*/
public List<ArgOption> getAcceptedOptions() {
return acceptedOptions;
}
/**
* Получить список принятых аргументов.
* @return список принятых опций.
*/
public List<String> getAcceptedArguments() {
return acceptedArguments;
}
/**
* Получить список принятых аргументов.
* @return список принятых аргументов.
*/
public String[] getAcceptedArgs() {
String[] acceptedArgs = new String[acceptedArguments.size()];
for(int i=0; i<acceptedArguments.size(); i++) {
acceptedArgs[i] = acceptedArguments.get(i);
}
return acceptedArgs;
}
/**
* Получить количество полученных опций.
* @return количество полученных опций.
*/
public int getNumOfOpt() {
return acceptedOptions.size();
}
/**
* Получить количество полученных аргументов.
* @return количество полученных аргументов.
*/
public int getNumOfArgs() {
return acceptedArguments.size();
}
/**
* Если ли ещё опции по текущему итератору.
*/
public boolean hasNextOpt() {
return optionIter.hasNext();
}
/**
* Есть ли ещё аргументы по текущему итератору.
*/
public boolean hasNextArg() {
return argumentIter.hasNext();
}
/**
* Метод итерации по опциям командной строки.
* Итератор сбрасывается методом flush.
* @return - следующая опция командной строки.
*/
public ArgOption nextOpt() {
return optionIter.next();
}
/**
* Метод итерации по аргументам командной строки.
* Итератор сбрасывается методом flush.
* @return - следующий аргумент командной строки.
*/
public String nextArg() {
return argumentIter.next();
}
/**
* Сбросить итераторы по опциям и аргументам командной строки.
*/
public void flush() {
optionIter = acceptedOptions.iterator();
argumentIter = acceptedArguments.iterator();
}
public boolean containsOptionByName(String name) {
if(null!=getOptionFromList(acceptedOptions, name, true))
return true;
else
return false;
}
public ArgOption getOptionByName(String name) {
return getOptionFromList(acceptedOptions, name, true);
}
public boolean containsOptionByShortName(String shortName) {
if(null!=getOptionFromList(acceptedOptions, shortName, false))
return true;
else
return false;
}
public ArgOption getOptionByShortName(String shortName) {
return getOptionFromList(acceptedOptions, shortName, false);
}
private ArgOption getOptionFromList(List<ArgOption> optList, String option, boolean fullName) {
if(fullName) {
for(ArgOption argOpt : optList) {
if(argOpt.getName().equals(option)) {
return argOpt;
}
}
} else {
for(ArgOption argOpt : optList) {
if(argOpt.getShortName().equals(option)) {
return argOpt;
}
}
}
return null;
}
}

View File

@@ -0,0 +1,77 @@
package ru.delkom07.util;
import java.util.Iterator;
public class CyclicStack<T> implements Iterable<T> {
private T[] container;
private int pointer = -1;
@SuppressWarnings("unchecked")
public CyclicStack(int size) {
container = (T[]) new Object[size];
}
public void put(T obj) {
pointer++;
if(pointer == container.length) {
pointer = 0;
}
container[pointer] = obj;
}
public T pull() {
T obj = container[pointer];
container[pointer--] = null;
if(pointer == -1) {
pointer = container.length-1;
}
return obj;
}
public T get(int i) {
return container[i];
}
@Override
public Iterator<T> iterator() {
return new CyclicStackIterator();
}
class CyclicStackIterator implements Iterator<T> {
int iPointer = pointer;
int iteration = 0;
@Override
public boolean hasNext() {
if(iteration < container.length && iPointer != -1 && container[iPointer] != null) {
return true;
} else {
return false;
}
}
@Override
public T next() {
T obj = container[iPointer--];
if(iPointer == -1) {
iPointer = container.length-1;
}
iteration++;
return obj;
}
}
}

View File

@@ -0,0 +1,16 @@
package ru.delkom07.util;
/**
*
* @author Komyshev
* v.1.0 (C)
*/
public class IncorrectCmdException extends Exception {
private static final long serialVersionUID = 1L;
public IncorrectCmdException(){}
public IncorrectCmdException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,33 @@
package ru.delkom07.util;
public class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
@Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
@Override
public boolean equals(Object o) {
if(o instanceof Pair) {
@SuppressWarnings("unchecked")
Pair<L, R> pairo = (Pair<L, R>) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
} else {
return false;
}
}
}

View File

@@ -0,0 +1,5 @@
package ru.delkom07.workspaces;
public interface FileNameReplacer {
public String replaceFileName(String fileName);
}

View File

@@ -0,0 +1,139 @@
package ru.delkom07.workspaces;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import ru.delkom07.fileutils.FileUtilities;
import ru.delkom07.util.Pair;
/**
* Простая рабочая среда.
* Создает список операционных единиц (OpUnit) на основе входного/выходного каталогов,
* которые связывают имена и пути входных и выходных файлов текущей сессии выполнения.
* Способен создавать рабочую среду из подкаталогов входного каталога с древовидной структурой.
* Для этого в выходном каталоге создается аналогичная входному каталогу структура подкаталогов и соответствующие файлы в них связываются через OpUnit.
* Выходные файлы при этом не создаются. Вместо этого для них формируются имена и пути (экземпляры класса File).
* В результате каждый OpUnit может обрабатываться независимо друг от друга.
* @author Komyshev
*
*/
public class SimpleWorkspace {
protected File inputDir; // директория входных файлов
protected File outputDir; // директория выходных файлов
protected List<OpUnit> operationUnits = new LinkedList<OpUnit>(); // единицы обработки
private boolean treeStrtucture = false;
public SimpleWorkspace(File inputDir, File outputDir, boolean treeStructure, FileFilter fileFilter, FileNameReplacer fileNameReplacer) throws IOException {
this.inputDir = inputDir;
this.outputDir = outputDir;
this.treeStrtucture = treeStructure;
if(treeStrtucture) {
init(prepareAppropriateTreeOutputs(inputDir, outputDir, fileFilter, fileNameReplacer));
} else {
init(prepareAppropriateFlatOutputs(inputDir, outputDir, fileFilter, fileNameReplacer));
}
}
/**
* Подготовить список пар файлов (входной-выходной) с соответствующей структурой.
* Для этого считать все входные файлы, соответствующие fileFilter, из директории inputDir а также её поддиректорий, и подготовить пути выходных файлов
* с соответствующей структурой поддиректорий в outputDir.
* @param inputDir - директория входных файлов.
* @param outputDir - директория выходных файлов.
* @param fileFilter - фильтр файлов.
* @return - список пар (входной файл - выходной файл), где путь в поддиректориях в ouputDir выходного файла совпадает с соответствующим путем
* входного файла в поддиректориях inputDir.
* @throws IOException
*/
public static List<Pair<File, File>> prepareAppropriateTreeOutputs(File inputDir, File outputDir, FileFilter fileFilter, FileNameReplacer fileNameReplacer) throws IOException {
List<Pair<File, File>> inputsAndOutputs = new ArrayList<Pair<File, File>>();
List<File> inputFiles = FileUtilities.getFilesRecursively(inputDir, fileFilter);
for(File inputFile : inputFiles) {
File outputImgDir = new File(outputDir, inputFile.getParentFile().getCanonicalPath().replace(inputDir.getCanonicalPath(), ""));
if(!outputImgDir.exists()) {
outputImgDir.mkdirs();
}
File outputImgFile = new File(outputImgDir, (null == fileNameReplacer) ? inputFile.getName() : fileNameReplacer.replaceFileName(inputFile.getName()));
inputsAndOutputs.add(new Pair<File, File>(inputFile, outputImgFile));
}
return inputsAndOutputs;
}
/**
* Подготовить список пар файлов (входной-выходной) без учета поддиректорий.
* @param inputImgsDir - директория входных файлов.
* @param outputImgsDir - директория выходных файлов.
* @param filter - фильтр файлов.
* @param fileNameReplacer - объект, заменяющие (изменяющее) названия входных файлов при создании выходных файлов.
* @return список пар файлов.
*/
public static List<Pair<File, File>> prepareAppropriateFlatOutputs(File inputImgsDir, File outputImgsDir, FileFilter filter, FileNameReplacer fileNameReplacer) {
List<Pair<File, File>> imgPairs = new ArrayList<Pair<File, File>>();
File[] files = inputImgsDir.listFiles(filter != null ? filter : FileUtilities.fileFilter);
// link source image
for(int i=0; i<files.length; i++) {
File sourceImgFile = files[i];
String name = (null == fileNameReplacer) ? sourceImgFile.getName() : fileNameReplacer.replaceFileName(sourceImgFile.getName());
File outputImgFile = new File(outputImgsDir, name);
imgPairs.add(new Pair<File, File>(sourceImgFile, outputImgFile));
}
return imgPairs;
}
void init(List<Pair<File, File>> insAndOuts) throws IOException {
for(Pair<File, File> inAndOut : insAndOuts) {
operationUnits.add(new OpUnit(inAndOut.getLeft(), inAndOut.getRight()));
}
}
public List<OpUnit> getOpUnits() {
return operationUnits;
}
/**
* Единица операции.
* @author Komyshev
*
*/
public class OpUnit {
protected File in;
protected File out;
OpUnit(File in, File out) {
this.in = in;
this.out = out;
}
public File getIn() {
return in;
}
public File getOut() {
return out;
}
}
}

View File

@@ -0,0 +1,110 @@
package ru.delkom07.workspaces;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
/**
* Подкласс класса SimpleWorkspace с дополнительной фукнциональностью - отслеживанием и сохранением статуса обработки операционных единиц.
* @author Komyshev
*
*/
public class StatusWorkspace extends SimpleWorkspace {
private boolean[] statuses;
private File statusFile;
public StatusWorkspace(File inputDir, File outputDir, boolean treeStructure, FileFilter fileFilter, FileNameReplacer fileNameReplacer) throws IOException {
super(inputDir, outputDir, treeStructure, fileFilter, fileNameReplacer);
initStatus();
}
private void initStatus() throws IOException {
statuses = new boolean[operationUnits.size()]; // False by default ???
statusFile = new File(outputDir, "status.txt");
if(!statusFile.exists()) {
statusFile.createNewFile();
} else {
loadStatus();
}
}
private void loadStatus() {
try (BufferedReader br = new BufferedReader(new FileReader(statusFile))) {
String line;
while ((line = br.readLine()) != null) {
setProcessed(line);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void setProcessed(String identificationPath) {
boolean success = false;
for(int i=0; i<operationUnits.size(); i++) {
OpUnit opUnit = operationUnits.get(i);
try {
if(opUnit.getIn().getCanonicalPath().equals(identificationPath)) {
statuses[i] = true;
success = true;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(!success) {
System.err.println("File: " + identificationPath + " is'nt found in workspace.");
}
}
/**
* Записать операционную единицу как обработанную.
* @param opUnit - обработанная операционная единица.
*/
public void saveProcessedOpUnit(OpUnit opUnit) {
int ind = operationUnits.indexOf(opUnit);
statuses[ind] = true;
try (PrintWriter pw = new PrintWriter(new FileWriter(statusFile, true))) {
pw.println(opUnit.getIn().getCanonicalPath());
pw.flush();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Выдать необработанные операционные единицы.
* @return необработанные операционные единицы.
*/
public List<OpUnit> getRawOpUnits() {
List<OpUnit> rawOpUnits = new LinkedList<OpUnit>();
for(int i=0; i<operationUnits.size(); i++) {
if(statuses[i]==false) {
rawOpUnits.add(operationUnits.get(i));
}
}
return rawOpUnits;
}
}

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