2016-08-21 7 views
1

저는 Golang에서 이미지의 크기를 조정하는 몇 가지 기본 방법을 쓰고 있습니다. 이미지 크기 조정에 대한 여러 게시물을 보았습니다. 그러나 내 인생에서 내가 무엇을 놓치고 있는지 알 수는 없습니다 ...Golang에서 Lanczos Resampling을 사용하여 거친 가장자리를 만들었습니다.

기본적으로 내 문제는 Golang에서 이미지 크기를 조정할 때 내 결과가 나타나는 것입니다. 앨리어싱이 많이 필요합니다.

이미지를 반복적으로 다운 샘플링하려고 시도했지만 개선되지 않았습니다.

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)) 
} 

내가 최적화 살펴보기 전에 좋은 품질의 결과를 얻을 원하기 때문에 그것은, 특히 확대됨되지 않습니다 :

여기 내 코드입니다.

이미지 리샘플링 경험이있는 사람이라면 문제가 될 수있는 것이 있습니까? 다음 enter image description here

가 있습니다 : 여기 enter image description here

내가 재귀 호출을 제거하면 내 결과입니다 : 여기 Source Image

내 결과입니다 : 참고로

, 여기에 내 소스 이미지 Ruby를 통해 RMagick/ImageMagick을 사용하여 얻은 결과 (내가 뭘 촬영했는지) : enter image description here

아무도 원활하게 다운 스케일 결과를 얻을 수있는 방법에 대한 조언이 있습니까? 이 특별한 예는 매우 격렬한 다운 스케일이지만, Rmagick은 훌륭한 품질로 매우 빠르게 축소 할 수 있었으므로 가능해야합니다.

나는 Lanczos3 Resampling이 좋은 결과를 산출한다고 말했고, 그게 내가 여기서 사용하려고하는 것입니다. 제 구현이 맞는지 확실하지 않습니다.

또한 참고로, golang의 "At"함수는 [0, 0xFFFF] ([0, 65535]) 범위의 rgba 값을 반환하기 때문에 0xFF/0xFFFF 변환이 사용되지만 "Set"은 [0, 0xFF] ([0, 255]) 범위로 초기화 됨

지금은 성능보다 품질에 더 관심이 있습니다.

답변

0

좋아, 앨리어싱 문제를 해결할 방법을 찾은 것 같습니다. lanczos3를 사용하는 대신에 필자는 쌍 선형 보간법을 사용하여 소스 이미지를 크기 조정 (edgeSize = 1080), 가우시안 이미지 흐리게하기, 이미지를 대상 크기 (edgeSize = 600)로 약간 축소하여 재 샘플링했습니다. , 이번에는 바이 큐빅 보간법을 사용합니다. 이것은 RMagick이 나에게주는 것과 거의 같은 결과를주었습니다.