我想从上传文件中裁剪1x1员工图片,以将其设置为我的Grails应用程序上的头像。我听说OpenCV做得很好,所以我在ImageService
内部使用了它。问题在于,它似乎无法找到(或读取)所需的CascadeClassifier
XML文件:
class ImageService {
final String FRONTAL_FACE_XML = "D:\\Devtools\\opencv\\build\\etc\\lbpcascades\\lbpcascade_frontalface_improved.xml"
final String ORIGINAL_PICTURE = "D:\\Projects\\opencv\\grails-app\\assets\\4fc30smaegvq0z3mvgm9yhf6vtv9kv8bgryi9x08wuada8jxu3.jpg"
final String CROPPED_PICTURE = "D:\\Projects\\opencv\\grails-app\\assets\\4fc30smaegvq0z3mvgm9yhf6vtv9kv8bgryi9x08wuada8jxu3_100.jpg"
void opencvtest() {
// Before I placed the OpenCV dll in the environment path, this line causes an error.
System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
// UnsatisfiedLinkError here
CascadeClassifier faceDetector = new CascadeClassifier(this.getClass().getResource("lbpcascade_frontalface_improved.xml").getPath());
// Same error as well.
// File cascadeFile = new File(FRONTAL_FACE_XML);
// CascadeClassifier faceDetector = new CascadeClassifier(cascadeFile.getAbsolutePath());
// And also here.
// CascadeClassifier faceDetector = new CascadeClassifier(FRONTAL_FACE_XML);
Mat image = HighGui.imread(ORIGINAL_PICTURE)
faceDetector.detectMultiScale(image, face_Detections)
Rect rect_Crop = null
for (Rect rect : face_Detections.toArray()) {
Core.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0))
rectCrop = new Rect(rect.x, rect.y, rect.width, rect.height)
}
Mat image_roi = new Mat(image, rectCrop)
HighGui.imwrite(CROPPED_PICTURE, image_roi)
return
}
}
并导致以下错误:
java.lang.UnsatisfiedLinkError org.opencv.objdetect.CascadeClassifier.CascadeClassifier_0(Ljava / lang / String;)J
环境:Windows 7,Java 1.8,Grails 2.4.4
我所做的事情:
D:\opencv\4.0.1
上解压缩了发行版。D:\opencv\4.0.1\build\java\x64
jar
上找到的D:\opencv\4.0.1\build\java
复制到了grails的lib
目录中。dll
和xml
复制到C:\Windows\system32
。jar
可能是“错误的”,我将其替换为compile "org.bytedeco.javacpp-presets:opencv:4.0.1-1.4.4"
内的这个软件包BuildConfig.groovy
,但仍然会在同一行上引起错误。FRONTAL_FACE_XML
和ORIGINAL_PICTURE
是否都是正确的文件路径,并且它们都是正确的。答案 0 :(得分:0)
在此示例中,我们将在Windows上使用通过VC15的OpenCV。我尚未学习如何在Linux上移植它。
APPLICATION_PATH = D:\application
JAVA_DLL_PATH = D:\opencv\4.0.1\build\java\x64
VC_DLL_PATH = D:\opencv\4.0.1\build\x64\vc15\bin
%VC_DLL_PATH%
目录。debug
(或您喜欢的名称)的目录,并将所有*d.dll
文件移到该目录。这些以*d.dll
结尾的文件在运行时可能会导致错误,因为它正在寻找不属于基本Visual C ++ 2015的vc15调试dll
。在%APPLICATION_PATH%\src
目录中创建一个名为files
的目录(或任何您喜欢的目录)。应该与此类似。
%APPLICATION_PATH% ├── bin\ ├── grails-app\ ├── lib\ ├── ... ├── src\ │ ├── groovy\ │ ├── java\ │ └── files\ --new directory └── ...
将*.dll
和%JAVA_DLL_PATH%
目录中的所有%VC_DLL_PATH%
复制到新创建的目录%APPLICATION_PATH%\src\files
。
将新创建的目录包括到您的.classpath
文件中。
<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry excluding="spring/" kind="src" path="grails-app/conf"/> ... <classpathentry kind="src" path="src/files"/> ... </classpath>
%APPLICATION_PATH%
中创建另一个名为files
的目录(或您喜欢的任何一个)。
%APPLICATION_PATH% ├── bin\ ├── grails-app\ ├── files\ --new directory └── ...
在此处复制将要使用的CascadeClassifiers
xml文件。它们位于\opencv\4.0.1\build\etc
目录中。
即使在dll
中复制了%APPLICATION_PATH%
之后,我们仍然需要将它们包括在系统路径中。编辑环境变量,并将%JAVA_DLL_PATH%
和%VC_DLL_PATH%
目录包括到PATH
中。
此外,即使我们复制了dll
并将库目录包含在系统路径中,我们仍然需要将其包含在Tomcat JVM库路径中。打开您的BuildConfig.groovy
并添加以下内容:
grails.tomcat.jvmArgs = ["-Djava.library.path=D:\opencv\4.0.1\build\x64\vc15\bin;D:\opencv\4.0.1\build\java\x64"]
Config.groovy
中。
openCV { cascadeClassifiers = "D:\\application\\files\\opencv" home = "D:\\opencv\\4.0.1\\build\\x64\\vc15\\bin" java = "D:\\opencv\\4.0.1\\build\\java\\x64" }
您需要创建*.java
文件,而不是*.groovy
中的%APPLICATION_PATH%\src\java
文件。我尚未检查为什么它不适用于.groovy
文件。
import grails.util.Holders;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.UUID;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.FileUtils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.objdetect.CascadeClassifier;
public class ImageService {
static final String[] OPENCV_SUPPORTED_EXTENSION = new String[]{"bmp", "dib", "jp2", "jpe", "jpeg", "jpg", "pbm", "pgm", "png", "ppm", "ras", "sr", "tif", "tiff"};
public static File cropImage(final File originalFile) {
loadLibrary();
return cropImage(originalFile, originalFile.getParentFile(), getCascadeClassifiers());
}
public static File cropImage(final File originalFile, final ArrayList<CascadeClassifier> cascadeClassifiers) {
return cropImage(originalFile, originalFile.getParentFile(), cascadeClassifiers);
}
public static File cropImage(final File originalFile, final File targetDirectory) {
loadLibrary();
return cropImage(originalFile, originalFile.getParentFile(), getCascadeClassifiers());
}
public static File cropImage(final File originalFile, final File targetDirectory, final ArrayList<CascadeClassifier> cascadeClassifiers) {
ArrayList<File> siblingFiles = getFaces(originalFile, cascadeClassifiers);
File maxFile = null;
int maxWidth = 0;
for(int x = 0; x < siblingFiles.size(); x++) {
Mat image = Imgcodecs.imread(siblingFiles.get(x).getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
if(image.width() > maxWidth) {
maxFile = siblingFiles.get(x);
maxWidth = image.width();
}
}
File croppedFile = null;
if(maxFile != null) {
croppedFile = new File(targetDirectory.getAbsolutePath() +
File.separator +
originalFile.getName());
try {
FileUtils.copyFile(maxFile, croppedFile);
}
catch(IOException e) {}
}
for(int y = 0; y < siblingFiles.size(); y++) {
siblingFiles.get(y).delete();
}
System.gc();
System.runFinalization();
return croppedFile;
}
public static ArrayList<CascadeClassifier> getCascadeClassifiers() {
ArrayList<CascadeClassifier> classifiers = new ArrayList<CascadeClassifier>();
final String[] cascadeSupportedExtensions = new String[]{"xml"};
final String cascadeClassifierPath = Holders.getFlatConfig().get("openCV.cascadeClassifiers").toString();
File cascadeClassifierDirectory = new File(cascadeClassifierPath);
ArrayList<File> detectors = new ArrayList<File>(FileUtils.listFiles(cascadeClassifierDirectory, cascadeSupportedExtensions, false));
for(int y = 0; y < detectors.size(); y++) {
CascadeClassifier faceDetector = new CascadeClassifier();
faceDetector.load(detectors.get(y).getAbsolutePath());
classifiers.add(faceDetector);
}
return classifiers;
}
public static ArrayList<File> getFaces(final File originalFile) {
loadLibrary();
return getFaces(originalFile, getCascadeClassifiers());
}
public static ArrayList<File> getFaces(final File originalFile, final ArrayList<CascadeClassifier> cascadeClassifiers) {
File temporaryFile = new File(originalFile.getParentFile().getAbsolutePath() +
File.separator +
UUID.randomUUID().toString() +
"." + FilenameUtils.getExtension(originalFile.getName()));
try {
FileUtils.copyFile(originalFile, temporaryFile);
}
catch(IOException e) {}
final int frame = 9;
final int offset = 8;
int rotateCounter = 0;
Integer marginX, marginY, marginWidth, marginHeight;
Integer pxPerOffset, excess;
ArrayList<File> siblingFiles = new ArrayList<File>();
while(rotateCounter < 4) {
if(rotateCounter > 0) {
Mat image = Imgcodecs.imread(temporaryFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
Core.transpose(image, image);
Core.flip(image, image, 1);
Imgcodecs.imwrite(temporaryFile.getAbsolutePath(), image);
image.release();
}
for(int y = 0; y < cascadeClassifiers.size(); y++) {
CascadeClassifier faceDetector = cascadeClassifiers.get(y);
Mat image = Imgcodecs.imread(temporaryFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale(image, faceDetections);
Rect defaultRect = null;
Rect marginRect = null;
Rect[] facesRect = faceDetections.toArray();
for(int z = 0; z < facesRect.length; z++) {
defaultRect = facesRect[z];
pxPerOffset = defaultRect.width / frame;
marginX = defaultRect.x - (pxPerOffset * offset);
marginY = defaultRect.y - (pxPerOffset * offset);
marginWidth = defaultRect.width + (offset * pxPerOffset * 2);
marginHeight = defaultRect.height + (offset * pxPerOffset * 2);
excess = Math.max(
0 - marginX,
Math.max(0 - marginY,
Math.max(marginX + marginWidth - image.width(),
Math.max(marginY + marginHeight - image.height(), 0)))
);
if(excess > 0) {
marginX += excess;
marginY += excess;
marginWidth -= excess * 2;
marginHeight -= excess * 2;
}
marginRect = new Rect(marginX, marginY, marginWidth, marginHeight);
Mat imageWithMargin = new Mat(image, marginRect);
String croppedFileName = temporaryFile.getParentFile().getAbsolutePath() +
File.separator +
UUID.randomUUID().toString() + "_" +
y + "_" +
rotateCounter + "_" +
z + "." +
FilenameUtils.getExtension(temporaryFile.getName());
Imgcodecs.imwrite(croppedFileName, imageWithMargin);
siblingFiles.add(new File(croppedFileName));
imageWithMargin.release();
}
image.release();
}
rotateCounter++;
}
temporaryFile.delete();
return siblingFiles;
}
public static void loadLibrary() {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
final String[] urls = new String[]{
Holders.getFlatConfig().get("openCV.java").toString(),
Holders.getFlatConfig().get("openCV.home").toString()
};
final String[] validExtensions = new String[]{"dll"};
ArrayList<File> dlls = new ArrayList<File>();
for(int y = 0; y < urls.length; y++) {
dlls.addAll(FileUtils.listFiles(new File(urls[y]), validExtensions, false));
}
for(int y = 0; y < dlls.size(); y++) {
for(int z = 0; z < validExtensions.length; z++) {
System.loadLibrary(dlls.get(y).getName().replace("." + validExtensions[z], ""));
System.load(dlls.get(y).getAbsolutePath());
}
}
}
}
然后将其用于常规服务类。
class TestService {
def openCVTest() {
File picture = new File("D:\\original.jpg");
File savingDirectory = new File("D:\\");
ImageService.cropImage(picture, savingDirectory);
return;
}
}