opencv
Utilizzo di classificatori Cascade in Java
Ricerca…
Sintassi
- CascadeClassifier cascade = new CascadeClassifier ("cascade.xml"); // Crea un classificatore a cascata da cascade.xml
- Immagine mat = Imgcodecs.imread ("image.png"); // Converte image.png in un oggetto Mat (Matrix)
- MatOfRect rilevamenti = nuovo MatOfRect (); // Crea un file MatOfRect (Matrix of Rectangles) vuoto, utilizzato come output per le nostre classi di rilevamento
- detections.toArray (); // Restituisce una matrice di oggetti Rect che possono essere ripetuti
- Imgproc.rectangle (immagine, nuovo punto (rect.x, rect.y), nuovo punto (rect.x + rect.width, rect.y + rect.height), nuovo scalare (0, 255, 0)); // Disegna un rettangolo con contorno verde dalle posizioni xey del primo punto alla posizione xey del secondo punto su "immagine" dell'oggetto Mat. "rect" è un oggetto Rect, solitamente fornito da detectings.toArray (). Utilizza la classe Point di OpenCV.
- Imgcodecs.imwrite ("output.png", image); // Scrive l'oggetto Mat modificato "image" in "output.png"
- CascadeClassifier.detectMultiScale (immagine, rilevamenti); // Rileva qualsiasi oggetto nell'oggetto Mat "image" e restituisce i rilevamenti nell'oggetto MatOfRect "rilevamenti"
- CascadeClassifier.detectMultiScale (immagine, rilevazioni, scaleFactor, minNeighbors, bandiere, minSize, maxSize); // Esegue un rilevamento con parametri aggiuntivi. Vedi i dettagli di seguito.
- Imgproc.ellipse (immagine, centro, assi , 0, 0, 360, nuovo scalare (255, 0, 255), spessore , lineType , 0); // Disegna un'ellisse sull'immagine al
center
del punto. Utilizza la classe Point di OpenCV.
Parametri
Parametro | Dettagli |
---|---|
fattore di scala | Quanto è ridotta la dimensione dell'immagine a ciascuna scala dell'immagine. Impostazione predefinita = 1.1 |
minNeighbors | Quanti vicini deve avere un rettangolo candidato prima di selezionarlo come oggetto rilevato. Impostazione predefinita = 4 |
bandiere | Bandiere legacy. Nella maggior parte dei casi, dovrebbe essere impostato su 0 . Impostazione predefinita = 0 |
minSize | Dimensione minima che può essere un rettangolo candidato. Questo usa la classe Size di OpenCV. Può essere utilizzato per ridurre il tempo di rilevamento e l'utilizzo della CPU, nonché per ridurre i falsi positivi. |
maxSize | Dimensione massima che può essere un rettangolo candidato. Questo usa la classe Size di OpenCV. Può essere utilizzato per ridurre il tempo di rilevamento e l'utilizzo della CPU, nonché per ridurre i falsi positivi. |
assi | Usa la classe Size di OpenCV. Definisce la larghezza e l'altezza dell'ellisse. |
spessore | Spessore della linea, in pixel. |
modello di linea | Ha vari parametri 0 è la linea continua, 8 è per una linea a 8, 4 è per una linea a 4 e CV_AA è per una linea antialias. Impostazione predefinita = 8 |
Ottenere un'immagine statica, rilevare elementi su di esso e produrre risultati.
Si prega di notare che questo esempio utilizza 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);
}
}
Il Rect[]
restituito da diceDetections.toArray()
può essere ripetuto. Ogni Rect
all'interno dell'array avrà quattro proprietà principali: x
, y
, width
e height
. x
ed y
definisce la posizione in alto a sinistra del rettangolo, e width
e height
restituisce un int
della larghezza e l'altezza del rettangolo. Questo è usato quando si disegnano rettangoli sulle immagini. I Imgproc.rectangle
minimi richiesti Imgproc.rectangle
funzione Imgproc.rectangle
sono i seguenti:
Imgproc.rectangle(Mat image, Point start, Point end, Scalar color);
Entrambi i Point
vengono utilizzati per le posizioni dell'angolo superiore sinistro e dell'angolo inferiore destro. Queste posizioni sono sia assolute all'immagine fornita come primo parametro, che non l'una all'altra. Pertanto, è necessario aggiungere la posizione x
o y
del rettangolo oltre alla width
o height
per definire correttamente il Punto end
.
Si noti che la classe Point
utilizzata in questi parametri non è la classe Point
della libreria standard di Java. Devi invece importare la classe Point
di OpenCV!
Rilevazione di immagini da un dispositivo video
Questo esempio introduce la classe VideoCapture
, in cui viene utilizzata per scattare un'immagine da una webcam e salvarla su un'immagine.
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.
}
}
Convertire un oggetto Mat in un oggetto BufferedImage
Questo esempio di Daniel Baggio è stato preso direttamente da questa risposta StackExchange , ma è stato ripubblicato per visibilità.
Questa classe accetta un oggetto Mat e restituisce l'oggetto BufferedImage utilizzato dalle librerie javax.swing
. Questo può essere usato da un oggetto Graphics
per disegnare l'immagine.
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;
}
Rilevamenti all'interno dei rilevamenti
Questo esempio usa i dadi e le macchie nere sui dadi (i semi) come oggetto. Dato che l'esempio è piuttosto lungo, la prima spiegazione di alcuni concetti chiave è fondamentale per comprendere l'esempio.
Capire il primo esempio, "Ottenere un'immagine statica, rilevare elementi su di esso e produrre i risultati." è fondamentale per comprendere questo esempio, in particolare come OpenCV disegna rettangoli.
Dai un'occhiata alla seguente immagine:
Useremo il metodo di sottomissione, in cui utilizziamo un'area rilevata come base per l'applicazione di ulteriori rilevazioni. Questo è possibile solo se un oggetto sarà sempre all'interno di un altro oggetto che possiamo rilevare, come i nostri semi sui nostri dadi. Questo metodo ha diversi vantaggi:
- Invece di scansionare l'intera immagine, abbiamo solo bisogno di scansionare l'area in cui sappiamo che l'oggetto sarà dentro.
- Rimuove qualsiasi possibilità di falsi positivi al di fuori dell'area di rilevamento.
Facciamo questo applicando prima una scansione di classificatore a cascata sull'intera immagine per darci un oggetto MatOfRect
contenente i nostri oggetti di grandi dimensioni (dadi, in questo caso). Quindi toArray()
l' toArray()
sull'array Rect[]
fornito dalla funzione toArray()
dall'oggetto MatOfRect
. Questo oggetto Rect
viene utilizzato nella creazione di un oggetto Mat
temporaneo che viene "ritagliato" alle proprietà dell'oggetto Rect
( x, y, width, height
) dall'immagine originale, dove possiamo quindi eseguire rilevazioni sull'oggetto Mat
temporaneo. In altre parole, diciamo al classificatore di eseguire solo rilevamenti sulle parti di dadi dell'immagine, invece, e specifichiamo la posizione di ogni dado usando gli oggetti Rect
che abbiamo ottenuto eseguendo un rilevamento sull'intera immagine.
Tuttavia, gli oggetti Rect
(pips) hanno le loro proprietà relative ai loro dadi, e non l'immagine stessa. Per risolvere questo problema, quando vogliamo disegnare rettangoli sull'immagine reale che mostra le posizioni dei semi, aggiungiamo sia i dice.x
che i dice.y
al Point
iniziale.
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;
}
}
La funzione getNextImage()
restituisce un oggetto Mat
, che con gli altri esempi pubblicati può essere chiamato costantemente e può essere convertito in un BufferImage
, per fornire un livestream che visualizza i rilevamenti.