diff --git a/carver_test.go b/carver_test.go new file mode 100644 index 0000000..f73d270 --- /dev/null +++ b/carver_test.go @@ -0,0 +1,204 @@ +package caire + +import ( + "image" + "image/color" + "image/draw" + "testing" +) + +var p *Processor + +func init() { + p = &Processor{ + NewWidth: ImgWidth, + NewHeight: ImgHeight, + BlurRadius: 1, + SobelThreshold: 4, + Percentage: false, + Square: false, + Debug: false, + } +} + +func TestCarver_EnergySeamShouldNotBeDetected(t *testing.T) { + var seams [][]Seam + var totalEnergySeams int + + img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) + dx, dy := img.Bounds().Dx(), img.Bounds().Dy() + + var c = NewCarver(dx, dy) + for x := 0; x < ImgWidth; x++ { + width, height := img.Bounds().Max.X, img.Bounds().Max.Y + c = NewCarver(width, height) + c.ComputeSeams(img, p) + les := c.FindLowestEnergySeams() + seams = append(seams, les) + } + + for i := 0; i < len(seams); i++ { + for s := 0; s < len(seams[i]); s++ { + totalEnergySeams += seams[i][s].X + } + } + if totalEnergySeams != 0 { + t.Errorf("Energy seam shouldn't been detected") + } +} + +func TestCarver_DetectHorizontalEnergySeam(t *testing.T) { + var seams [][]Seam + var totalEnergySeams int + + img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) + draw.Draw(img, img.Bounds(), &image.Uniform{image.White}, image.ZP, draw.Src) + + // Replace the pixel colors in a single row from 0xff to 0xdd. 5 is an arbitrary value. + // The seam detector should recognize that line as being of low energy density + // and should perform the seam computation process. + // This way we'll make sure, that the seam detector correctly detects one and only one line. + dx, dy := img.Bounds().Dx(), img.Bounds().Dy() + for x := 0; x < dx; x++ { + img.Pix[(5*dx+x)*4+0] = 0xdd + img.Pix[(5*dx+x)*4+1] = 0xdd + img.Pix[(5*dx+x)*4+2] = 0xdd + img.Pix[(5*dx+x)*4+3] = 0xdd + } + + var c = NewCarver(dx, dy) + for x := 0; x < ImgWidth; x++ { + width, height := img.Bounds().Max.X, img.Bounds().Max.Y + c = NewCarver(width, height) + c.ComputeSeams(img, p) + les := c.FindLowestEnergySeams() + seams = append(seams, les) + } + + for i := 0; i < len(seams); i++ { + for s := 0; s < len(seams[i]); s++ { + totalEnergySeams += seams[i][s].X + } + } + if totalEnergySeams == 0 { + t.Errorf("The seam detector should have detected a horizontal energy seam") + } +} + +func TestCarver_DetectVerticalEnergySeam(t *testing.T) { + var seams [][]Seam + var totalEnergySeams int + + img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) + draw.Draw(img, img.Bounds(), &image.Uniform{image.White}, image.ZP, draw.Src) + + // Replace the pixel colors in a single column from 0xff to 0xdd. 5 is an arbitrary value. + // The seam detector should recognize that line as being of low energy density + // and should perform the seam computation process. + // This way we'll make sure, that the seam detector correctly detects one and only one line. + dx, dy := img.Bounds().Dx(), img.Bounds().Dy() + for y := 0; y < dy; y++ { + img.Pix[5*4+(dx*y)*4+0] = 0xdd + img.Pix[5*4+(dx*y)*4+1] = 0xdd + img.Pix[5*4+(dx*y)*4+2] = 0xdd + img.Pix[5*4+(dx*y)*4+3] = 0xff + } + + var c = NewCarver(dx, dy) + img = c.RotateImage90(img) + for x := 0; x < ImgHeight; x++ { + width, height := img.Bounds().Max.X, img.Bounds().Max.Y + c = NewCarver(width, height) + c.ComputeSeams(img, p) + les := c.FindLowestEnergySeams() + seams = append(seams, les) + } + img = c.RotateImage270(img) + + for i := 0; i < len(seams); i++ { + for s := 0; s < len(seams[i]); s++ { + totalEnergySeams += seams[i][s].X + } + } + if totalEnergySeams == 0 { + t.Errorf("The seam detector should have detected a vertical energy seam") + } +} + +func TestCarver_RemoveSeam(t *testing.T) { + img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) + bounds := img.Bounds() + + // We choose to fill up the background with an uniform white color + // and afterwards we replace the colors in a single row with lower intensity ones. + draw.Draw(img, bounds, &image.Uniform{image.White}, image.ZP, draw.Src) + origImg := img + + dx, dy := img.Bounds().Dx(), img.Bounds().Dy() + // Replace the pixels in row 5 with lower intensity colors. + for x := 0; x < dx; x++ { + img.Set(x, 5, color.RGBA{R: 0xdd, G: 0xdd, B: 0xdd, A: 0xff}) + } + + c := NewCarver(dx, dy) + c.ComputeSeams(img, p) + seams := c.FindLowestEnergySeams() + img = c.RemoveSeam(img, seams, false) + + isEq := true + // The test should pass if the detector correctly finds the row wich pixel values are of lower intensity. + for x := 0; x < dx; x++ { + for y := 0; y < dy; y++ { + // In case the seam detector correctly recognize the modified line as of low importance + // it should remove it, which means the new image width should be 1px less then the original image. + r0, g0, b0, _ := origImg.At(x, y).RGBA() + r1, g1, b1, _ := img.At(x, y).RGBA() + + if r0>>8 != r1>>8 && g0>>8 != g1>>8 && b0>>8 != b1>>8 { + isEq = false + } + } + } + if isEq { + t.Errorf("Seam should have been removed") + } +} + +func TestCarver_AddSeam(t *testing.T) { + img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) + bounds := img.Bounds() + + // We choose to fill up the background with an uniform white color + // Afterwards we'll replace the colors in a single row with lower intensity ones. + draw.Draw(img, bounds, &image.Uniform{image.White}, image.ZP, draw.Src) + origImg := img + + dx, dy := img.Bounds().Dx(), img.Bounds().Dy() + // Replace the pixels in row 5 with lower intensity colors. + for x := 0; x < dx; x++ { + img.Set(x, 5, color.RGBA{R: 0xdd, G: 0xdd, B: 0xdd, A: 0xff}) + } + + c := NewCarver(dx, dy) + c.ComputeSeams(img, p) + seams := c.FindLowestEnergySeams() + img = c.AddSeam(img, seams, false) + + dx, dy = img.Bounds().Dx(), img.Bounds().Dy() + + isEq := true + // The test should pass if the detector correctly finds the row wich has lower intensity colors. + for x := 0; x < dx; x++ { + for y := 0; y < dy; y++ { + r0, g0, b0, _ := origImg.At(x, y).RGBA() + r1, g1, b1, _ := img.At(x, y).RGBA() + + if r0>>8 != r1>>8 && g0>>8 != g1>>8 && b0>>8 != b1>>8 { + isEq = false + } + } + } + if isEq { + t.Errorf("Seam should have been added") + } +} diff --git a/grayscale_test.go b/grayscale_test.go index 2541519..a524248 100644 --- a/grayscale_test.go +++ b/grayscale_test.go @@ -6,10 +6,12 @@ import ( "testing" ) -const ImgWidth = 10 -const ImgHeight = 10 +const ( + ImgWidth = 10 + ImgHeight = 10 +) -func TestGrayscale(t *testing.T) { +func TestImage_GrayscaleMode(t *testing.T) { img := image.NewRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) for i := 0; i < img.Bounds().Dx(); i++ { for j := 0; j < img.Bounds().Dy(); j++ { diff --git a/process.go b/process.go index b392ae7..d9f385b 100644 --- a/process.go +++ b/process.go @@ -417,7 +417,7 @@ func (p *Processor) Process(r io.Reader, w io.Writer) error { if err != nil { return err } - img := imgToNRGBA(src) + img := p.imgToNRGBA(src) switch w.(type) { case *os.File: @@ -491,7 +491,7 @@ func (p *Processor) enlarge(c *Carver, img *image.NRGBA) (*image.NRGBA, error) { } // imgToNRGBA converts any image type to *image.NRGBA with min-point at (0, 0). -func imgToNRGBA(img image.Image) *image.NRGBA { +func (p *Processor) imgToNRGBA(img image.Image) *image.NRGBA { srcBounds := img.Bounds() if srcBounds.Min.X == 0 && srcBounds.Min.Y == 0 { if src0, ok := img.(*image.NRGBA); ok { diff --git a/process_test.go b/process_test.go index 0235c2d..1136e61 100644 --- a/process_test.go +++ b/process_test.go @@ -5,25 +5,14 @@ import ( "testing" ) -func TestProcessor_Resize(t *testing.T) { - reduceImageH(t) - reduceImageV(t) -} - -func reduceImageH(t *testing.T) { +func TestResize_ReduceImageWidth(t *testing.T) { img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) var c = NewCarver(img.Bounds().Dx(), img.Bounds().Dy()) newWidth := ImgWidth / 2 - p := &Processor{ - BlurRadius: 2, - SobelThreshold: 10, - NewWidth: newWidth, - NewHeight: ImgHeight, - Percentage: false, - Square: false, - Debug: false, - } - // Reduce image size horizontally + + p.NewWidth = newWidth + p.NewHeight = ImgHeight + for x := 0; x < newWidth; x++ { width, height := img.Bounds().Max.X, img.Bounds().Max.Y c = NewCarver(width, height) @@ -38,20 +27,14 @@ func reduceImageH(t *testing.T) { } } -func reduceImageV(t *testing.T) { +func TestResize_ReduceImageHeight(t *testing.T) { img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) var c = NewCarver(img.Bounds().Dx(), img.Bounds().Dy()) newHeight := ImgHeight / 2 - p := &Processor{ - BlurRadius: 2, - SobelThreshold: 10, - NewWidth: ImgWidth, - NewHeight: newHeight, - Percentage: false, - Square: false, - Debug: false, - } - // Reduce image size horizontally + + p.NewWidth = ImgWidth + p.NewHeight = newHeight + img = c.RotateImage90(img) for x := 0; x < newHeight; x++ { width, height := img.Bounds().Max.X, img.Bounds().Max.Y @@ -67,3 +50,51 @@ func reduceImageV(t *testing.T) { t.Errorf("Resulted image height expected to be %v. Got %v", newHeight, imgHeight) } } + +func TestResize_IncreaseImageWidth(t *testing.T) { + img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) + origImgWidth := img.Bounds().Dx() + var c = NewCarver(img.Bounds().Dx(), img.Bounds().Dy()) + newWidth := ImgWidth * 2 + + p.NewWidth = newWidth + p.NewHeight = ImgHeight + + for x := 0; x < newWidth; x++ { + width, height := img.Bounds().Max.X, img.Bounds().Max.Y + c = NewCarver(width, height) + c.ComputeSeams(img, p) + seams := c.FindLowestEnergySeams() + img = c.AddSeam(img, seams, p.Debug) + } + imgWidth := img.Bounds().Max.X - origImgWidth + + if imgWidth != newWidth { + t.Errorf("Resulted image width expected to be %v. Got %v", newWidth, imgWidth) + } +} + +func TestResize_IncreaseImageHeight(t *testing.T) { + img := image.NewNRGBA(image.Rect(0, 0, ImgWidth, ImgHeight)) + origImgHeigth := img.Bounds().Dy() + var c = NewCarver(img.Bounds().Dx(), img.Bounds().Dy()) + newHeight := ImgHeight * 2 + + p.NewWidth = ImgWidth + p.NewHeight = newHeight + + img = c.RotateImage90(img) + for x := 0; x < newHeight; x++ { + width, height := img.Bounds().Max.X, img.Bounds().Max.Y + c = NewCarver(width, height) + c.ComputeSeams(img, p) + seams := c.FindLowestEnergySeams() + img = c.AddSeam(img, seams, p.Debug) + } + img = c.RotateImage270(img) + imgHeight := img.Bounds().Max.Y - origImgHeigth + + if imgHeight != newHeight { + t.Errorf("Resulted image height expected to be %v. Got %v", newHeight, imgHeight) + } +}