Rodin Mesh

The Rodin mesh function is designed to create 3D meshes from images based on specific prompts and settings.

The request body should be wrapped in the FormData instead of plain JSON, while settings should be parsed to JSON.

The image passed to the Rodin mesh endpoint must be segmented with an alpha channel. Otherwise, the image will need to be preprocessed with image preprocessing API.

For the initial generation, images should also be wrapped in the FormData with each image binary appended under the field name images. For example, if there are two images, there should be two images fields, each followed by the binary of one image.

Also, for settings, only params marked as Mesh Generation Only need to be passed through.

Examples

package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/textproto"
	"os"
)

const BASE_URL = "http://localhost:3005/api"

func rodinPreprocessImage(generatePrompt bool, image io.Reader, name string, token string) map[string]interface{} {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	generatePromptValue := "false"
	if generatePrompt {
		generatePromptValue = "true"
	}
	if err := writer.WriteField("generate_prompt", generatePromptValue); err != nil {
		log.Fatal("Error writing field 'generate_prompt':", err)
	}

	part, err := writer.CreateFormFile("images", name)
	if err != nil {
		log.Fatal("Error creating form file:", err)
	}
	if _, err := io.Copy(part, image); err != nil {
		log.Fatal("Error copying image data:", err)
	}

	if err := writer.Close(); err != nil {
		log.Fatal("Error closing writer:", err)
	}

	req, err := http.NewRequest("POST", BASE_URL+"/task/rodin_mesh_image_process", body)
	if err != nil {
		log.Fatal("Error creating request:", err)
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	req.Header.Set("Authorization", "Bearer "+token)
	
	client := &http.Client{}
	response, err := client.Do(req)
	if err != nil {
		log.Fatal("Error sending request:", err)
	}
	defer response.Body.Close()
	
	respBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal("Error reading response body:", err)
	}
	log.Println("Response Body:", string(respBody))

	var result map[string]interface{}
	if err := json.Unmarshal(respBody, &result); err != nil {
		log.Fatal("Error unmarshaling response:", err)
	}
	return result
}

func rodinMesh(prompt string, groupUUID string, settings map[string]interface{}, images []io.Reader, name string, token string) map[string]interface{} {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	writer.WriteField("prompt", prompt)
	writer.WriteField("group_uuid", groupUUID)
	writer.WriteField("settings", toJSON(settings))

	for _, img := range images {
		h := make(textproto.MIMEHeader)
		h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="images"; filename="%s"`, name))
		h.Set("Content-Type", "image/png")

		part, err := writer.CreatePart(h)
		if err != nil {
			log.Fatal("Error creating form part with MIME type:", err)
		}
		if _, err := io.Copy(part, img); err != nil {
			log.Fatal("Error copying image data:", err)
		}
	}

	if err := writer.Close(); err != nil {
		log.Fatal("Error closing writer:", err)
	}

	req, err := http.NewRequest("POST", BASE_URL+"/task/rodin_mesh", body)
	if err != nil {
		log.Fatal("Error creating request:", err)
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	req.Header.Set("Authorization", "Bearer "+token)

	client := &http.Client{}
	response, err := client.Do(req)
	if err != nil {
		log.Fatal("Error sending request:", err)
	}
	defer response.Body.Close()

	respBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal("Error reading response body:", err)
	}

	var result map[string]interface{}
	if err := json.Unmarshal(respBody, &result); err != nil {
		log.Fatal("Error unmarshaling response:", err)
	}
	return result
}

func convertBase64ToBinary(base64String string) *bytes.Reader {
	data, err := base64.StdEncoding.DecodeString(base64String)
	if err != nil {
		log.Fatal("Error decoding base64 string:", err)
	}
	return bytes.NewReader(data)
}

func toJSON(data interface{}) string {
	bytes, _ := json.Marshal(data)
	return string(bytes)
}

func main() {
	token := "eyJhbGciOiJIUzI1NiJ9.MSA1NDc1.J-zXs6y3aKS40eWB-iebtFiTUvG2M6ez3pREmTvjIXo"
	imageName := "images.jpeg"
	imageFile, err := os.Open(imageName)
	if err != nil {
		log.Fatal("Invalid image file.")
	}
	defer imageFile.Close()

	preprocessResponse := rodinPreprocessImage(true, imageFile, imageName, token)
	if errorVal, exists := preprocessResponse["error"]; exists && errorVal != nil {
		log.Println("Error in image preprocessing:", errorVal)
		return
	}

	prompt, ok := preprocessResponse["prompt"].(string)
	if !ok {
		prompt = "Default prompt if none returned"
	}

	processedImageBase64, ok := preprocessResponse["processed_image"].(string)
	if !ok {
		log.Println("Processed image is missing or not a string", prompt, preprocessResponse)
		return
	}
	processedImage := processedImageBase64
	log.Println("Data length:", len(processedImage))

	settings := map[string]interface{}{"view_weights": []float64{1}}
	log.Println(processedImage)
	images := []io.Reader{convertBase64ToBinary(processedImage)}

	for _, img := range images {
		if byteReader, ok := img.(*bytes.Reader); ok {
			log.Println("Image data length:", byteReader.Len())
			if byteReader.Len() < 10 {
				log.Println("Warning: Image data is too small, likely incorrect.")
			}
		}
	}

	log.Println("Images:", images)
	meshResponse := rodinMesh(prompt, "", settings, images, imageName, token)
	if errorVal, exists := meshResponse["error"]; exists && errorVal != nil {
		log.Println("Error in Rodin mesh generation:", errorVal)
		return
	}

	log.Println(meshResponse)
}

Last updated