mirror of
https://github.com/esimov/caire.git
synced 2025-11-02 12:44:12 +08:00
Code refactoring
This commit is contained in:
100
carver.go
100
carver.go
@@ -5,14 +5,12 @@ import (
|
||||
_ "image/png"
|
||||
"math"
|
||||
"image/color"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// SeamCarver is an interface that Carver uses to implement the Resize function.
|
||||
// It takes an image and the output as parameters and returns the resized image
|
||||
// and the error, if exists.
|
||||
type SeamCarver interface {
|
||||
Resize(*image.NRGBA) (image.Image, error)
|
||||
type Carver struct {
|
||||
Width int
|
||||
Height int
|
||||
Points []float64
|
||||
}
|
||||
|
||||
// Seam struct containing the pixel coordinate values.
|
||||
@@ -22,15 +20,11 @@ type Seam struct {
|
||||
}
|
||||
|
||||
// NewCarver returns an initialized Carver structure.
|
||||
func NewCarver(width, height, threshold, blur, rw, rh int, perc bool) *Carver {
|
||||
func NewCarver(width, height int) *Carver {
|
||||
return &Carver{
|
||||
width,
|
||||
height,
|
||||
make([]float64, width*height),
|
||||
threshold,
|
||||
blur,
|
||||
rw, rh,
|
||||
perc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,21 +47,22 @@ func (c *Carver) set(x, y int, px float64) {
|
||||
//
|
||||
// - the minimum energy level is calculated by summing up the current pixel value
|
||||
// with the minimum pixel value of the neighboring pixels from the previous row.
|
||||
func (c *Carver) computeSeams(img *image.NRGBA) []float64 {
|
||||
func (c *Carver) ComputeSeams(img *image.NRGBA, p *Processor) []float64 {
|
||||
var src *image.NRGBA
|
||||
bounds := img.Bounds()
|
||||
iw, ih := bounds.Dx(), bounds.Dy()
|
||||
sobel := SobelFilter(Grayscale(img), float64(c.SobelThreshold))
|
||||
sobel := SobelFilter(Grayscale(img), float64(p.SobelThreshold))
|
||||
|
||||
if c.BlurRadius > 0 {
|
||||
src = Stackblur(sobel, uint32(iw), uint32(ih), uint32(c.BlurRadius))
|
||||
if p.BlurRadius > 0 {
|
||||
src = Stackblur(sobel, uint32(iw), uint32(ih), uint32(p.BlurRadius))
|
||||
} else {
|
||||
src = sobel
|
||||
}
|
||||
|
||||
for x := 0; x < c.Width; x++ {
|
||||
for y := 0; y < c.Height; y++ {
|
||||
r, _, _, a := src.At(x, y).RGBA()
|
||||
c.set(x, y, float64(r)/float64(a))
|
||||
r, _, _, _ := src.At(x, y).RGBA()
|
||||
c.set(x, y, float64(r))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +90,7 @@ func (c *Carver) computeSeams(img *image.NRGBA) []float64 {
|
||||
}
|
||||
|
||||
// Find the lowest vertical energy seam.
|
||||
func (c *Carver) findLowestEnergySeams() []Seam {
|
||||
func (c *Carver) FindLowestEnergySeams() []Seam {
|
||||
// Find the lowest cost seam from the energy matrix starting from the last row.
|
||||
var min float64 = math.MaxFloat64
|
||||
var px int
|
||||
@@ -104,7 +99,7 @@ func (c *Carver) findLowestEnergySeams() []Seam {
|
||||
// Find the pixel on the last row with the minimum cumulative energy and use this as the starting pixel
|
||||
for x := 0; x < c.Width; x++ {
|
||||
seam := c.get(x, c.Height-1)
|
||||
if seam < min && seam > 0 {
|
||||
if seam < min {
|
||||
min = seam
|
||||
px = x
|
||||
}
|
||||
@@ -150,7 +145,7 @@ func (c *Carver) findLowestEnergySeams() []Seam {
|
||||
}
|
||||
|
||||
// Remove image pixels based on energy seams level.
|
||||
func (c *Carver) removeSeam(img *image.NRGBA, seams []Seam) *image.NRGBA {
|
||||
func (c *Carver) RemoveSeam(img *image.NRGBA, seams []Seam) *image.NRGBA {
|
||||
bounds := img.Bounds()
|
||||
dst := image.NewNRGBA(image.Rect(0, 0, bounds.Dx()-1, bounds.Dy()))
|
||||
|
||||
@@ -171,7 +166,7 @@ func (c *Carver) removeSeam(img *image.NRGBA, seams []Seam) *image.NRGBA {
|
||||
}
|
||||
|
||||
// Rotate image by 90 degree counter clockwise
|
||||
func (c *Carver) rotateImage90(src *image.NRGBA) *image.NRGBA {
|
||||
func (c *Carver) RotateImage90(src *image.NRGBA) *image.NRGBA {
|
||||
b := src.Bounds()
|
||||
dst := image.NewNRGBA(image.Rect(0, 0, b.Max.Y, b.Max.X))
|
||||
for dstY := 0; dstY < b.Max.X; dstY++ {
|
||||
@@ -188,7 +183,7 @@ func (c *Carver) rotateImage90(src *image.NRGBA) *image.NRGBA {
|
||||
}
|
||||
|
||||
// Rotate image by 270 degree counter clockwise
|
||||
func (c *Carver) rotateImage270(src *image.NRGBA) *image.NRGBA {
|
||||
func (c *Carver) RotateImage270(src *image.NRGBA) *image.NRGBA {
|
||||
b := src.Bounds()
|
||||
dst := image.NewNRGBA(image.Rect(0, 0, b.Max.Y, b.Max.X))
|
||||
for dstY := 0; dstY < b.Max.X; dstY++ {
|
||||
@@ -202,65 +197,4 @@ func (c *Carver) rotateImage270(src *image.NRGBA) *image.NRGBA {
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Resize is the main function taking the source image and encoding the rescaled image into the output file.
|
||||
func (c *Carver) Resize(img *image.NRGBA) (image.Image, error) {
|
||||
width, height := img.Bounds().Max.X, img.Bounds().Max.Y
|
||||
carver := NewCarver(width, height, c.SobelThreshold, c.BlurRadius, c.NewWidth, c.NewHeight, c.Percentage)
|
||||
|
||||
resize := func() {
|
||||
carver.computeSeams(img)
|
||||
seams := carver.findLowestEnergySeams()
|
||||
img = carver.removeSeam(img, seams)
|
||||
}
|
||||
|
||||
if carver.Percentage {
|
||||
// Calculate new sizes based on provided percentage.
|
||||
nw := carver.Width - int(float64(carver.Width) - (float64(carver.NewWidth)/100 * float64(carver.Width)))
|
||||
nh := carver.Height - int(float64(carver.Height) - (float64(carver.NewHeight)/100 * float64(carver.Height)))
|
||||
|
||||
// Resize image horizontally
|
||||
for x := 0; x < nw; x++ {
|
||||
resize()
|
||||
}
|
||||
// Resize image vertically
|
||||
img = c.rotateImage90(img)
|
||||
|
||||
// Needs to update the slice width & height because of image rotation.
|
||||
carver.Width = img.Bounds().Dx()
|
||||
carver.Height = img.Bounds().Dy()
|
||||
for y := 0; y < nh; y++ {
|
||||
resize()
|
||||
}
|
||||
img = c.rotateImage270(img)
|
||||
} else if carver.NewWidth > 0 || carver.NewHeight > 0 {
|
||||
if carver.NewWidth > 0 {
|
||||
if carver.NewWidth > carver.Width {
|
||||
err := errors.New("new width should be less than image width.")
|
||||
return nil, err
|
||||
}
|
||||
for x := 0; x < carver.NewWidth; x++ {
|
||||
resize()
|
||||
}
|
||||
}
|
||||
|
||||
if carver.NewHeight > 0 {
|
||||
if carver.NewHeight > carver.Height {
|
||||
err := errors.New("new height should be less than image height.")
|
||||
return nil, err
|
||||
}
|
||||
img = c.rotateImage90(img)
|
||||
|
||||
// Needs to update the slice width & height because of image rotation
|
||||
// otherwise the new image will be cut off.
|
||||
carver.Width = img.Bounds().Dx()
|
||||
carver.Height = img.Bounds().Dy()
|
||||
for y := 0; y < carver.NewHeight; y++ {
|
||||
resize()
|
||||
}
|
||||
img = c.rotateImage270(img)
|
||||
}
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func main() {
|
||||
|
||||
toProcess := make(map[string]string)
|
||||
|
||||
p := &caire.Carver{
|
||||
p := &caire.Processor{
|
||||
BlurRadius: *blurRadius,
|
||||
SobelThreshold: *sobelThreshold,
|
||||
NewWidth: *newWidth,
|
||||
|
||||
80
process.go
80
process.go
@@ -8,13 +8,17 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"image/jpeg"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// SeamCarver is an interface that Carver uses to implement the Resize function.
|
||||
// It takes an image and the output as parameters and returns the resized image.
|
||||
type SeamCarver interface {
|
||||
Resize(*image.NRGBA) (image.Image, error)
|
||||
}
|
||||
|
||||
// Processor options
|
||||
type Carver struct {
|
||||
Width int
|
||||
Height int
|
||||
Points []float64
|
||||
type Processor struct {
|
||||
SobelThreshold int
|
||||
BlurRadius int
|
||||
NewWidth int
|
||||
@@ -22,20 +26,84 @@ type Carver struct {
|
||||
Percentage bool
|
||||
}
|
||||
|
||||
|
||||
// Implement the Resize method of the Carver interface.
|
||||
func Resize(s SeamCarver, img *image.NRGBA) (image.Image, error) {
|
||||
return s.Resize(img)
|
||||
}
|
||||
|
||||
// This is the main entry point which takes the source image
|
||||
// and encodes the new, rescaled image into the output file.
|
||||
func (p *Processor) Resize(img *image.NRGBA) (image.Image, error) {
|
||||
var c *Carver = NewCarver(img.Bounds().Dx(), img.Bounds().Dy())
|
||||
|
||||
resize := func() {
|
||||
width, height := img.Bounds().Max.X, img.Bounds().Max.Y
|
||||
c = NewCarver(width, height)
|
||||
c.ComputeSeams(img, p)
|
||||
seams := c.FindLowestEnergySeams()
|
||||
img = c.RemoveSeam(img, seams)
|
||||
}
|
||||
|
||||
if p.Percentage {
|
||||
// Calculate new sizes based on provided percentage.
|
||||
nw := c.Width - int(float64(c.Width) - (float64(p.NewWidth)/100 * float64(c.Width)))
|
||||
nh := c.Height - int(float64(c.Height) - (float64(p.NewHeight)/100 * float64(c.Height)))
|
||||
// Resize image horizontally
|
||||
for x := 0; x < nw; x++ {
|
||||
resize()
|
||||
}
|
||||
// Resize image vertically
|
||||
img = c.RotateImage90(img)
|
||||
|
||||
// Needs to update the slice width & height because of image rotation.
|
||||
c.Width = img.Bounds().Dx()
|
||||
c.Height = img.Bounds().Dy()
|
||||
for y := 0; y < nh; y++ {
|
||||
resize()
|
||||
}
|
||||
img = c.RotateImage270(img)
|
||||
} else if p.NewWidth > 0 || p.NewHeight > 0 {
|
||||
if p.NewWidth > 0 {
|
||||
if p.NewWidth > c.Width {
|
||||
err := errors.New("new width should be less than image width.")
|
||||
return nil, err
|
||||
}
|
||||
for x := 0; x < p.NewWidth; x++ {
|
||||
resize()
|
||||
}
|
||||
}
|
||||
|
||||
if p.NewHeight > 0 {
|
||||
if p.NewHeight > c.Height {
|
||||
err := errors.New("new height should be less than image height.")
|
||||
return nil, err
|
||||
}
|
||||
img = c.RotateImage90(img)
|
||||
|
||||
// Needs to update the slice width & height because of image rotation
|
||||
// otherwise the new image will be cut off.
|
||||
c.Width = img.Bounds().Dx()
|
||||
c.Height = img.Bounds().Dy()
|
||||
for y := 0; y < p.NewHeight; y++ {
|
||||
resize()
|
||||
}
|
||||
img = c.RotateImage270(img)
|
||||
}
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
|
||||
// Process is the main entry point for the image resize operation.
|
||||
func (c *Carver) Process(file io.Reader, output string) (*os.File, error) {
|
||||
func (p *Processor) Process(file io.Reader, output string) (*os.File, error) {
|
||||
src, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img := imgToNRGBA(src)
|
||||
res, err := Resize(c, img)
|
||||
//sobel := SobelFilter(Grayscale(img), float64(p.SobelThreshold))
|
||||
res, err := Resize(p, img)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user