commit ea0f22a2ca4344c3bb0e9219d8594209641daee4 Author: talksik Date: Tue Jan 20 18:09:07 2026 -0800 filter using ffmpeg on linux with rembg diff --git a/README.md b/README.md new file mode 100644 index 0000000..88c9d3d --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Photo Background Remover + +A simple Go program that captures a photo from your webcam and removes the background. + +## Prerequisites + +### 1. Install ffmpeg +```bash +# Ubuntu/Debian +sudo apt install ffmpeg + +# macOS +brew install ffmpeg +``` + +### 2. Install Docker +```bash +# Ubuntu/Debian +sudo apt install docker.io + +# macOS +brew install --cask docker +``` + +The program uses the `danielgatis/rembg` Docker container for background removal. The AI model (~176MB) will be downloaded automatically on first run. + +## Usage + +1. Build and run the program: +```bash +go run main.go +``` + +2. The program will: + - Capture a photo from your webcam + - Remove the background + - Save both images in the `output/` directory + +## Output + +- `output/photo_TIMESTAMP.jpg` - Original photo +- `output/photo_TIMESTAMP_nobg.png` - Photo with background removed + +## Troubleshooting + +- **Webcam not found**: Make sure `/dev/video0` exists (Linux) or adjust the device in the code +- **ffmpeg not found**: Install ffmpeg using the commands above +- **Docker not found**: Install Docker using the commands above +- **Docker permission denied**: Add your user to the docker group: `sudo usermod -a -G docker $USER` (then log out and back in) +- **Webcam permission denied**: You may need to add your user to the `video` group: `sudo usermod -a -G video $USER` diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..614edfb --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module video-filter + +go 1.25.2 diff --git a/main.go b/main.go new file mode 100644 index 0000000..7deeb0e --- /dev/null +++ b/main.go @@ -0,0 +1,105 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "time" +) + +func main() { + // Create output directory if it doesn't exist + outputDir := "output" + if err := os.MkdirAll(outputDir, 0755); err != nil { + fmt.Printf("Error creating output directory: %v\n", err) + os.Exit(1) + } + + // Generate filenames with timestamp + timestamp := time.Now().Format("20060102_150405") + originalPhoto := filepath.Join(outputDir, fmt.Sprintf("photo_%s.jpg", timestamp)) + processedPhoto := filepath.Join(outputDir, fmt.Sprintf("photo_%s_nobg.png", timestamp)) + + fmt.Println("šŸ“ø Capturing photo from webcam...") + if err := capturePhoto(originalPhoto); err != nil { + fmt.Printf("Error capturing photo: %v\n", err) + os.Exit(1) + } + fmt.Printf("āœ“ Photo saved to: %s\n", originalPhoto) + + fmt.Println("šŸŽØ Removing background...") + if err := removeBackground(originalPhoto, processedPhoto); err != nil { + fmt.Printf("Error removing background: %v\n", err) + os.Exit(1) + } + fmt.Printf("āœ“ Processed photo saved to: %s\n", processedPhoto) + + fmt.Println("\nšŸŽ‰ Done! Background removed successfully!") +} + +// capturePhoto captures a single frame from the webcam using ffmpeg +func capturePhoto(outputPath string) error { + // For Linux, typically /dev/video0 + // For macOS, use "0:0" with avfoundation + // For Windows, use "video=CAMERA_NAME" with dshow + + var cmd *exec.Cmd + + // Detect platform and use appropriate video device + if _, err := os.Stat("/dev/video0"); err == nil { + // Linux + cmd = exec.Command("ffmpeg", + "-f", "v4l2", + "-i", "/dev/video0", + "-frames:v", "1", + "-y", + outputPath, + ) + } else { + // Try macOS + cmd = exec.Command("ffmpeg", + "-f", "avfoundation", + "-i", "0", + "-frames:v", "1", + "-y", + outputPath, + ) + } + + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("ffmpeg error: %v\nOutput: %s", err, string(output)) + } + + return nil +} + +// removeBackground removes the background using rembg Docker container +func removeBackground(inputPath, outputPath string) error { + // Get absolute path of current directory + cwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("failed to get working directory: %v", err) + } + + // Docker paths are relative to /data mount point + dockerInput := "/data/" + inputPath + dockerOutput := "/data/" + outputPath + + // Use rembg Docker container + cmd := exec.Command("docker", "run", "--rm", + "-v", fmt.Sprintf("%s:/data", cwd), + "danielgatis/rembg", + "i", + dockerInput, + dockerOutput, + ) + + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("docker/rembg error: %v\nOutput: %s", err, string(output)) + } + + return nil +} diff --git a/output/photo_20260120_180612.jpg b/output/photo_20260120_180612.jpg new file mode 100644 index 0000000..e01dc10 Binary files /dev/null and b/output/photo_20260120_180612.jpg differ diff --git a/output/photo_20260120_180612_nobg.png b/output/photo_20260120_180612_nobg.png new file mode 100644 index 0000000..c07307b Binary files /dev/null and b/output/photo_20260120_180612_nobg.png differ