Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flip horizontal transformation #22

Merged
merged 2 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Loading