删除边框/裁剪图像的最佳方法

I am using a Go package (Go binding to ImageMagick's MagickWand C API) to ImageMagick where I'm removing borders from images (cropping). The way I am using the trim function can be found below.

Now the problem is the fuzzy factor. For example, if I set the value to 2000, the image (here is the source) still has some white images like these:

  • fuzz factor value 2000 --> result
  • fuzz factor value 10000 --> result

I have created a small html which illustrates the problem best. It contains both images: https://dl.dropboxusercontent.com/u/15684927/image-trim-problem.html

As you can see the source has some pixels on the bottom right corner which are causing the trouble. If I set the factor to 10000, I'm afraid that I will loose pixels on other pictures. If I set it on 2000, the trimming isn't done right in pictures like these.

So my actual question is: what is the best way to "crop" / "trim" images?

package main

import "gopkg.in/gographics/imagick.v1/imagick"

func main() {
    imagick.Initialize()
    defer imagick.Terminate()

    inputFile := "tshirt-original.jpg"
    outputFile := "trimmed.jpg"
    mw := imagick.NewMagickWand()
    // Schedule cleanup
    defer mw.Destroy()

    // read image
    err := mw.ReadImage(inputFile)
    if err != nil {
        panic(err)
    }

    // first trim original image
    // fuzz: by default target must match a particular pixel color exactly.
    // However, in many cases two colors may differ by a small amount. The fuzz
    // member of image defines how much tolerance is acceptable to consider two
    // colors as the same. For example, set fuzz to 10 and the color red at
    // intensities of 100 and 102 respectively are now interpreted as the same
    // color for the purposes of the floodfill.
    mw.TrimImage(10000)

    // Set the compression quality to 95 (high quality = low compression)
    err = mw.SetImageCompressionQuality(95)
    if err != nil {
        panic(err)
    }

    // save
    err = mw.WriteImage(outputFile)
    if err != nil {
        panic(err)
    }
}

Basically, your problem is that you have a high-frequency, high-amplitude artifact at the edge of your image. Or, put differently, a sharp, high peak at the edge, which, if you want to use trim, forces you to use such a high a fuzz-value to overcome this, that the algorithm also considers the 'actual content' as equal to the 'background' (border).

One solution here is to use a multi-step approach, whereby you first smooth out the edge artifacts and then apply trim to the resulting image. By smoothing it out, you get rid of the high peak and smear it out into a nice rolling hill. Rolling hills, in turn, can be easily trimmed with low fuzz values. This then provides you with the desired geometry which you can use to crop the original.

Specifically, let's take the original image: Original Image

Now, let's smooth out that ridge on the edge using a blur with a radius of 10 and a sigma of 10 through convert original.jpg -blur 10x10 10x10.jpg, which yields: 10x10 blurred image

Now, you might notice that the artifacts on the edge have now pretty much disappeared.

We can now do a 'virtual' trim and ask ImageMagick what the result of the trim would be through convert 10x10.jpg -fuzz 2000 -format %@ info:, which, according to the documentation gives you the "trim bounding box (without actually trimming)": 1326x1560+357+578%

Taking these values (except for the percentage sign) and using them for crop geometry, gives you the convert with crop command convert original.jpg -crop 1326x1560+357+578 cropped.jpg, which gives you:

cropped image without noise

Edit:

Now, since you want this as code, using imagick, here's the solution in code. It assumes you have the file stored as './data/original.jpg' and will store it as './data/trimmed.jpg'

package main

import (
  "fmt"

  "gopkg.in/gographics/imagick.v2/imagick"
)

func init() {
  imagick.Initialize()
}

const originalImageFilename = "data/original.jpg"

func main() {

  mw := imagick.NewMagickWand()
  err := mw.ReadImage(originalImageFilename)
  if err != nil {
    fmt.Sprint(err.Error())
    return
  }

  // Use a clone to determine what will happen
  mw2 := mw.Clone()

  mw2.BlurImage(10, 10)
  mw2.TrimImage(2000)

  _, _, xOffset, yOffset, err := mw2.GetImagePage()
  if err != nil {
    fmt.Sprint(err.Error())
    return
  }

  trimmedWidth := mw2.GetImageWidth()
  trimmedHeight := mw2.GetImageHeight()

  mw2.Destroy()

  mw.CropImage(trimmedWidth, trimmedHeight, xOffset, yOffset)

  mw.WriteImage("data/trimmed.jpg")
  mw.Destroy()
}