准备好痛苦了没有?
痛苦之后是欢乐。因为必竟大多人还是用的WINDOWS来开发的居多。因此如果OpenCV无法在Win10下安装,一样不能起到普及作用。
而Windows下的编译安装OpenCV也是最痛苦的一件事。原因就在于在编译安装OpenCV时很多Extra的模块是被“墙”掉了。
因此整个过程一会这个错一会那个错,开发机电脑也不太好,导致一次编译,30多分钟,然后报了一堆错网上查了半天解决了一个,又来一个。
如此周而复始,让绝大多数开发者放弃了OpenCV。
本篇就是要着重讲述OpenCV在Windows10下的编译全过程和全部坑的排错。
截图-python3
截图-系统path环境变量
opencv在windows下任何版本的编译是如下过程的
千万不要去下载网上提供的已经编译好的这两个文件。如果你为了图个方便,网上去下载别人编译安装好的这两个文件
那么在学习过程中运行死机、运行报错、或者干脆一会header找不到一会什么.h文件找不到,不要怪我没有事先提醒。
opencv在编译时会需要很多第3方依赖包,这些额外包有这么大一陀,我给各位看一下。
每一个第3方安装包,都在墙外,你在编译安装时一个下载时被卡住、过几分钟后再Failed、再一个。。。。。。再一个。。。。。。这些包全部下载Failed后用了几十分钟,然后你直接会觉得有一种“生无可怜”的味道。
我是一个个自己看着编译报错时的log内容,一个个手动用百度云盘方式先下载后再按照了OpenCV编译安装时所需要的目录结构排列后,并且下载了OpenCV3.4.3的源码(不动任何东西)以及OpenCV3.4.3的contribute包。然后我把三个目录打成了一个包,做了一个脱机包给到大家,算是一种福利吧。
完整全套脱机包在此百度网盘内,请笑纳:
链接:https://pan.baidu.com/s/1mXOEfqykUT2TWk6OXtAEVA?pwd=ioti
提取码:ioti
--来自百度网盘超级会员V6的分享
下载后并解压是这样的一个文件目录结构内容把它放到一个叫opencvinstall的目录内。
3rdparty为opencv编译安装时必备,这里面我已经把我所有一个个幸苦下载好的文件按照opencv安装时的需要摆放好了。
随后我们单建一个文件夹叫opencvbuild。用来存cmake编译出来的opencv的编译输出的文件夹
届此,即完成了目录设置的准备了。
运行起来后是这样的界面
把我网盘里下载下来的3个文件夹中的“3rdparty”里的所有的内容
拷贝到opencv3.4.3.master即源码包下的.cache里,并按照提示覆盖掉里面3rdparty目录内的所有内容。
然后点一下这个界面中的【Configure】按钮。
然后cmake-gui会提示你选择visual c的编译器,你此时应该事先已经装好了visual studio 14 2015包含visual c++了,请选择visual studio 14 2015。
然后点【Finish】后你会得到一堆红色的列表,没有关系。跟着我的步骤一步步走,不会有问题的。
在红色的列表中进行相应的选择和填充
否则就要自己手工指定目录
把OPENCV_ENABLE_NONFREE打勾
记得这边指定的可是/opencvinstall/opencv_contrib-3.4.3/modules目录,很多人指定到了opencv_contrib-3.4.3这一层,会导致点【configure】按钮时出一个.h文件找不到的错的。
否则就手工指定。
然后再次点击【Configure】
然后看到此处的两个Java都要Success。
此时我们来看opencvbuild目录内,出现了这么多东西,好家伙。
此时,我们在进入下一步前需要做一件事,否则你一会会感觉要疯了。
那就是在之前OpenCV Java入门一 在MAC系统上安装OpenCV里,我说过:opencv会使用最新的javac compiler编译打包opencv for java。
在mac里它打出的jar包我们需要使用jdk17来运行,同时兼顾装有jdk1.8来运行最新版2021-12版的eclipse。
然后:
很可惜,windows下的eclipse 2021-12的compile version只支持到 jdk16,这和mac是有极大的区别的,它比mac上的东西慢了半拍。
因此,如果在windows里编译出来的opencv的jar包也是jdk17编译成的,你的eclipse就算是最新的也会在运行时报一个非常恶心的错“need class version 62, but it support to 61”。
所以,一开始我们就要在这个jar包被编译前就指定jdk编译版本把这个jar包用jdk1.8编译出来。这样我们在windows里就可以使用jdk1.8以及eclipse早期版(只要支持jdk1.8的)可以使用opencv jar了。
在opencvbuild即cmake-gui输出的目录中找到如下的这个文件
打开它,然后把这一行加上一个target="1.8":
改完后的build.xml全文如下范例
在cmake-gui界面点击[Generate]按钮
并得到成功的输出。
然后再点击【Open Project】按钮
此时,visual studio就被打开了。当全部的项目装载完后先把Debug改成Release,然后点击菜单里的“生成”->“生成解决方案”。
全部生成成功在我的台式机上大概也就用了4分钟。
然后在右边的这块树型project explorer界面中
点击【INSTALL】,按鼠标右键,选【仅用于项目】
过一会全部编译好的opencv就放置在了opencvbuild\install目录下了。
java用的两个文件生成在此opencvbuild\install\java
在Native library location处,点击【edit】按钮
把这个路径定位到D:\opencvbuild\install\java目录,这个目录里有opencv-343.jar和opencv_java343.dll文件。
在下面的classpath里点击右边的【Add External JARs】按钮。把它定位到D:\opencvbuild\install\java\opencv-343.jar文件。
在windows10的控制面板的系统设置里,在系统变量的Path里加入如下内容:
opencvbuild的\install\x64\vc14\bin
届此,OpenCV Java工程的环境已经全部搭建完毕。
我们来读入一张带有人脸的图片,然后在人脸上把他的眼晴部分给标识出来。
OpenCVUtil.java
package org.mk.opencv.util;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.io.File;
import org.apache.log4j.Logger;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
public class OpenCVUtil {
private static Logger logger = Logger.getLogger(OpenCVUtil.class);
public static Image matToImage(Mat matrix) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (matrix.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = matrix.channels() * matrix.cols() * matrix.rows();
byte[] buffer = new byte[bufferSize];
matrix.get(0, 0, buffer); // 获取所有的像素点
BufferedImage image = new BufferedImage(matrix.cols(), matrix.rows(), type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
return image;
}
public static List getFilesFromFolder(String folderPath) {
List fileList = new ArrayList();
File f = new File(folderPath);
if (f.isDirectory()) {
File[] files = f.listFiles();
for (File singleFile : files) {
fileList.add(singleFile.getPath());
}
}
return fileList;
}
public static String randomFileName() {
StringBuffer fn = new StringBuffer();
fn.append(System.currentTimeMillis()).append((int) (System.currentTimeMillis() % (10000 - 1) + 1))
.append(".jpg");
return fn.toString();
}
public static List getPicFromFolder(String rootPath) {
List fList = new ArrayList();
int fileNum = 0, folderNum = 0;
File file = new File(rootPath);
if (file.exists()) {
LinkedList list = new LinkedList();
File[] files = file.listFiles();
for (File file2 : files) {
if (file2.isDirectory()) {
// logger.info(">>>>>>文件夹:" + file2.getAbsolutePath());
list.add(file2);
folderNum++;
} else {
// logger.info(">>>>>>文件:" + file2.getAbsolutePath());
FileBean f = new FileBean();
String fileName = file2.getName();
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
File fParent = new File(file2.getParent());
String parentFolderName = fParent.getName();
f.setFileFullPath(file2.getAbsolutePath());
f.setFileType(suffix);
f.setFolderName(parentFolderName);
fList.add(f);
fileNum++;
}
}
File temp_file;
while (!list.isEmpty()) {
temp_file = list.removeFirst();
files = temp_file.listFiles();
for (File file2 : files) {
if (file2.isDirectory()) {
// System.out.println("文件夹:" + file2.getAbsolutePath());
list.add(file2);
folderNum++;
} else {
// logger.info(">>>>>>文件:" + file2.getAbsolutePath());
FileBean f = new FileBean();
String fileName = file2.getName();
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
File fParent = new File(file2.getParent());
String parentFolderName = fParent.getName();
f.setFileFullPath(file2.getAbsolutePath());
f.setFileType(suffix);
f.setFolderName(parentFolderName);
fList.add(f);
fileNum++;
}
}
}
} else {
logger.info(">>>>>>文件不存在!");
}
// logger.info(">>>>>>文件夹共有:" + folderNum + ",文件共有:" + fileNum);
return fList;
}
public static BufferedImage matToBufferedImage(Mat matrix) {
int cols = matrix.cols();
int rows = matrix.rows();
int elemSize = (int) matrix.elemSize();
byte[] data = new byte[cols * rows * elemSize];
int type;
matrix.get(0, 0, data);
switch (matrix.channels()) {
case 1:
type = BufferedImage.TYPE_BYTE_GRAY;
break;
case 3:
type = BufferedImage.TYPE_3BYTE_BGR;
// bgr to rgb
byte b;
for (int i = 0; i < data.length; i = i + 3) {
b = data[i];
data[i] = data[i + 2];
data[i + 2] = b;
}
break;
default:
return null;
}
BufferedImage image2 = new BufferedImage(cols, rows, type);
image2.getRaster().setDataElements(0, 0, cols, rows, data);
return image2;
}
public static Mat bufferedImageToMat(BufferedImage bi) {
Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC3);
byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
mat.put(0, 0, data);
return mat;
}
}
ImageViewer.java
这是一个JFrame,用于显示OpenCV的Mat格式图像用的通用类,后面好几个例子都会用到这个通用显示图像类
package org.mk.opencv;
import org.mk.opencv.util.OpenCVUtil;
import org.opencv.core.Mat;
import javax.swing.*;
import java.awt.*;
public class ImageViewer {
private JLabel imageView;
private Mat image;
private String windowName;
/**
* 如果使用junit测试时调用该方法,图像会一闪而过,可通过sleep()等方式暂时显示
*
* @param
*/
private JFrame frame = null;
public ImageViewer() {
frame = createJFrame(windowName, 800, 600);
}
public ImageViewer(Mat image) {
this.image = image;
}
/**
* @param image 要显示的mat
* @param windowName 窗口标题
*/
public ImageViewer(Mat image, String windowName) {
frame = createJFrame(windowName, 1024, 768);
this.image = image;
this.windowName = windowName;
}
public void setTitle(String windowName) {
this.windowName = windowName;
}
public void setImage(Mat image) {
this.image = image;
}
/**
* 图片显示
*/
public void imshow() {
setSystemLookAndFeel();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 用户点击窗口关闭
if (image != null) {
Image loadedImage = OpenCVUtil.matToImage(image);
// JFrame frame = createJFrame(windowName, image.width(), image.height());
imageView.setIcon(new ImageIcon(loadedImage));
frame.pack();
// frame.setLocationRelativeTo(null);
// frame.setVisible(true);
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 用户点击窗口关闭
}
}
private void setSystemLookAndFeel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
private JFrame createJFrame(String windowName, int width, int height) {
JFrame frame = new JFrame(windowName);
imageView = new JLabel();
final JScrollPane imageScrollPane = new JScrollPane(imageView);
imageScrollPane.setPreferredSize(new Dimension(width, height));
frame.add(imageScrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
return frame;
}
}
DetectEye.java
这个类就是用来探测人眼的类了。
它作这么几件事:
package org.mk.opencv;
import org.opencv.core.Core;
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 DetectEye {
private static Mat detectEye(Mat src) {
Mat dst = src.clone();
// 绾ц仈鍒嗙被鍣ㄧ被鐢ㄤ簬瀵硅薄妫�娴嬨��
CascadeClassifier objDetector = new CascadeClassifier(
"D:\\opencv\\opencv3.4.3\\install-mk-pc-with-xfeatures2d\\etc\\haarcascades\\haarcascade_eye.xml");
MatOfRect objDetections = new MatOfRect();
objDetector.detectMultiScale(dst, objDetections);
if (objDetections.toArray().length <= 0) {
return src;
}
for (Rect rect : objDetections.toArray()) {
Imgproc.rectangle(dst, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height),
new Scalar(0, 0, 255), 2);
}
return dst;
}
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat src = Imgcodecs.imread("D:\\opencv-demo\\anold.jpg");
if (src.dataAddr() == 0) {
System.out.println("打开文件出错");
}
Mat eyeDtMat = detectEye(src);
ImageViewer imageViewer = new ImageViewer(eyeDtMat, "第一幅图片");
imageViewer.imshow();
}
}
运行后效果如下