opencv
Korzystanie z klasyfikatorów kaskadowych w Javie
Szukaj…
Składnia
- CascadeClassifier cascade = new CascadeClassifier („cascade.xml”); // Tworzy klasyfikator kaskadowy z cascade.xml
- Mat image = Imgcodecs.imread ("image.png"); // Konwertuje image.png na obiekt Mat (Matrix)
- Wykrywania MatOfRect = nowy MatOfRect (); // Tworzy pusty plik MatOfRect (Matrix of Rectangles), używany jako dane wyjściowe dla naszych klas wykrywania
- detections.toArray (); // Zwraca tablicę obiektów Rect, które można iterować
- Imgproc.prostokąt (obraz, nowy punkt (rect.x, rect.y), nowy punkt (rect.x + rect.width, rect.y + rect.height), nowy Scalar (0, 255, 0)); // Rysuje zielony obrysowany prostokąt z położenia xiy pierwszego punktu do położenia xiy drugiego punktu na „obrazie” obiektu Mat. „rect” to obiekt Rect, zwykle dostarczany przez detections.toArray (). Wykorzystuje klasę Point OpenCV.
- Imgcodecs.imwrite ("output.png", obraz); // Zapisuje zmodyfikowany obiekt Mat „image” do „output.png”
- CascadeClassifier.detectMultiScale (obraz, wykrycia); // Wykrywa dowolny obiekt w „obrazie” obiektu Mat i wysyła detekcje w „wykrywaniu” obiektu MatOfRect
- CascadeClassifier.detectMultiScale (obraz, detekcje, scaleFactor , minNeighbors , flagi , minSize , maxSize ); // Przeprowadza wykrywanie z dodatkowymi parametrami. Szczegóły poniżej.
- Imgproc.ellipse (obraz, środek, osie , 0, 0, 360, nowy Skalar (255, 0, 255), grubość , typ linii , 0); // Rysuje elipsę na obrazie w
center
punktu. Wykorzystuje klasę Point OpenCV.
Parametry
Parametr | Detale |
---|---|
Współczynnik skali | Jak duży rozmiar obrazu jest zmniejszany przy każdej skali obrazu. Domyślnie = 1.1 |
min Sąsiedzi | Ilu sąsiadów powinien mieć prostokąt kandydujący przed wybraniem go jako wykrytego obiektu. Domyślnie = 4 |
flagi | Starsze flagi. W większości przypadków należy ustawić na 0 . Domyślnie = 0 |
minSize | Minimalny rozmiar kandydującego prostokąta może być. Wykorzystuje klasę Size OpenCV. Może być stosowany w celu skrócenia czasu wykrywania i zużycia procesora, a także w celu zmniejszenia fałszywych alarmów. |
największy rozmiar | Maksymalny rozmiar może być kandydatem na prostokąt. Wykorzystuje klasę Size OpenCV. Może być użyty do skrócenia czasu wykrywania i zużycia procesora, a także do zmniejszenia fałszywych alarmów. |
osie | Wykorzystuje klasę wielkości OpenCV. Określa szerokość i wysokość elipsy. |
grubość | Grubość linii w pikselach. |
rodzaj linii | Ma różne parametry. 0 oznacza linię ciągłą, 8 oznacza linię połączoną 8, 4 oznacza linię połączoną 4, a CV_AA oznacza linię CV_AA . Domyślnie = 8 |
Uzyskiwanie obrazu statycznego, wykrywanie na nim elementów i generowanie wyników.
Pamiętaj, że w tym przykładzie użyto OpenCV 3.1.
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
public class Classifier {
private CascadeClassifier diceCascade = new
CascadeClassifier("res/newMethod/diceCascade.xml");
private Mat image;
private String loc = "path/to/image.png";
private String output = "path/to/output.png";
public void detImg() {
Mat image = Imgcodecs.imread(loc); // Reads the image
MatOfRect diceDetections = new MatOfRect(); // Output container
diceCascade.detectMultiScale(image, diceDetections); // Performs the detection
// Draw a bounding box around each detection.
for (Rect rect : diceDetections.toArray()) {
Imgproc.rectangle(image, new Point(rect.x, rect.y),
new Point(rect.x + rect.width, rect.y + rect.height),
new Scalar(0, 255, 0));
}
// Save the visualized detection.
Imgcodecs.imwrite(output, image);
}
}
Rect[]
zwrócone przez diceDetections.toArray()
może być iterowane. Każdy Rect
wewnątrz tablicy będzie miał cztery główne właściwości: x
, y
, width
i height
. x
i y
określa położenie górnego lewego prostokąta, a width
i height
Zwraca int
na szerokość i wysokość prostokąta. Jest to używane podczas rysowania prostokątów na obrazach. Minimalne wymagane parametry funkcji Imgproc.rectangle
są następujące:
Imgproc.rectangle(Mat image, Point start, Point end, Scalar color);
Oba Point
są używane do pozycji lewego górnego rogu i prawego dolnego rogu. Pozycje te są bezwzględne względem obrazu podanego jako pierwszy parametr, a nie względem siebie. Dlatego należy dodać zarówno pozycję x
lub y
prostokąta, jak y
width
lub height
aby poprawnie zdefiniować punkt end
.
Należy zauważyć, że klasa Point
używana w tych parametrach nie jest klasą Point
biblioteki standardowej biblioteki Java. Zamiast tego musisz zaimportować klasę Point
OpenCV!
Wykrywanie obrazów z urządzenia wideo
Ten przykład przedstawia klasę VideoCapture
, w której używamy jej do pobrania obrazu z kamery internetowej i zapisania go na obrazie.
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
public class Classifier {
private CascadeClassifier diceCascade = new
CascadeClassifier("res/newMethod/diceCascade.xml");
private Mat image;
private String loc = "path/to/image.png";
private String output = "path/to/output.png";
private VideoCapture vc = new VideoCapture();
public void detImg() {
vc.open(0); // Opens the video stream
Mat image = new Mat(); // Creates an empty matrix
vc.read(image); // Reads the image from the video stream and
writes it to the image matrix.
MatOfRect diceDetections = new MatOfRect(); // Output container
diceCascade.detectMultiScale(image, diceDetections); // Performs the detection
// Draw a bounding box around each detection.
for (Rect rect : diceDetections.toArray()) {
Imgproc.rectangle(image, new Point(rect.x, rect.y),
new Point(rect.x + rect.width, rect.y + rect.height),
new Scalar(0, 255, 0));
}
// Save the visualized detection.
Imgcodecs.imwrite(output, image);
vc.release(); // Closes the stream.
}
}
Konwertowanie obiektu Mat na obiekt BufferedImage
Ten przykład Daniela Baggio został zaczerpnięty bezpośrednio z tej odpowiedzi StackExchange , ale został przebudowany dla widoczności.
Ta klasa pobiera obiekt Mat i zwraca obiekt BufferedImage używany przez biblioteki javax.swing
. Może to zostać wykorzystane przez obiekt Graphics
do narysowania obrazu.
private BufferedImage toBufferedImage(Mat m) {
if (!m.empty()) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (m.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = m.channels() * m.cols() * m.rows();
byte[] b = new byte[bufferSize];
m.get(0, 0, b); // get all the pixels
BufferedImage image = new BufferedImage(m.cols(), m.rows(), type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(b, 0, targetPixels, 0, b.length);
return image;
}
return null;
}
Wykrywania w ramach Wykrywania
W tym przykładzie jako obiekt wykorzystujemy kości i czarne plamki na kościach (pipsy). Ponieważ przykład jest dość długi, najpierw wyjaśnienie niektórych kluczowych pojęć ma kluczowe znaczenie dla zrozumienia przykładu.
Zrozumienie pierwszego przykładu: „Pobieranie obrazu statycznego, wykrywanie na nim elementów i generowanie wyników”. ma kluczowe znaczenie dla zrozumienia tego przykładu, w szczególności tego, jak OpenCV rysuje prostokąty.
Spójrz na następujący obraz:
Będziemy używać metody obrazowania, w której wykorzystamy wykryty obszar jako bazę do zastosowania większej liczby detekcji. Jest to możliwe tylko wtedy, gdy obiekt zawsze znajdzie się w innym obiekcie, który możemy wykryć, takim jak nasze pestki na naszych kostkach. Ta metoda ma kilka zalet:
- Zamiast skanować cały obraz, musimy tylko zeskanować obszar, w którym wiemy, że obiekt będzie.
- Usuwa wszelkie szanse na fałszywe alarmy poza obszarem wykrywania.
Robimy to najpierw poprzez zastosowanie jednego kaskadowego skanu klasyfikatora na całym obrazie, aby dać nam obiekt MatOfRect
zawierający nasze duże obiekty (w tym przypadku kostkę). Następnie toArray()
tablicę Rect[]
podaną przez funkcję toArray()
z obiektu MatOfRect
. Ten obiekt Rect
jest używany do tworzenia tymczasowego obiektu Mat
który jest „przycięty” do właściwości obiektu Rect
( x, y, width, height
) z oryginalnego obrazu, gdzie możemy następnie wykryć tymczasowy obiekt Mat
. Innymi słowy, mówimy klasyfikatorowi, aby zamiast tego dokonywał detekcji tylko na częściach kostki obrazu, a my określamy pozycję każdej kości za pomocą obiektów Rect
, które otrzymaliśmy z wykonania detekcji na całym obrazie.
Jednak obiekty Rect
(pipsy) mają swoje właściwości w stosunku do swoich kości, a nie sam obraz. Aby rozwiązać ten problem, gdy chcemy narysować prostokąty na rzeczywistym obrazie pokazującym lokalizację pipsów, dodajemy zarówno dice.x
jak i dice.y
do Point
początkowego.
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
public class Classifier {
private CascadeClassifier diceCascade =
new CascadeClassifier("res/newMethod/diceCascade.xml");
private CascadeClassifier pipCascade =
new CascadeClassifier("res/newMethod/pipCascade6.xml");
private VideoCapture vc = new VideoCapture();
private Mat image;
public void openVC(int index) {
vc.open(index);
}
public void closeVC() {
vc.close();
}
public Mat getNextImage() {
image = new Mat();
vc.read(image); // Sets the matrix to the current livestream frame.
MatOfRect diceDetections = new MatOfRect(); // Output container
// See syntax for explainations on addition parameters
diceCascade.detectMultiScale(image, diceDetections, 1.1, 4, 0, new Size(20, 20),
new Size(38, 38));
// Iterates for every Dice ROI
for (int i = 0; i < diceDetections.toArray().length; i++) {
Rect diceRect = diceDetections.toArray()[i];
// Draws rectangles around our detected ROI
Point startingPoint = new Point(diceRect.x, diceRect.y);
Point endingPoint = new Point(diceRect.x + diceRect.width,
diceRect.y + diceRect.height);
Imgproc.rectangle(image, startingPoint, endingPoint, new Scalar(255, 255, 0));
MatOfRect pipDetections = new MatOfRect();
pipCascade.detectMultiScale(image.submat(diceRect), pipDetections, 1.01, 4, 0,
new Size(2, 2), new Size(10, 10));
// Gets the number of detected pips and draws a cricle around the ROI
for (int y = 0; y < pipDetections.toArray().length; y++) {
// Provides the relative position of the pips to the dice ROI
Rect pipRect = pipDetections.toArray()[y];
// See syntax explaination
// Draws a circle around our pips
Point center = new Point(diceRect.x + pipRect.x + pipRect.width / 2,
diceRect.y + pipRect.y + pipRect.height / 2);
Imgproc.ellipse(image, center, new Size(pipRect.width / 2, pipRect.height / 2),
0, 0, 360, new Scalar(255, 0, 255), 1, 0, 0);
}
}
return image;
}
}
Funkcja getNextImage()
zwraca obiekt Mat
, który w połączeniu z innymi opublikowanymi przykładami może być wywoływany stale i może być konwertowany na BufferImage
, aby zapewnić wykrywanie na żywo.