Проблемы с обрезкой и изменением размера изображения

Я пытаюсь создать миниатюру для большего изображения. Я использую Jcrop, чтобы получить координаты обрезки, но не могу включить это в правильно обрезанное миниатюрное изображение. Я правильно настроил Jcrop, и он отправляет координаты xy вместе с размером поля, но я не могу понять, как это обрезать.

У меня есть путь к изображению на моем сервере, а также 4 координаты, а также ширина и высота квадратного поля, которое они создают (я заблокировал соотношение сторон 1: 1, так как мне нужны квадратные миниатюры). Затем я отправляю это через Ajax в свой сценарий обрезки PHP, но я не могу заставить его обрезать на основе того, что установлено.

Это то, что у меня есть до сих пор:

public function Crop($file, $crop) {

    $height = $width = 180;
    $ratio = $width / $height;
    $pos = strrpos($file, '.');
    $name = substr($file, 0, $pos);
    $ext = strtolower(substr($file, $pos));

    if( ! in_array($ext, array('.gif', '.jpg', '.jpeg', '.png'))) {
        return 'INVALID_EXT';
    }

    // When loading the image we check to see if the first character in file is a slash, and if so remove it as the last character of root is a slash.
    $src = ROOT . (in_array(substr($file, 0, 1), array('/', '\\')) ? substr($file, 1) : $file);
    $srcRes = imagecreatefromstring(file_get_contents($src));
    if( ! $srcRes) {
        return 'INVALID_FILE';
    }
    $srcWidth = imagesx($srcRes);
    $srcHeight = imagesy($srcRes);
    $srcRatio = $srcWidth / $srcHeight;
    $dstRes = imagecreatetruecolor($crop['w'], $crop['h']);

    if($ext == '.gif') {
        $dstBg = imagecolorallocate($dstRes, 0, 0, 0);
        imagecolortransparent($dstRes, $dstBg);
    } elseif($ext == '.png') {
        $dstBg = imagecolorallocate($dstRes, 0, 0, 0);
        imagecolortransparent($dstRes, $dstBg);
        imagealphablending($dstRes, FALSE);
        imagesavealpha($dstRes, TRUE);
    }

    $srcX = 0;
    $srcY = 0;
    if($srcRatio > $ratio) {
        $tmpWidth = $srcHeight * $ratio;
        $tmpHeight = $srcHeight;
        $srcX = ($srcWidth - $tmpWidth) / 2;
        $srcY = 0;
    } else {
        $tmpWidth = $srcWidth;
        $tmpHeight = $srcWidth / $ratio;
        $srcX = 0;
        $srcY = ($srcHeight - $tmpHeight) / 2;
    }

    imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], $crop['w'], $crop['h'], $tmpWidth, $tmpHeight);

    $dst = ROOT . (in_array(substr($name, 0, 1), array('/', '\\')) ? substr($name, 1) : $name) . '-thumb' . $ext;
    if($ext == '.gif') {
        $try = imagegif($dstRes, $dst);
    } elseif($ext == '.jpg' || $ext == '.jpeg') {
        $try = imagejpeg($dstRes, $dst, 80);
    } elseif($ext == '.png') {
        $try = imagepng($newThumbImageResource, $dst);
    }

    if( ! $try) {
        return 'CREATE_ERR';
    }

    return 'SUCCESS';

}

Я пытался изменить все что угодно в imagecopyresampled, но не могу заставить его обрезаться в соответствии с тем, что отправляет Jcrop. Если я использую $srcWidth и $srcHeight, он сохраняет соотношение сторон исходного изображения и сжимает его в квадрат 180 пикселей. Но, похоже, он не изменяет его размер или не обрезает его в нужном месте.

Может кто-нибудь, пожалуйста, помогите мне решить, какие цифры я должен использовать где? Я бьюсь об это головой весь день.


ИЗМЕНИТЬ

Вот остальная часть кода. Сначала JavaScript, который запускает JCrop. Он запускается после загрузки файла Ajax для создания миниатюры этого изображения:

Это загрузка функции Ajax, которая в конце вызывает обрезку.

$(function () {

    'use strict';
    // Change this to the location of your server-side upload handler:
    var url = '/eshop/library/ajax/ajax.file-upload.php';
    var uploadDir = 'prodimages/';

    $('.listing-image').fileupload({
        url: url,
        dataType: 'json',
        autoUpload: true,
        acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
        maxFileSize: 1000000, // 1 MB
        // Enable image resizing, except for Android and Opera,
        // which actually support image resizing, but fail to
        // send Blob objects via XHR requests:
        disableImageResize: /Android(?!.*Chrome)|Opera/
            .test(window.navigator.userAgent),
        previewMaxWidth: 120,
        previewMaxHeight: 120,
        previewCrop: true,
        paramName: 'files[]',
        formData: {uploadDir: uploadDir}
    })/*.on('fileuploadprocessalways', function (e, data) {
        var index = data.index;
        var file = data.files[index];
        $(this).html(file.preview);
    })*/.on('fileuploadprogressall', function (e, data) {
        var progress = parseInt(data.loaded / data.total * 100, 10);
        $('.listing-progress', this).css(
            'width',
            progress + '%'
        );
    }).on('fileuploaddone', function (e, data) {
        var file = data.result.files[0];
        var html = '<div class="listing-preview">\
            <img src="' + file.thumbnailUrl + '" data-name="' + file.name + '">\
            <div class="listing-preview-delete">Delete</div>\
        </div>';
        $(this).html(html).data('delete-url', file.deleteUrl).css('padding', 0);

        Crop('/' + uploadDir + file.name, $(this).prop('id'));

    });

});

Это отправляет сведения об урожае в PHP-скрипт выше через Ajax.

$(document).on('click', '.crop-btn', function() {

    var data = {
        file: $(this).data('src'),
        crop: jcrop.tellSelect()
    }
    ShowLoadingById('crop-loading');
    AjaxHandler('/eshop/library/ajax/ajax.product-crop-thumb.php', data, 'POST', true);

});

Окно кадрирования представляет собой лайтбокс, эта функция центрирует его по вертикали и изменяет размер изображения, если оно по вертикали больше доступного пространства.

function CentreCrop() {

    var m = ($(window).height() - ($('.crop > div').height() + 60)) / 2;
    $('.crop > div').css('margin-top', m);

    if($('#crop-img').height() > $('.crop > div').height() - 30) {
        $('#crop-img-container').height($('.crop > div').height() - 30);
    }

}

Это начальная функция, которая сохраняет файл для обрезки и вызывает рабочий процесс, если он не запущен.

var toBeCropped = [];
var working = false;
function Crop(file, id) {

    toBeCropped.push({path: file, id: id});

    if( ! working) {
        working = true;
        CropWorker();
    }

}

Это функция, которую я запускаю, когда обрезка завершена, чтобы уничтожить jcrop и очистить лайтбокс обрезки, готовый к обрезке следующего изображения.

function CropSuccess() {

    $('.crop').fadeOut(250, function() {

        jcrop.destroy();
        $(this).html('');
        CropWorker();

    });

}

Это рабочий процесс, который фактически создает содержимое в лайтбоксе и инициирует jcrop.

function CropWorker() {

    if(toBeCropped.length > 0) {
        file = toBeCropped.shift();
        html = '<div>\
            <div id="crop-img-container" class="row-fluid">\
                <img id="crop-img" src="' + file.path + '">\
            </div>\
            <div class="row-fluid">\
                <div class="span3 offset9">\
                    <button class="span12 btn crop-btn" data-id="' + file.id + '" data-src="' + file.path + '">Create Thumb</button>\
                </div>\
            </div>\
            <div class="row-fluid loading-screen" id="crop-loading">\
                <div>\
                    <h4>Cropping...</h4>\
                    <img src="/img/loading.gif">\
                </div>\
            </div>\
        </div>';
        $('.crop').html(html);
        $('.crop').fadeIn(250);
        $('#crop-img').load(function() {
            CentreCrop();
            $('#crop-img').Jcrop({
                aspectRatio: 1/1,
                bgColor: 'black',
                bgOpacity: 0.4,
                boxWidth: $('#crop-img').width(), // Only just recently added boxWidth and height to see if that would fix it, no difference with or without.
                boxHeight: $('#crop-img').height(),
                //maxSize: [300,300],
                minSize: [180,180]
            }, function() {
                jcrop = this;
                jcrop.setSelect([0,0,180,180]);
            });
        });
    } else {
        working = false;
    }

}

ОБНОВЛЕНИЕ

Часть проблемы, похоже, заключалась в изменении размера изображения. Я менял размер изображения, чтобы он соответствовал размеру экрана, хотя JCrop позаботится об этом за меня, но, похоже, вы должны сообщить JCrop исходные размеры изображения. Я добавил параметр истинного размера при инициализации JCrop, и, кажется, мы почти у цели.

Для небольших изображений, размером менее 1000 пикселей, обрезка работает отлично. Но для больших (1000 пикселей +) он создает черные изображения. Он не делает этого, когда я использую его с демо-скриптом JCrop, но единственная разница между ними заключается в том, что один выводит файл на экран, а другой сохраняет его. Я не вижу никакой другой разницы или не могу понять, почему это произошло.

ОБНОВЛЕНИЕ 2 Похоже, это влияет только на запуск кода через Ajax. Если я запускаю ту же самую функцию, просто публикуя на странице и запуская ее вверху, миниатюра каждый раз создается идеально, независимо от размера исходного изображения или размера поля, которое я рисую.


person Styphon    schedule 26.06.2014    source источник


Ответы (2)


В вашем коде $crop['w'] и $crop['h'] для источника;

$tmpWidth и $tmpHeight — пункт назначения;

поэтому вы должны переключать их в соответствии с функцией imagecopyresampled (http://php.net//manual/fr/function.imagecopyresampled.php).

imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], $tmpWidth, $tmpHeight, $crop['w'], $crop['h']);

изменить 2

Ваше целевое изображение должно быть установлено не с данными $crop, а с желаемым размером:

$dstRes = imagecreatetruecolor(180, 180);

И я не уверен, какое значение у вас есть для $tmpWidth и $tmpHeight, но они должны быть равны 180, так как это нужный вам размер:

imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], 180, 180, $crop['w'], $crop['h']);

Изменить 3

Хорошо, последняя попытка, у меня есть рабочий код для jcrop, но я не использую его внутри функции, он все еще работает так же. Я проверил построчно ваш код и вот что у меня есть. Я удалил все, что касается отношения, Jcrop управляет этим, не нужно делать это с PHP. Правильное изображение назначения, правильное imagecopyresampled, мне нравится :/

public function Crop($file, $crop) {

    $height = $width = 180;

    // your file type checking
    $pos = strrpos($file, '.');
    $name = substr($file, 0, $pos);
    $ext = strtolower(substr($file, $pos));

    if( ! in_array($ext, array('.gif', '.jpg', '.jpeg', '.png'))) {
        return 'INVALID_EXT';
    }

    // source image
    $src = ROOT . (in_array(substr($file, 0, 1), array('/', '\\')) ? substr($file, 1) : $file);
    $srcRes = imagecreatefromstring(file_get_contents($src));
    if( ! $srcRes) {
        return 'INVALID_FILE';
    }

    // destination image
    $dstRes = imagecreatetruecolor($width, $height);

    // file type transparence
    if($ext == '.gif') {
        $dstBg = imagecolorallocate($dstRes, 0, 0, 0);
        imagecolortransparent($dstRes, $dstBg);
    } elseif($ext == '.png') {
        $dstBg = imagecolorallocate($dstRes, 0, 0, 0);
        imagecolortransparent($dstRes, $dstBg);
        imagealphablending($dstRes, FALSE);
        imagesavealpha($dstRes, TRUE);
    }

    // bool imagecopyresampled ( resource $dst_image , resource $src_image , int $dst_x , int $dst_y , int $src_x , int $src_y , int $dst_w , int $dst_h , int $src_w , int $src_h )
    imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], $width, $height, $crop['w'], $crop['h']);

    $dst = ROOT . (in_array(substr($name, 0, 1), array('/', '\\')) ? substr($name, 1) : $name) . '-thumb' . $ext;
    if($ext == '.gif') {
        $try = imagegif($dstRes, $dst);
    } elseif($ext == '.jpg' || $ext == '.jpeg') {
        $try = imagejpeg($dstRes, $dst, 80);
    } elseif($ext == '.png') {
        $try = imagepng($newThumbImageResource, $dst);
    }

    if( ! $try) {
        return 'CREATE_ERR';
    }

    return 'SUCCESS';

}
person Aurélien Grimpard    schedule 26.06.2014
comment
Это не работает. Это не изменение размера изображения, поэтому кадрирование значительно увеличивается. Если я выберу кадрирование 300 x 300, оно должно обрезать его, а затем изменить его размер до 180 x 180, но это не так. - person Styphon; 26.06.2014
comment
Это тоже не работает. Верхний левый угол не соответствует тому месту, где я помещаю его на обрезчик, и он также не обрезается с правильным увеличением. - person Styphon; 27.06.2014
comment
Я скопировал ваш код и вставил его, чтобы убедиться, что он правильный, и все равно получил то же самое. Нет ли какой-либо команды, в которой я могу ввести 4 координаты (вверху слева, вверху справа, внизу слева, внизу справа) и получить их, а затем изменить размер до 180? - person Styphon; 27.06.2014
comment
Также мне интересно, связано ли это с масштабированием изображения? Jcrop должен справиться с этим, но мне интересно, правильно ли я это делаю? - person Styphon; 27.06.2014
comment
В этом весь смысл Jcrop: у вас есть большое изображение, вы обрезаете его часть, и оно изменяется в размере, и это работает очень хорошо для меня. Может быть, нам нужно увидеть весь код (javascript, html, php)? - person Aurélien Grimpard; 27.06.2014
comment
Я добавил для вас свой JavaScript, который также имеет HTML, так как он генерируется динамически. У вас уже есть все необходимые PHP. - person Styphon; 27.06.2014
comment
Хорошо, это не PHP. PHP работает нормально. Я только что полностью скопировал эту функцию в демонстрационный файл, с которым поставляется JCrop, и он отлично работал. Так что это должен быть javascript, я не могу получить правильную информацию. - person Styphon; 27.06.2014
comment
Почти готово, я добавил еще одно обновление, теперь оно работает для изображений меньшего размера. - person Styphon; 27.06.2014
comment
Сначала отметьте меня одной вещью: в моей инициализации Jcrop я использую параметр trueSize, он сообщает jcrop реальный размер изображения, поскольку изображение, отображаемое в вашем лайтбоксе, может быть уменьшено, чтобы соответствовать области, иначе это испортит все координаты. Вы можете использовать его таким образом trueSize: [/* real width */, /* real height */ ] прямо под опцией minSize. Но я не могу найти ничего об этом на веб-сайте Jcrop, поэтому я не уверен, что он все еще работает для недавнего Jcrop: s - person Aurélien Grimpard; 27.06.2014
comment
Да, это то, чего мне не хватало. Я добавил это сейчас, и он отлично работает для небольших изображений, но не для больших. Большие возвращают черное изображение. Он отлично работает, если я не использую Ajax, но использую демонстрационный скрипт, изображения всех размеров производят хорошие большие пальцы. Как только я отправляю его через Ajax (хотя PHP выполняет обрезку и сохранение точно одинаково), он создает черное изображение. - person Styphon; 27.06.2014
comment
Ах, черное изображение через AJAX, вы пытались echo значение, отправленное POST, это хорошо? У вас нет ошибки, поэтому изображение создается, но оно черное, похоже, что php получает странные координаты и обрезает где-то там, где ничего нет:/ - person Aurélien Grimpard; 27.06.2014
comment
Я вижу, что значения в инструментах разработчика отправляются правильно. Я думаю, что мне нужно повторять значения на разных этапах, чтобы убедиться, что я получаю то, что ожидаю. Странно то, что это нормально для небольших изображений, менее 800 пикселей или около того. - person Styphon; 27.06.2014
comment
Ну, я не чувствую себя глупо. Я фильтровал целые числа, но отправлял поплавки. Я не знаю, почему это работало для небольших изображений, они должны отправлять целые числа, а не числа с плавающей запятой, или фильтр округлял их или что-то в этом роде. Но сейчас разобрались. Это работает. Спасибо за твою помощь. - person Styphon; 27.06.2014

Попробуйте использовать эту функцию во время изменения размера изображения.

Просто вызовите функцию следующим образом:

    resize_image($source_image, $destination_filename, 200, 200);

Эта функция также включает в себя функцию кадрирования. Вы можете включить или отключить параметр урожая, передав ему true или false ($crop = true/false).

function resize_image($source_image, $destination_filename, $width, $height, $quality = 100, $crop = true)
{ 
        if( ! $image_data = getimagesize( $source_image ) )
        {
            return false;
        }

        switch( $image_data['mime'] )
        {
            case 'image/gif':
            $get_func = 'imagecreatefromgif';
            $suffix = ".gif";
            break;
            case 'image/jpeg';
            $get_func = 'imagecreatefromjpeg';
            $suffix = ".jpg";
            break;
            case 'image/png':
            $get_func = 'imagecreatefrompng';
            $suffix = ".png";
            break;
        }

        $img_original = call_user_func( $get_func, $source_image );
        $old_width = $image_data[0];
        $old_height = $image_data[1];
        $new_width = $width;
        $new_height = $height;
        $src_x = 0;
        $src_y = 0;
        $current_ratio = round( $old_width / $old_height, 2 );
        $desired_ratio_after = round( $width / $height, 2 );
        $desired_ratio_before = round( $height / $width, 2 );

        if( $old_width < $width || $old_height < $height )
        {
            /**
            * The desired image size is bigger than the original image.
            * Best not to do anything at all really.
            */
            return false;
        }


        /**
        * If the crop option is left on, it will take an image and best fit it
        * so it will always come out the exact specified size.
        */
        if( $crop )
        {
            /**
            * create empty image of the specified size
            */
            $new_image = imagecreatetruecolor( $width, $height );

            /**
            * Landscape Image
            */
            if( $current_ratio > $desired_ratio_after )
            {
                $new_width = $old_width * $height / $old_height;
            }

            /**
            * Nearly square ratio image.
            */
            if( $current_ratio > $desired_ratio_before && $current_ratio < $desired_ratio_after )
            {
                if( $old_width > $old_height )
                {
                    $new_height = ma(angry) $width, $height );
                    $new_width = $old_width * $new_height / $old_height;
                }
                else
                {
                    $new_height = $old_height * $width / $old_width;
                }
            }

            /**
            * Portrait sized image
            */
            if( $current_ratio < $desired_ratio_before )
            {
                $new_height = $old_height * $width / $old_width;
            }

            /**
            * Find out the ratio of the original photo to it's new, thumbnail-based size
            * for both the width and the height. It's used to find out where to crop.
            */
            $width_ratio = $old_width / $new_width;
            $height_ratio = $old_height / $new_height;

            /**
            * Calculate where to crop based on the center of the image
            */
            $src_x = floor( ( ( $new_width - $width ) / 2 ) * $width_ratio );
            $src_y = round( ( ( $new_height - $height ) / 2 ) * $height_ratio );
        }
        /**
        * Don't crop the image, just resize it proportionally
        */
        else
        {
            if( $old_width > $old_height )
            {
                $ratio = ma(angry) $old_width, $old_height ) / ma(angry) $width, $height );
            }else{
                $ratio = ma(angry) $old_width, $old_height ) / min( $width, $height );
            }

            $new_width = $old_width / $ratio;
            $new_height = $old_height / $ratio;

            $new_image = imagecreatetruecolor( $new_width, $new_height );
        }

        /**
        * Where all the real magic happens
        */
        imagecopyresampled( $new_image, $img_original, 0, 0, $src_x, $src_y, $new_width, $new_height, $old_width, $old_height );

        /**
        * Save it as a JPG File with our $destination_filename param.
        */
        imagejpeg( $new_image, $destination_filename, $quality );

        /**
        * Destroy the evidence!
        */
        imagedestroy( $new_image );
        imagedestroy( $img_original );

        /**
        * Return true because it worked and we're happy. Let the dancing commence!
        */
        return true;
}
person TBI    schedule 26.06.2014
comment
Как мне указать, откуда обрезать этот скрипт? Кажется, он не принимает никаких параметров x, y. - person Styphon; 26.06.2014
comment
Для обрезки вы должны установить $destination_filename как абсолютный путь, и он автоматически обрежет изображение из центра основного изображения. - person TBI; 27.06.2014
comment
Я не хочу, чтобы он обрезался из центра основного изображения, я хочу указать координаты, где обрезать. - person Styphon; 27.06.2014