diff --git a/vips/conversion.c b/vips/conversion.c index 1862620d..49f82be8 100644 --- a/vips/conversion.c +++ b/vips/conversion.c @@ -126,6 +126,10 @@ int flip_image(VipsImage *in, VipsImage **out, int direction) { return vips_flip(in, out, direction, NULL); } +int recomb_image(VipsImage *in, VipsImage **out, VipsImage *m) { + return vips_recomb(in, out, m, NULL); +} + int extract_image_area(VipsImage *in, VipsImage **out, int left, int top, int width, int height) { return vips_extract_area(in, out, left, top, width, height, NULL); diff --git a/vips/conversion.go b/vips/conversion.go index 67e11925..5e7c09e9 100644 --- a/vips/conversion.go +++ b/vips/conversion.go @@ -212,6 +212,18 @@ func vipsFlip(in *C.VipsImage, direction Direction) (*C.VipsImage, error) { return out, nil } +// https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-recomb +func vipsRecomb(in *C.VipsImage, m *C.VipsImage) (*C.VipsImage, error) { + incOpCounter("recomb") + var out *C.VipsImage + + if err := C.recomb_image(in, &out, m); err != 0 { + return nil, handleImageError(out) + } + + return out, nil +} + // https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-extract-area func vipsExtractArea(in *C.VipsImage, left, top, width, height int) (*C.VipsImage, error) { incOpCounter("extractArea") diff --git a/vips/conversion.h b/vips/conversion.h index bf978a93..5cb2d361 100644 --- a/vips/conversion.h +++ b/vips/conversion.h @@ -20,6 +20,8 @@ int embed_multi_page_image_background(VipsImage *in, VipsImage **out, int left, int flip_image(VipsImage *in, VipsImage **out, int direction); +int recomb_image(VipsImage *in, VipsImage **out, VipsImage *m); + int extract_image_area(VipsImage *in, VipsImage **out, int left, int top, int width, int height); int extract_area_multi_page(VipsImage *in, VipsImage **out, int left, int top, diff --git a/vips/image.go b/vips/image.go index 774f7e88..7d4339f5 100644 --- a/vips/image.go +++ b/vips/image.go @@ -1774,6 +1774,55 @@ func (r *ImageRef) Flip(direction Direction) error { return nil } +// Recomb recombines the image bands using the matrix provided +func (r *ImageRef) Recomb(matrix [][]float64) error { + numBands := r.Bands() + // Ensure the provided matrix is 3x3 + if len(matrix) != 3 || len(matrix[0]) != 3 || len(matrix[1]) != 3 || len(matrix[2]) != 3 { + return errors.New("Invalid recombination matrix") + } + // If the image is RGBA, expand the matrix to 4x4 + if numBands == 4 { + matrix = append(matrix, []float64{0, 0, 0, 1}) + for i := 0; i < 3; i++ { + matrix[i] = append(matrix[i], 0) + } + } else if numBands != 3 { + return errors.New("Unsupported number of bands") + } + + // Flatten the matrix + var matrixValues []float64 + for _, row := range matrix { + for _, value := range row { + matrixValues = append(matrixValues, value) + } + } + + // Convert the Go slice to a C array and get its size + matrixPtr := unsafe.Pointer(&matrixValues[0]) + matrixSize := C.size_t(len(matrixValues) * 8) // 8 bytes for each float64 + + // Create a VipsImage from the matrix in memory + matrixImage := C.vips_image_new_from_memory(matrixPtr, matrixSize, C.int(numBands), C.int(numBands), 1, C.VIPS_FORMAT_DOUBLE) + + // Check for any Vips errors + errMsg := C.GoString(C.vips_error_buffer()) + if errMsg != "" { + C.vips_error_clear() + return errors.New("Vips error: " + errMsg) + } + + // Recombine the image using the matrix + out, err := vipsRecomb(r.image, matrixImage) + if err != nil { + return err + } + + r.setImage(out) + return nil +} + // Rotate rotates the image by multiples of 90 degrees. To rotate by arbitrary angles use Similarity. func (r *ImageRef) Rotate(angle Angle) error { width := r.Width() diff --git a/vips/image_test.go b/vips/image_test.go index c7066f11..02328d4a 100644 --- a/vips/image_test.go +++ b/vips/image_test.go @@ -672,6 +672,38 @@ func TestImageRef_CompositeMulti(t *testing.T) { require.NoError(t, err) } +func TestImageRef_Recomb(t *testing.T) { + Startup(nil) + + image, err := NewImageFromFile(resources + "png-24bit.png") + require.NoError(t, err) + + matrix := [][]float64{ + {0.3588, 0.7044, 0.1368}, + {0.2990, 0.5870, 0.1140}, + {0.2392, 0.4696, 0.0912}, + } + + err = image.Recomb(matrix) + require.NoError(t, err) +} + +func TestImageRef_Recomb_Error(t *testing.T) { + Startup(nil) + + image, err := NewImageFromFile(resources + "png-24bit.png") + require.NoError(t, err) + + matrix := [][]float64{ + {0.3588, 0.7044, 0.1368, 0}, + {0.2990, 0.5870, 0.1140, 0}, + {0.2392, 0.4696, 0.0912, 0}, + } + + err = image.Recomb(matrix) + require.Error(t, err) +} + func TestCopy(t *testing.T) { Startup(nil)