Skip to content

Commit

Permalink
Merge pull request #22 from LabsMaya/xenowits/flip-horizontal
Browse files Browse the repository at this point in the history
flip horizontal transformation
  • Loading branch information
xenowits authored Feb 25, 2024
2 parents 56af3f1 + 7acc2f0 commit 516fe91
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 21 deletions.
21 changes: 0 additions & 21 deletions cmd/brighten.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,27 +207,6 @@ func (c *brightenCircuit) Define(api frontend.API) error {
return nil
}

func isBrightenedVersion(arr1, arr2 [][][]float64, value float64) bool {
// Check if the dimensions of both images are the same
if len(arr1) != len(arr2) {
return false
}
for x := range arr1[0] { // Swap: Iterate over x first
for y := range arr1 { // Swap: Then iterate over y
if len(arr1[y][x]) != len(arr2[y][x]) {
return false
}
for c := range arr1[y][x] {
// Check if arr2 is a brightened version of arr1 by the factor "value"
if arr2[y][x][c] != arr1[y][x][c]*value {
return false
}
}
}
}
return true
}

// verifyBrightenConfig specifies the verification configuration for rotating an image by 90 degrees.
type verifyBrightenConfig struct {
proofDir string
Expand Down
2 changes: 2 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func New() *cobra.Command {
newRotate180Cmd(),
newRotate270Cmd(),
newFlipVerticalCmd(),
newFlipHorizontalCmd(),
newBrightenCmd(),
),
newVerifyCmd(
Expand All @@ -21,6 +22,7 @@ func New() *cobra.Command {
newVerifyRotate180Cmd(),
newVerifyRotate270Cmd(),
newVerifyFlipVerticalCmd(),
newVerifyFlipHorizontalCmd(),
newVerifyBrightenCmd(),
),
)
Expand Down
279 changes: 279 additions & 0 deletions cmd/fliphorizontal.go
Original file line number Diff line number Diff line change
@@ -1 +1,280 @@
package cmd

import (
"fmt"
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/spf13/cobra"
"os"
"path"
"time"
)

// flipHorizontalConfig specifies the configuration for flipping an image horizontally.
type flipHorizontalConfig struct {
originalImg string
finalImg string
proofDir string
}

// newFlipHorizontalCmd returns a new cobra.Command for flipping an image horizontally.
func newFlipHorizontalCmd() *cobra.Command {
var conf flipHorizontalConfig

cmd := &cobra.Command{
Use: "flip-horizontal",
RunE: func(cmd *cobra.Command, args []string) error {
return proveFlipHorizontal(conf)
},
}

bindFlipHorizontalFlags(cmd, &conf)

return cmd
}

// bindFlipHorizontalFlags binds the flip horizontal configuration flags.
func bindFlipHorizontalFlags(cmd *cobra.Command, conf *flipHorizontalConfig) {
cmd.Flags().StringVar(&conf.originalImg, "original-image", "", "The path to the original image. Supported image formats: PNG.")
cmd.Flags().StringVar(&conf.finalImg, "final-image", "", "The path to the final image. Supported image formats: PNG.")
cmd.Flags().StringVar(&conf.proofDir, "proof-dir", "", "The path to the proof directory.")
}

// proveFlipHorizontal generates the zk proof of flip horizontal transformation.
func proveFlipHorizontal(config flipHorizontalConfig) error {
// Open the original image file.
originalImage, err := os.Open(config.originalImg)
if err != nil {
return err
}
defer originalImage.Close()

// Open the final image file.
finalImage, err := os.Open(config.finalImg)
if err != nil {
return err
}
defer finalImage.Close()

// Get the pixel values for the original image.
originalPixels, err := convertImgToPixels(originalImage)
if err != nil {
return err
}

// Get the pixel values for the final image.
finalPixels, err := convertImgToPixels(finalImage)
if err != nil {
return err
}

proof, vk, err := generateFlipHorizontalProof(originalPixels, finalPixels)
if err != nil {
return err
}

flipHorizontalDir := path.Join(config.proofDir, "flipHorizontal")
if err = os.MkdirAll(flipHorizontalDir, 0o777); err != nil {
return err
}

proofFile, err := os.Create(path.Join(flipHorizontalDir, "proof.bin"))
if err != nil {
return err
}
defer proofFile.Close()

n, err := proof.WriteTo(proofFile)
if err != nil {
return err
}

fmt.Println("Proof Size: ", n)

vkFile, err := os.Create(path.Join(flipHorizontalDir, "vkey.bin"))
if err != nil {
return err
}
defer vkFile.Close()

_, err = vk.WriteTo(vkFile)
if err != nil {
return err
}

return nil
}

// generateFlipHorizontalProof returns the proof of flipHorizontal transformation.
func generateFlipHorizontalProof(original, flipped [][][]uint8) (groth16.Proof, groth16.VerifyingKey, error) {
var circuit FlipHorizontalCircuit
circuit.Original = make([][][]frontend.Variable, len(original)) // First dimension
for i := range original {
circuit.Original[i] = make([][]frontend.Variable, len(original[i])) // Second dimension
for j := range circuit.Original[i] {
circuit.Original[i][j] = make([]frontend.Variable, len(original[i][j])) // Third dimension
}
}

circuit.Flipped = make([][][]frontend.Variable, len(flipped)) // First dimension
for i := range flipped {
circuit.Flipped[i] = make([][]frontend.Variable, len(flipped[i])) // Second dimension
for j := range circuit.Flipped[i] {
circuit.Flipped[i][j] = make([]frontend.Variable, len(flipped[i][j])) // Third dimension
}
}

t0 := time.Now()
cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit)
if err != nil {
panic(err)
}

fmt.Println("Flip Horizontal compilation time:", time.Since(t0).Seconds())

t0 = time.Now()
witness, err := frontend.NewWitness(&FlipHorizontalCircuit{
Original: convertToFrontendVariable(original),
Flipped: convertToFrontendVariable(flipped),
}, ecc.BN254.ScalarField())
if err != nil {
return nil, nil, err
}

pk, vk, err := groth16.Setup(cs)
if err != nil {
return nil, nil, err
}

proof, err := groth16.Prove(cs, pk, witness)
if err != nil {
return nil, nil, err
}

fmt.Println("Time taken to prove: ", time.Since(t0).Seconds())

return proof, vk, nil
}

// FlipHorizontalCircuit represents the arithmetic circuit to prove flip horizontal transformations.
type FlipHorizontalCircuit struct {
Original [][][]frontend.Variable `gnark:",secret"`
Flipped [][][]frontend.Variable `gnark:",public"`
}

func (c *FlipHorizontalCircuit) Define(api frontend.API) error {
api.AssertIsDifferent(len(c.Original), 0)
api.AssertIsDifferent(len(c.Flipped), 0)
api.AssertIsEqual(len(c.Original), len(c.Flipped))
api.AssertIsEqual(len(c.Original[0]), len(c.Flipped[0]))

// The pixel values for the original and flipped images must match exactly.
for i := 0; i < len(c.Original); i++ {
for j := 0; j < len(c.Original[i]); j++ {
j2 := len(c.Original[i]) - j - 1
api.AssertIsEqual(c.Original[i][j][0], c.Flipped[i][j2][0]) // R
api.AssertIsEqual(c.Original[i][j][1], c.Flipped[i][j2][1]) // G
api.AssertIsEqual(c.Original[i][j][2], c.Flipped[i][j2][2]) // B
}
}

return nil
}

// isHorizontalFlip checks if arr2 is a horizontal flip of arr1
func isHorizontalFlip(arr1, arr2 [][][]uint8) bool {
if len(arr1) != len(arr2) || len(arr1[0]) != len(arr2[0]) {
return false // Different dimensions
}

for y := range arr1 {
for x := range arr1[y] {
// Compare the pixel at (x, y) in arr1 with the pixel at (width-x-1, y) in arr2
x2 := len(arr1[y]) - 1 - x
for c := range arr1[y][x] {
if arr1[y][x][c] != arr2[y][x2][c] {
return false
}
}
}
}

return true
}

// verifyFlipHorizontalConfig specifies the verification configuration for rotating an image by 270 degrees.
type verifyFlipHorizontalConfig struct {
proofDir string
finalImg string
}

// newVerifyFlipHorizontalCmd returns a new cobra.Command for rotating an image by 270 degrees.
func newVerifyFlipHorizontalCmd() *cobra.Command {
var conf verifyFlipHorizontalConfig

cmd := &cobra.Command{
Use: "flip-horizontal",
RunE: func(cmd *cobra.Command, args []string) error {
return verifyFlipHorizontal(conf)
},
}

cmd.Flags().StringVar(&conf.proofDir, "proof-dir", "", "The path to the proof directory.")
cmd.Flags().StringVar(&conf.finalImg, "final-image", "", "The path to the final image. Supported image formats: PNG.")

return cmd
}

// verifyFlipHorizontal verifies the zk proof of flip horizontal transformation.
func verifyFlipHorizontal(config verifyFlipHorizontalConfig) error {
// Open the final image file.
finalImage, err := os.Open(config.finalImg)
if err != nil {
return err
}
defer finalImage.Close()

// Get the pixel values for the final image.
finalPixels, err := convertImgToPixels(finalImage)
if err != nil {
return err
}

witness, err := frontend.NewWitness(&FlipHorizontalCircuit{
Flipped: convertToFrontendVariable(finalPixels),
}, ecc.BN254.ScalarField())
if err != nil {
return err
}

flipHorizontalDir := path.Join(config.proofDir, "flipHorizontal")
if err = os.MkdirAll(flipHorizontalDir, 0o777); err != nil {
return err
}

proof, err := readProof(path.Join(flipHorizontalDir, "proof.bin"))
if err != nil {
return err
}

vk, err := readVerifyingKey(path.Join(flipHorizontalDir, "vkey.bin"))
if err != nil {
return err
}

publicWitness, err := witness.Public()
if err != nil {
return err
}

err = groth16.Verify(proof, vk, publicWitness)
if err != nil {
fmt.Println("Invalid proof 😞")
} else {
fmt.Println("Proof verified 🎉")
}

return nil
}
26 changes: 26 additions & 0 deletions cmd/fliphorizontal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cmd

import (
"github.com/stretchr/testify/require"
"testing"
)

func TestProveFlipHorizontal(t *testing.T) {
proofDir := t.TempDir()
conf := flipHorizontalConfig{
originalImg: "../sample/original.png",
finalImg: "../sample/flipped_horizontal.png",
proofDir: proofDir,
}

err := proveFlipHorizontal(conf)
require.NoError(t, err)

verifyConf := verifyFlipHorizontalConfig{
finalImg: "../sample/flipped_horizontal.png",
proofDir: proofDir,
}

err = verifyFlipHorizontal(verifyConf)
require.NoError(t, err)
}
Binary file added sample/flipped_horizontal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 516fe91

Please sign in to comment.