Skip to content

Commit

Permalink
feat: add convertToGreyScale
Browse files Browse the repository at this point in the history
  • Loading branch information
MM-coder committed Dec 8, 2023
1 parent 1e5c6a3 commit 374ccc1
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 11 deletions.
26 changes: 26 additions & 0 deletions colometry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import "errors"

// convertToGreyScale converts a matrix to greyscale equivalent
func convertToGreyScale(matrix [][][4]uint32) ([][][4]uint32, error) {
height := len(matrix) // Get the height of the matrix
if height == 0 {
return nil, errors.New("empty matrix")
}

width := len(matrix[0])

greyScaleImage := Make2D[[4]uint32](height, width) // Create a new image

// Iterate through all pixels
for y := 0; y < height; y++ {
for x := range matrix[y] {
current := matrix[y][x] // Convenience, cleans up the following line, does allocate more memory however
r, g, b, a := current[0], current[1], current[2], current[3] // Thank you Go for not providing list expansion
luminance := uint32(float64(r)*0.299 + float64(g)*0.587 + float64(b)*0.114) // Apply the formula
greyScaleImage[y][x] = [4]uint32{luminance, luminance, luminance, a} // Write the luminance value, keeping the alpha as current
}
}
return greyScaleImage, nil
}
2 changes: 1 addition & 1 deletion filters.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package matrix_image_manipulation
package main

import (
"errors"
Expand Down
2 changes: 1 addition & 1 deletion helpers.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package matrix_image_manipulation
package main

import (
"bytes"
Expand Down
56 changes: 48 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package matrix_image_manipulation
package main

import (
"errors"
Expand All @@ -7,26 +7,66 @@ import (
"image/color"
"image/png"
"os"
"path/filepath"
"strings"
)

func main() {
var path string
var path, choice string

// Request the file path from the user
fmt.Print("Input file path: ")
_, err := fmt.Scan(&path)
_, err := fmt.Scanln(&path)
if err != nil {
fmt.Println("Error requesting input from user.")
fmt.Println("Error requesting input from user:", err)
return
}

// Read the image into a matrix
matrix, err := readImageToMatrix(path)
if err != nil {
fmt.Println(err)
fmt.Println("Error reading image:", err)
return
}

// Ask the user for the operation to perform
fmt.Println("Escolha uma operação:")
fmt.Println("1: Filtro Gaussiano")
fmt.Println("2: Converter para Grayscale")
fmt.Println("Escolha (1 or 2):")
_, err = fmt.Scanln(&choice)
if err != nil {
fmt.Println("Error reading choice:", err)
return
}

switch choice {
case "1":
matrix, err = gaussianFilter(matrix, 7, 10.5)
if err != nil {
fmt.Println("Erro a aplicar filtro Gaussiano:", err)
return
}
case "2":
matrix, err = convertToGreyScale(matrix)
if err != nil {
fmt.Println("Erro a converter para grayscale:", err)
return
}
default:
fmt.Println("Escolha inválida.")
return
}

// Write the modified image back to a file
outputPath := strings.TrimSuffix(path, filepath.Ext(path)) + "_modified.png"
err = writeImageFromMatrix(matrix, outputPath)
if err != nil {
fmt.Println("Error writing image:", err)
return
}
//fmt.Println(matrix)
matrix, _ = gaussianFilter(matrix, 7, 10.5)
_ = writeImageFromMatrix(matrix, strings.TrimSuffix(path, ".png")+"_new.png")

fmt.Println("Operação completada. Output guardado em:", outputPath)
}

// readImageToMatrix takes a path for a given PNG image and returns a 2D uint8 slice that represents the matrix for that image
Expand Down
117 changes: 116 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package matrix_image_manipulation
package main

import (
"errors"
"fmt"
"image"
"image/color"
"math/rand"
"os"
"reflect"
"strconv"
"testing"
)
Expand All @@ -32,6 +34,34 @@ func assertColourEquality(colour1 color.Color, colour2 color.Color) bool {
return r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2
}

// generateRandomImage generates a random image of a given width and height.
func generateRandomImage(width, height int) [][][4]uint32 {
image := Make2D[[4]uint32](height, width)
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
image[y][x] = [4]uint32{
uint32(rand.Intn(256)), // Red
uint32(rand.Intn(256)), // Green
uint32(rand.Intn(256)), // Blue
uint32(rand.Intn(256)), // Alpha
}
}
}
return image
}

func assertValidMatrix(matrix [][][4]uint32) error {
for y := range matrix {
for x := range matrix[y] {
pixel := matrix[y][x]
if pixel[0] > 255 || pixel[1] > 255 || pixel[2] > 255 || pixel[3] > 255 {
return errors.New("invalid matrix: values exceed 255")
}
}
}
return nil
}

// TestMatrixContinuity tests that if an image is converted to a matrix, and then back, it remains the same
func TestMatrixContinuity(t *testing.T) {

Expand Down Expand Up @@ -109,3 +139,88 @@ func TestMake2D(t *testing.T) {
}
}
}

// TestConvertToGreyScaleWithRandomInput tests convertToGreyScale function with a randomly generated input.
func TestConvertToGreyScaleWithRandomInput(t *testing.T) {

width, height := rand.Intn(3841), rand.Intn(2161) // Random dimensions up to 4k
randomImage := generateRandomImage(width, height)

expected := make([][][4]uint32, height)
for y := 0; y < height; y++ {
expected[y] = make([][4]uint32, width)
for x := 0; x < width; x++ {
r, g, b, a := randomImage[y][x][0], randomImage[y][x][1], randomImage[y][x][2], randomImage[y][x][3]
luminance := uint32(float64(r)*0.299 + float64(g)*0.587 + float64(b)*0.114)
expected[y][x] = [4]uint32{luminance, luminance, luminance, a}
}
}

result, err := convertToGreyScale(randomImage)
if err != nil {
t.Errorf("convertToGreyScale() returned an unexpected error: %v", err)
}
if !reflect.DeepEqual(result, expected) {
t.Errorf("convertToGreyScale() result does not match expected output")
}
}

// TestGenerateRandomImage tests the generateRandomImage function.
func TestGenerateRandomImage(t *testing.T) {
width, height := rand.Intn(3841), rand.Intn(2161) // Random dimensions up to 4k

// Generate a random image
randomImage := generateRandomImage(width, height)

// Assert that the generated image is valid
err := assertValidMatrix(randomImage)
if err != nil {
t.Errorf("generateRandomImage() generated an invalid matrix: %v", err)
}
}

// TestAssertValidMatrix tests the assertValidMatrix function with a random number of valid and invalid matrices.
func TestAssertValidMatrix(t *testing.T) {

// Inline function to generate a random pixel
generateRandomPixel := func(maxVal uint32) [4]uint32 {
return [4]uint32{
uint32(rand.Intn(int(maxVal) + 1)),
uint32(rand.Intn(int(maxVal) + 1)),
uint32(rand.Intn(int(maxVal) + 1)),
uint32(rand.Intn(int(maxVal) + 1)),
} // I miss list comprehensions
}

// Function to generate a random matrix using Make2D
generateRandomMatrix := func(width int, height int, valid bool) [][][4]uint32 {
matrix := Make2D[[4]uint32](height, width)
maxVal := uint32(255)
if !valid {
maxVal = 300 // Ensure an invalid matrix
}
for i := range matrix {
for j := range matrix[i] {
matrix[i][j] = generateRandomPixel(maxVal)
}
}
return matrix
}

// Generate a random number of test cases
numTests := rand.Intn(10) + 1 // At least 1 test, up to 10

for i := 0; i < numTests; i++ {

valid := rand.Intn(2) == 0 // Randomly decide if this matrix should be valid or not
width, height := rand.Intn(3841), rand.Intn(2161) // Random dimensions up to 4k
matrix := generateRandomMatrix(width, height, valid)

err := assertValidMatrix(matrix)
if valid && err != nil {
t.Errorf("assertValidMatrix() returned an error for a valid matrix: %v", err)
} else if !valid && err == nil {
t.Errorf("assertValidMatrix() did not return an error for an invalid matrix")
}
}
}

0 comments on commit 374ccc1

Please sign in to comment.