2016-08-21 11 views
1

Я писал некоторые основные методы для изменения размеров изображений в Голанге. Я видел несколько сообщений об изменении размеров изображений, но для жизни я не могу понять, чего мне не хватает ...Rough Edges With Lanczos Resampling в Голанге

По сути, моя проблема в том, что при изменении размера изображения в Голанге мои результаты кажутся иметь много псевдонимов.

Я пробовал итеративно сбрасывать изображение, но это не дало значительных улучшений.

Вот мой код:

func resize(original image.Image, 
    edgeSize int, filterSize int) image.Image { 

    oldBounds := original.Bounds() 
    if oldBounds.Dx() < edgeSize && oldBounds.Dy() < edgeSize { 
     // No resize necessary 
     return original 
    } 

    threshold := edgeSize * 4/3 
    if oldBounds.Dx() > threshold || oldBounds.Dy() > threshold { 
     fmt.Println("Upstream") 
     original = customResizeImageToFitBounds(original, threshold, filterSize) 
     oldBounds = original.Bounds() 
    } 

    newBounds := getNewBounds(oldBounds, edgeSize) 

    resized := image.NewRGBA(newBounds) 

    var ratioX = float64(oldBounds.Dx())/float64(newBounds.Dx()) 
    var ratioY = float64(oldBounds.Dy())/float64(newBounds.Dy()) 

    for x := 0; x < newBounds.Dx(); x++ { 
     for y := 0; y < newBounds.Dy(); y++ { 
      sourceX := ratioX * float64(x) 
      minX := int(math.Floor(sourceX)) 

      sourceY := ratioY * float64(y) 
      minY := int(math.Floor(sourceY)) 

      sampleSize := filterSize<<1 + 1 
      var xCoeffs = make([]float64, sampleSize) 
      var yCoeffs = make([]float64, sampleSize) 

      var sumX = 0.0 
      var sumY = 0.0 
      for i := 0; i < sampleSize; i++ { 
       xCoeffs[i] = lanczos(filterSize, sourceX-float64(minX+i-filterSize)) 
       yCoeffs[i] = lanczos(filterSize, sourceY-float64(minY+i-filterSize)) 

       sumX += xCoeffs[i] 
       sumY += yCoeffs[i] 
      } 

      for i := 0; i < sampleSize; i++ { 
       xCoeffs[i] /= sumX 
       yCoeffs[i] /= sumY 
      } 

      rgba := make([]float64, 4) 

      for i := 0; i < sampleSize; i++ { 
       if yCoeffs[i] == 0.0 { 
        continue 
       } 
       currY := minY + i - filterSize 

       rgbaRow := make([]float64, 4) 
       for j := 0; j < sampleSize; j++ { 
        if xCoeffs[j] == 0.0 { 
         continue 
        } 
        currX := minX + i - filterSize 

        rij, gij, bij, aij := original.At(
         clamp(currX, currY, oldBounds)).RGBA() 

        rgbaRow[0] += float64(rij) * xCoeffs[j] 
        rgbaRow[1] += float64(gij) * xCoeffs[j] 
        rgbaRow[2] += float64(bij) * xCoeffs[j] 
        rgbaRow[3] += float64(aij) * xCoeffs[j] 
       } 
       rgba[0] += float64(rgbaRow[0]) * yCoeffs[i] 
       rgba[1] += float64(rgbaRow[1]) * yCoeffs[i] 
       rgba[2] += float64(rgbaRow[2]) * yCoeffs[i] 
       rgba[3] += float64(rgbaRow[3]) * yCoeffs[i] 
      } 

      rgba[0] = clampRangeFloat(0, rgba[0], 0xFFFF) 
      rgba[1] = clampRangeFloat(0, rgba[1], 0xFFFF) 
      rgba[2] = clampRangeFloat(0, rgba[2], 0xFFFF) 
      rgba[3] = clampRangeFloat(0, rgba[3], 0xFFFF) 

      var rgbaF [4]uint64 
      rgbaF[0] = (uint64(math.Floor(rgba[0]+0.5)) * 0xFF)/0xFFFF 
      rgbaF[1] = (uint64(math.Floor(rgba[1]+0.5)) * 0xFF)/0xFFFF 
      rgbaF[2] = (uint64(math.Floor(rgba[2]+0.5)) * 0xFF)/0xFFFF 
      rgbaF[3] = (uint64(math.Floor(rgba[3]+0.5)) * 0xFF)/0xFFFF 

      rf := uint8(clampRangeUint(0, uint32(rgbaF[0]), 255)) 
      gf := uint8(clampRangeUint(0, uint32(rgbaF[1]), 255)) 
      bf := uint8(clampRangeUint(0, uint32(rgbaF[2]), 255)) 
      af := uint8(clampRangeUint(0, uint32(rgbaF[3]), 255)) 

      resized.Set(x, y, color.RGBA{R: rf, G: gf, B: bf, A: af}) 
     } 
    } 
    return resized 
} 


// Machine epsilon 
var epsilon = math.Nextafter(1.0, 2.0) - 1 

func lanczos(filterSize int, x float64) float64 { 
    x = math.Abs(x) 
    fs := float64(filterSize) 
    if x < epsilon { 
     return 1.0 
    } 

    if x > fs { 
     return 0 
    } 

    piX := math.Pi * x 
    piXOverFS := piX/fs 
    return (math.Sin(piX)/piX) * (math.Sin(piXOverFS)/(piXOverFS)) 
} 

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

Кто-нибудь, у кого есть опыт с повторной выборкой изображения, видит что-нибудь потенциально проблематичное?

Для справки, вот мой источник изображения: Source Image

Вот мой результат: enter image description here

Вот мой результат, если я удалю рекурсивный вызов: enter image description here

Вот является результат использования RMagick/ImageMagick через Ruby (что я снимаю для): enter image description here

У кого-нибудь есть совет, как я могу получить более гладкий результат? Этот конкретный пример - довольно резкое сокращение, но Rmagick смог быстро его масштабировать с большим качеством, поэтому это должно быть возможно.

Мне сказали, что Lanczos3 Resampling дает хорошие результаты, и вот что я пытаюсь использовать здесь - я не уверен, что моя реализация правильная.

Кроме того, в качестве примечания стороны: преобразование 0xFF/0xFFFF связано с тем, что функция «At» golang возвращает значения rgba в диапазоне [0, 0xFFFF] ([0, 65535]), но «Set» принимает цвет, который является инициализируется диапазоном [0, 0xFF] ([0, 255])

В настоящее время я больше забочусь о качестве, чем о производительности.

ответ

0

Хорошо, я думаю, что нашел один из способов решения проблемы сглаживания. Вместо использования lanczos3 я использовал билинейную интерполяцию для повторной выборки исходного изображения с размером, немного превышающим то, что я собирался (edgeSize = 1080), гауссовым размытым изображением, затем масштабировал изображение до целевого размера (edgeSize = 600) , на этот раз с бикубической интерполяцией. Это дало мне результаты примерно такие же, как и те, которые мне давал Ремаджик.