Не удается создать экземпляр OpenCV-4.0.1 CascadeClassifier в проекте Grails 2.4.4 - PullRequest
0 голосов
/ 16 февраля 2019

Я хочу обрезать фотографии сотрудников 1х1 из файла их загрузки, чтобы они были настроены в качестве аватара в приложении 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

Что я сделал:

  1. Я установил OpenCV 4.0.1 с их сайта: https://opencv.org/releases.html и распаковал их дистрибутив на D:\opencv\4.0.1.
  2. Я включил это в свой путь к среде: D:\opencv\4.0.1\build\java\x64
  3. Так как я не могу найти официальную ссылку для зависимостей Maven, я скопировал jar, который я нашел на D:\opencv\4.0.1\build\java, в свои граалы libкаталог.
  4. Я уже пытался скопировать все dll и xml в C:\Windows\system32.
  5. Думая, что jar в дистрибутиве может быть "неисправен", я заменяю егос этим пакетом от compile "org.bytedeco.javacpp-presets:opencv:4.0.1-1.4.4" до BuildConfig.groovy, все еще оба вызывают ошибки в одной и той же строке.
  6. Я проверил, FRONTAL_FACE_XML and ORIGINAL_PICTURE - это и правильный путь к файлу, и они.

1 Ответ

0 голосов
/ 15 марта 2019

Мы будем использовать OpenCV с использованием VC15 в Windows для этого примера.Мне еще предстоит узнать, как перенести это в 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

Необязательно:

  1. Перейти в каталог %VC_DLL_PATH%.
  2. Создайте каталог с именем debug (или как вам удобнее) и переместите туда все файлы *d.dll.Эти файлы, оканчивающиеся на *d.dll, могут вызвать ошибку при их запуске, поскольку они ищут отладку vc15 dll s, не являющуюся частью базовой Visual C ++ 2015.

Конфигурация:

  1. Создайте каталог в %APPLICATION_PATH%\src каталоге с именем files (или по вашему выбору).Это должно быть похоже на это.

    %APPLICATION_PATH% ├── bin\ ├── grails-app\ ├── lib\ ├── ... ├── src\ │ ├── groovy\ │ ├── java\ │ └── files\ --new directory └── ...

  2. Скопируйте все *.dll s из %JAVA_DLL_PATH% и %VC_DLL_PATH% каталогов во вновь созданный каталог %APPLICATION_PATH%\src\files.

  3. Включите вновь созданный каталог в ваш .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 s и включили каталоги библиотек в системупуть, нам все еще нужно включить его в путь библиотеки JVM Tomcat.Откройте свой 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...