Как я могу быстро масштабировать и резкость изображения в Go? - PullRequest
4 голосов
/ 05 сентября 2011

В настоящее время я портирую довольно простое приложение галереи с PHP на Go. В этом приложении реализована автоматическая генерация миниатюр и средней версии каждого изображения.

В PHP я использовал GD, потому что он поставляется с ним и работал довольно хорошо. (Код в конце вопроса). Я думал, что смогу просто повторить это в Go и нашел go-gd из https://github.com/bolknote/go-gd (опять же, код в конце). Это работает, но примерно в 10 раз медленнее (измеряется с помощью time wget $URL). Реализация PHP занимает около 1 секунды для генерации версии 1024x768 из 10 MP-изображения, в то время как Go-код занимает почти 10 секунд.

Есть ли какой-нибудь способ ускорить это или любую другую библиотеку обработки изображений для Go, которая реализует масштабирование и свертку, будучи достаточно быстрой?

PHP-код

public function saveThumb($outName, $options) {
    $this->img = imagecreatefromjpeg($filename);
    if (!is_dir(dirname($outName))) {
        mkdir(dirname($outName), 0777, true);
    }

    $width = imagesx($this->img);
    $height = imagesy($this->img);

    if ($options["keep_aspect"]) {
        $factor = min($options["size_x"]/$width, $options["size_y"]/$height);
        $new_width = round($factor*$width);
        $new_height = round($factor*$height);
    } else {
        $new_width  = $options["size_x"];
        $new_height = $options["size_y"];
    }

    // create a new temporary image
    $tmp_img = imagecreatetruecolor($new_width, $new_height);

    // copy and resize old image into new image
    imagecopyresampled($tmp_img, $this->img, 0, 0, 0, 0, $new_width, $new_height, $width, $height);

    if ($options["sharpen"]) {
        // define the sharpen matrix
        $sharpen = array(
            array(-1, -1.7, -1),
            array(-1.7, 20, -1.7),
            array(-1, -1.7, -1) 
        );

        // calculate the sharpen divisor
        $divisor = array_sum(array_map('array_sum', $sharpen));

        // apply the matrix
        imageconvolution($tmp_img, $sharpen, $divisor, 0);
    }

    // save thumbnail into a file
    imagejpeg($tmp_img, $outName);     
}

Go-код

func (entry *entry) GenerateThumb(options ImageType, overwrite bool) os.Error {
    targetFilename := entry.Filename(imageType)
    sourceFilename := entry.Filename(IMAGE_TYPE_FULL)
    targetDirname, _ := filepath.Split(targetFilename)
    os.MkdirAll(targetDirname, 0777)

    targetFi, errT := os.Stat(targetFilename)
    sourceFi, errS := os.Stat(sourceFilename)

    image := gd.CreateFromJpeg(sourceFilename)
    if image == nil {
        return os.NewError("Image could not be loaded")
    }

    var targetX, targetY int = 0, 0

    if options.KeepAspect {
        factor := math.Fmin(float64(options.SizeX)/float64(image.Sx()), float64(options.SizeY)/float64(image.Sy()))
        targetX = int(factor*float64(image.Sx()))
        targetY = int(factor*float64(image.Sy()))
    } else {
        targetX = options.SizeX
        targetY = options.SizeY
    }
    tmpImage := gd.CreateTrueColor(targetX, targetY)
    image.CopyResampled(tmpImage, 0, 0, 0, 0, tmpImage.Sx(), tmpImage.Sy(), image.Sx(), image.Sy())

    if options.Sharpen {
        sharpenMatrix := [3][3]float32{
        {-1, -1.7, -1},
        {-1.7, 20, -1.7},
        {-1, -1.7, -1} }
        tmpImage.Convolution(sharpenMatrix, 9.2, 0)
    }
    tmpImage.Jpeg(targetFilename, 90)

    return nil
}

РЕДАКТИРОВАТЬ: Go-код с использованием resize.go (см. Ответ)

func (entry *entry) GenerateThumb(options ImageType, overwrite bool) os.Error {
    targetFilename := entry.Filename(imageType)
    sourceFilename := entry.Filename(IMAGE_TYPE_FULL)
    targetDirname, _ := filepath.Split(targetFilename)
    os.MkdirAll(targetDirname, 0777)

    targetFi, errT := os.Stat(targetFilename)
    sourceFi, errS := os.Stat(sourceFilename)

    if errT == nil && errS == nil {
        if targetFi.Mtime_ns > sourceFi.Mtime_ns && !overwrite {
            // already up-to-date, nothing to do
            return nil
        }
    }

    log.Printf("Generate(\"%v\", %v)\n", imageType, overwrite)

    inFile, fErr := os.Open(sourceFilename)
    if fErr != nil {
        log.Fatal(fErr)
    }
    defer inFile.Close()

    img, _, err := image.Decode(inFile)
    if err != nil {
        log.Fatal(err)
    }

    var targetX, targetY int
    if options.KeepAspect {
        factor := math.Fmin(float64(options.SizeX)/float64(img.Bounds().Max.X), float64(options.SizeY)/float64(img.Bounds().Max.Y))
        targetX = int(factor*float64(img.Bounds().Max.X))
        targetY = int(factor*float64(img.Bounds().Max.Y))
    } else {
        targetX = curType.SizeX
        targetY = curType.SizeY
    }
    newImg := resize.Resample(img, image.Rect(0, 0, img.Bounds().Max.X, img.Bounds().Max.Y), targetX, targetY)

    var outFile *os.File
    outFile, fErr = os.Create(targetFilename)
    if fErr != nil {
        log.Fatal(fErr)
    }
    defer outFile.Close()

    err = jpeg.Encode(outFile, newImg, &jpeg.Options{90})
    if err != nil {
        log.Fatal(err)
    }
    return nil
}

Ответы [ 3 ]

6 голосов
/ 26 июня 2013

Вы должны проверить эту библиотеку изменения размера: github.com / nfnt / resize .Он имеет 6 хороших функций интерполяции на выбор.

3 голосов
/ 06 сентября 2011

Пример приложения Moustachio для GAE Эндрю Герранда содержит файл resize.go с собственной реализацией Go. Несколько дней назад в списке рассылки офигевших был похожий вопрос , и Найджел опубликовал там обновленную версию этого файла. Вы можете попробовать это:)

0 голосов
/ 05 сентября 2011

Кажется, самое простое решение - сохранить образ на диск и выполнить convert из Image Magic, чтобы преобразовать его. Если вам нужна дополнительная производительность, вы можете использовать оперативный диск.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...