OpenCV-4-視頻流整合識別預測模型
使用語言:Java 1.8
操作系統:windows x64
OpenCV:4.1.1
說明一下
在此之前,已經試過了圖片的簡單處理,人臉識別,年齡性別預測。
而視頻的處理呢,其實就是吧視頻流對應的每一幀的圖片拿出來識別一下,然後畫點東西上去,再顯示出來。
大部分的解釋內容都在代碼的註釋裏面了,這種學習使用方法的看代碼是最直觀的。
顯示使用swing的就行了。
(由於電腦性能是視頻質量的問題,限制爲5幀識別一次,不然好卡)
代碼:加載視頻輸出,調用圖像識別
代碼是拆分進行說明的,但是所有代碼都會完整提供的。
之後也會上傳到資源,不過那個要積分的。在博客裏面copy出來整理一下也就可以了。
public class videoTest extends JPanel {
private BufferedImage mImg;
public static void main(String[] args) {
//使用Swing生成GUI
JFrame frame = new JFrame("camera");
try {
//加載opencv庫 本地上下文
System.load("D:\\OpenCV\\opencv\\build\\java\\x64\\opencv_java411.dll");
//加載普通視頻
// VideoCapture capture = new VideoCapture("D://VID_20181110_213415.mp4");
//獲取攝像頭視頻流 參數爲第幾個視頻設備
VideoCapture capture = new VideoCapture(0);
System.out.println("獲取視頻流情況:" + capture.isOpened());
int height = (int) capture.get(Videoio.CAP_PROP_FRAME_HEIGHT);
int width = (int) capture.get(Videoio.CAP_PROP_FRAME_WIDTH);
if (height == 0 || width == 0) {
throw new Exception("camera not found!");
}
// 創建swing 窗口
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
videoTest panel = new videoTest();
frame.setContentPane(panel);
frame.setVisible(true);
frame.setSize(width + frame.getInsets().left + frame.getInsets().right,
height + frame.getInsets().top + frame.getInsets().bottom);
Mat capImg = new Mat();
Mat temp = new Mat();
long num = 1;
while (frame.isShowing()) {
num++;
//獲取視頻幀
capture.read(capImg);
//旋轉 用於調整治療頸椎病的視頻
// Point center = new Point(capImg.width() / 2.0, capImg.height() / 2.0);
// Mat affineTrans = Imgproc.getRotationMatrix2D(center, 90.0, 1.0);
// Imgproc.warpAffine(capImg, capImg, affineTrans, capImg.size(), Imgproc.INTER_NEAREST);
// 由於電腦性能問題,只是每n幀進行一次識別
if (num % 10 == 0) {
//轉換爲灰度圖
Imgproc.cvtColor(capImg, temp, Imgproc.COLOR_RGB2GRAY);
//識別人臉
Mat image = detectFace(capImg);
//轉爲圖像顯示
panel.mImg = panel.mat2BI(image);
panel.repaint();
} else {
//對未進行識別的幀,進行之前一次識別數據的重繪 參數太多很亂,建議大屏幕查看
if (OpenCVTools.getRect() != null) {
// for (Rect rect : OpenCVTools.getRect()) {
for (int i = 0; i < OpenCVTools.getRect().size(); i++) {
Imgproc.rectangle(capImg, new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y),
new Point(OpenCVTools.getRect().get(i).x + OpenCVTools.getRect().get(i).width,
OpenCVTools.getRect().get(i).y + OpenCVTools.getRect().get(i).height),
new Scalar(0, 255, 0), 1);
Imgproc.putText(capImg, "age:" + OpenCVTools.rest_age_sex.get(i).get(0),
new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y), Imgproc.FONT_HERSHEY_PLAIN, 0.8,
new Scalar(0, 255, 0), 1);
Imgproc.putText(capImg, "sex:" + OpenCVTools.rest_age_sex.get(i).get(1),
new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y - 10), Imgproc.FONT_HERSHEY_PLAIN, 0.8,
new Scalar(0, 255, 0), 1);
if(Double.valueOf(OpenCVTools.rest_age_sex.get(i).get(2) ) > 0.8){
Imgproc.putText(capImg, "like:"+"WuBinHong",
new Point(OpenCVTools.getRect().get(i).x, OpenCVTools.getRect().get(i).y-20), Imgproc.FONT_HERSHEY_PLAIN,0.8,
new Scalar(0, 255, 0), 1);
}
}
}
//轉爲圖像顯示
panel.mImg = panel.mat2BI(capImg);
panel.repaint();
}
}
capture.release();
frame.dispose();
} catch (Exception e) {
System.out.println("有問題");
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
System.out.println(sw.toString());
} finally {
System.out.println("Exit");
frame.dispose();
}
System.exit(0);
}
/**
* 轉換圖像
*
* @param mat
* @return
*/
private BufferedImage mat2BI(Mat mat) {
int dataSize = mat.cols() * mat.rows() * (int) mat.elemSize();
byte[] data = new byte[dataSize];
mat.get(0, 0, data);
int type = mat.channels() == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;
if (type == BufferedImage.TYPE_3BYTE_BGR) {
for (int i = 0; i < dataSize; i += 3) {
byte blue = data[i + 0];
data[i + 0] = data[i + 2];
data[i + 2] = blue;
}
}
BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data);
return image;
}
@Override
public void paint(Graphics g) {
if (mImg != null) {
g.drawImage(mImg, 0, 0, mImg.getWidth(), mImg.getHeight(), this);
}
}
}
OpenCV的視頻處理預設方法,還要很多,比如每一幀的回調函數,延遲信息,運行狀態等等。由於這裏想弄的不在視頻處理,所以只是用最簡單的獲取每一幀進行處理。
代碼:圖像中人臉識別
由於使用攝像頭拍攝的時候,很多時候拍攝到的都是側臉,所以只使用一個正臉識別感覺不夠。如果還有其他的識別需求,可以看看OpenCV的模型文件夾下,還有很多現成的識別模型(眼睛,身體,等等)。
這裏只做了側臉+正臉的識別。
/**
* opencv實現人臉識別,同時檢測到人臉和人眼時才截圖
* @param img 需要識別的圖像
*/
public static Mat detectFace(Mat img) {
OpenCVTools.setRect(new ArrayList<Rect>());
OpenCVTools.rest_age_sex = new ArrayList<ArrayList<String>>();
System.out.println("Running DetectFace ... ");
// 從配置文件lbpcascade_frontalface.xml中創建一個人臉識別器,該文件位於opencv安裝目錄中
CascadeClassifier faceDetector = new CascadeClassifier("D:\\OpenCV\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml");
CascadeClassifier profilefaceDetector = new CascadeClassifier("D:\\OpenCV\\opencv\\sources\\data\\haarcascades\\haarcascade_profileface.xml");
// 在圖片中檢測人臉
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(img, faceDetections);
System.out.println(String.format("Detected %s faces", faceDetections.toArray().length));
Rect[] rects = faceDetections.toArray();
// 獨立出來的,進行對識別人臉圖像的處理
pointRect(rects, img);
// 在圖片中檢測側臉
MatOfRect profilefaceDetections = new MatOfRect();
profilefaceDetector.detectMultiScale(img, profilefaceDetections);
System.out.println(String.format("Detected %s profileface", profilefaceDetections.toArray().length));
Rect[] profilefaceRects = profilefaceDetections.toArray();
pointRect(profilefaceRects, img);
return img;
}
代碼:人臉年齡性別預測,繪製信息
這裏的性別預測使用的方法也就是上一篇的內容。
/**
* 繪製人臉以及預測年齡圖像信息
* 也可以進行人臉截取保存
* @param rects 識別到的人臉列表
* @param img 原始識別圖片
*/
private static void pointRect(Rect[] rects, Mat img) {
if (rects != null && rects.length >= 1) {
for (Rect rect : rects) {
OpenCVTools.getRect().add(rect);
//給臉 畫矩形
Imgproc.rectangle(img, new Point(rect.x, rect.y),
new Point(rect.x + rect.width, rect.y + rect.height),
new Scalar(0, 255, 0), 1);
//年齡識別
String age = OpenCVTools.predict_age(img.submat(rect));
Imgproc.putText(img, "age:" + age, new Point(rect.x, rect.y), Imgproc.FONT_HERSHEY_PLAIN, 0.8, new Scalar(0, 255, 0), 1);
//性別識別
String sex = OpenCVTools.predict_gender(img.submat(rect));
Imgproc.putText(img, "sex:" + sex, new Point(rect.x, rect.y - 10), Imgproc.FONT_HERSHEY_PLAIN, 0.8, new Scalar(0, 255, 0), 1);
ArrayList<String> age_sex = new ArrayList<String>();
age_sex.add(age);
age_sex.add(sex);
//圖像對比
Double compareNum = OpenCVTools.compare_image(OpenCVTools.faceImg, img.submat(rect));
Double compareNum2 = OpenCVTools.compare_image(OpenCVTools.faceImg2, img.submat(rect));
age_sex.add(compareNum > compareNum2 ? compareNum.toString() : compareNum2.toString());
if (compareNum > 0.8 || compareNum2 > 0.8) {
Imgproc.putText(img, "like:" + "One People", new Point(rect.x, rect.y - 20), Imgproc.FONT_HERSHEY_PLAIN, 0.8, new Scalar(0, 255, 0), 1);
} else {
//保存不被識別的人臉照片
// save(img, rect, "D:\\ijworkspace\\meaen_test\\data\\face\\" + new Random().nextInt(2000) + ".jpg");
}
//保存 識別的信息,由於其他幀的重繪
OpenCVTools.rest_age_sex.add(age_sex);
}
}
}
/**
* opencv將人臉進行截圖並保存
* @param img
*/
private static void save(Mat img, Rect rect, String outFile) {
Mat sub = img.submat(rect);
Mat mat = new Mat();
Size size = new Size(300, 300);
Imgproc.resize(sub, mat, size);
Imgcodecs.imwrite(outFile, mat);
}
代碼:補充的一下代碼
這裏的參數是會用到的,作爲識別幀保存的識別數據,用於重繪。
public class OpenCVTools {
public static Mat faceImg = Imgcodecs.imread("D:\\ijworkspace\\meaen_test\\data\\1625.jpg");
public static Mat faceImg2 = Imgcodecs.imread("D:\\ijworkspace\\meaen_test\\data\\914.jpg");
private static List<Rect> rect = null;
public static ArrayList<ArrayList<String>> rest_age_sex = new ArrayList<ArrayList<String>>();
public static java.util.List<Rect> getRect() {
return rect;
}
public static void setRect( java.util.List<Rect> rect) {
OpenCVTools.rect = rect;
}
// 文章的一下封裝的靜態方法都是放在這個類裏面的,只是爲了文章需要拆分開了。
}
小結一下
這個視頻流進行圖像識別的代碼,的結果就是攝像頭實時圖像識別,代碼中還有一個簡單的圖像對比,這樣就可以蠻當做一個識別攝像頭的程序使用了。【最終效果就是前一篇文字的圖片,只不過這個是視頻】
比如,使用老闆的頭像數據做對比,在檢查到比配度很好的情況下,提示些信息,或者切換到桌面 罒ω罒
2019-11-05 小杭
使用攝像頭來進行實時圖像識別完成了,學習結束了。(然而並沒有
接下來,就要研究一下識別模型和深度學習訓練和算法了。。。