Skip to content

Commit 9474863

Browse files
committed
Add segmented style transfer demo, and opencv code test for calling from chapel. didn't work.
1 parent aed5d9d commit 9474863

6 files changed

Lines changed: 322 additions & 30 deletions

File tree

demos/video/style-transfer/build.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33

44
# /usr/bin/clang++ -std=c++20 -c -fPIC mirror.cpp -o mirror.o $(pkg-config --cflags --libs opencv4)
55

6-
g++ -std=c++20 -c -fPIC mirror.cpp -o mirror.o $(pkg-config --cflags opencv4)
7-
chpl mirror.h mirror.o mirror.chpl --fast --print-commands --ldflags $(pkg-config --cflags --libs opencv4) -lstdc++
6+
SDL2LIBS="-I/opt/homebrew/include -I/opt/homebrew/include/SDL2 -L/opt/homebrew/lib -lSDL2"
7+
8+
g++ -std=c++20 -c -fPIC mirror.cpp -o mirror.o $(pkg-config --cflags opencv4) $(pkg-config --cflags sdl2)
9+
chpl mirror.h mirror.o mirror.chpl --print-commands --ldflags $(pkg-config --cflags --libs opencv4) -lstdc++
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import cv2
2+
import torch
3+
import numpy as np
4+
5+
def draw_border(img, pt1, pt2, color, thickness, r, d):
6+
x1,y1 = pt1
7+
x2,y2 = pt2
8+
9+
# Top left
10+
cv2.line(img, (x1 + r, y1), (x1 + r + d, y1), color, thickness)
11+
cv2.line(img, (x1, y1 + r), (x1, y1 + r + d), color, thickness)
12+
cv2.ellipse(img, (x1 + r, y1 + r), (r, r), 180, 0, 90, color, thickness)
13+
14+
# Top right
15+
cv2.line(img, (x2 - r, y1), (x2 - r - d, y1), color, thickness)
16+
cv2.line(img, (x2, y1 + r), (x2, y1 + r + d), color, thickness)
17+
cv2.ellipse(img, (x2 - r, y1 + r), (r, r), 270, 0, 90, color, thickness)
18+
19+
# Bottom left
20+
cv2.line(img, (x1 + r, y2), (x1 + r + d, y2), color, thickness)
21+
cv2.line(img, (x1, y2 - r), (x1, y2 - r - d), color, thickness)
22+
cv2.ellipse(img, (x1 + r, y2 - r), (r, r), 90, 0, 90, color, thickness)
23+
24+
# Bottom right
25+
cv2.line(img, (x2 - r, y2), (x2 - r - d, y2), color, thickness)
26+
cv2.line(img, (x2, y2 - r), (x2, y2 - r - d), color, thickness)
27+
cv2.ellipse(img, (x2 - r, y2 - r), (r, r), 0, 0, 90, color, thickness)
28+
29+
30+
def tensor_to_bgr(frame_tensor, *, undo_normalise=False, mean=None, std=None):
31+
"""
32+
Args
33+
----
34+
frame_tensor : torch.Tensor
35+
(C,H,W) or (1,C,H,W) ― float or half ― RGB
36+
undo_normalise : bool
37+
True if you previously applied (x - mean) / std
38+
mean, std : list/tuple of 3 floats
39+
Same numbers you used for normalising (e.g. ImageNet)
40+
Returns
41+
-------
42+
frame_bgr : np.ndarray (H,W,3) uint8 BGR contiguous
43+
"""
44+
# 1) squeeze batch dimension if present
45+
if frame_tensor.ndim == 4:
46+
frame_tensor = frame_tensor[0]
47+
48+
# 2) move to CPU & float32 for math
49+
img = frame_tensor.detach()
50+
51+
# 3) (optional) reverse mean/std normalisation
52+
if undo_normalise:
53+
if mean is None or std is None:
54+
raise ValueError("Supply mean and std to undo normalisation")
55+
mean = torch.tensor(mean).to(img).view(3,1,1)
56+
std = torch.tensor(std).to(img).view(3,1,1)
57+
img = img * std + mean
58+
59+
# 4) scale back to 0‑255, clamp, uint8
60+
img = (img * 255.0)
61+
# img = img # .to(torch.float16)
62+
img = img.clamp(0,255).byte()
63+
64+
# 5) channel‑last & numpy
65+
img = img.permute(1,2,0).cpu().numpy() # H,W,C RGB
66+
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) # → BGR
67+
img = np.ascontiguousarray(img) # ensure OpenCV‑happy
68+
return img
69+
70+
71+
def load_model(model_path):
72+
model = torch.jit.load(model_path)
73+
model.to(torch.device('mps'))
74+
model.eval()
75+
return model
76+
77+
def frame_to_tensor(frame):
78+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
79+
80+
# 3) Ensure the array is contiguous (torch needs it) -------------------------
81+
frame = np.ascontiguousarray(frame_rgb)
82+
83+
# 4) numpy -> torch, move channels, scale, add batch if wanted --------------
84+
tensor = torch.from_numpy(frame) # H x W x C, uint8 → int tensor
85+
tensor = tensor.to('mps', non_blocking=True)
86+
87+
tensor = tensor.permute(2, 0, 1) # C x H x W
88+
tensor = tensor.to(torch.float32).div(255.0) # float32, [0,1]
89+
90+
# 5) (Optional) add a batch dim and push to GPU ------------------------------
91+
tensor = tensor.unsqueeze(0) # 1 x C x H x W
92+
return tensor.to(torch.float16)
93+
94+
def model_inference(model, tensor):
95+
return model(tensor) / 255.0
96+
97+
98+
def main():
99+
# Load pre-trained Haar cascade classifier for frontal face detection
100+
# haarcascade_profileface
101+
# haarcascade_frontalface_default
102+
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
103+
104+
# Start video capture from the default webcam (device 0)
105+
cap = cv2.VideoCapture(0)
106+
if not cap.isOpened():
107+
print("Error: Could not open video capture")
108+
return
109+
110+
# starry_v_bt4_1e10_ep2_float16
111+
# nature_oil_painting_ep3_bt4_sw3e10_cw_1e5_float16
112+
# starry_ep3_bt4_sw1e11_cw_1e5_float16 <- one of the better ones
113+
# nature_oil_painting_ep4_bt4_sw1e10_cw_1e5_float16
114+
model = load_model('../models/exports/mps/starry_ep3_bt4_sw1e11_cw_1e5_float16.pt')
115+
116+
print("Press 'q' to quit")
117+
while True:
118+
# Read a frame from the webcam
119+
ret, frame = cap.read()
120+
if not ret:
121+
print("Error: Failed to read frame from webcam")
122+
break
123+
124+
# Convert the frame to grayscale (face detector expects gray images)
125+
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
126+
127+
# Detect faces in the grayscale image
128+
for n in [5,3,1,0]:
129+
faces = face_cascade.detectMultiScale(
130+
gray,
131+
scaleFactor=1.3,
132+
minNeighbors=n,
133+
minSize=(30, 30),
134+
flags=cv2.CASCADE_SCALE_IMAGE
135+
)
136+
if len(faces):
137+
break
138+
139+
x_grow = 1.6
140+
y_grow = 1.9
141+
height, width, channels = frame.shape
142+
143+
face_bounds = []
144+
for (i, (x, y, w, h)) in enumerate(faces):
145+
# Calculate the center of the face
146+
center_x = x + w // 2
147+
center_y = y + h // 2
148+
149+
# Calculate the new width and height
150+
new_w = int(w * x_grow)
151+
new_h = int(h * y_grow)
152+
153+
# Calculate the new top-left corner
154+
new_x = max(0, center_x - new_w // 2)
155+
new_y = max(0, center_y - new_h // 2)
156+
157+
# Ensure the new bounding box is within the image boundaries
158+
new_x = min(new_x, width - new_w)
159+
new_y = min(new_y, height - new_h)
160+
161+
face_bounds.append((new_x, new_y, new_w, new_h))
162+
163+
# Draw bounding boxes around detected faces
164+
for (x, y, w, h) in face_bounds:
165+
face_roi = frame[y:y+h, x:x+w]
166+
# Apply style transfer to the face region
167+
input_tensor = frame_to_tensor(face_roi)
168+
output_tensor = model_inference(model, input_tensor)[:,:,0:h, 0:w]
169+
# print(output_tensor.shape)
170+
output_face = tensor_to_bgr(output_tensor)
171+
# Replace the face region in the original frame with the stylized face
172+
frame[y:y+h, x:x+w] = output_face # output_face[:h, :w]
173+
# draw_border(frame, (x, y), (x + w, y + h), (255,255,127), 2, 20, 20)
174+
# cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
175+
176+
# Display the resulting frame
177+
cv2.imshow('Webcam Face Detection', frame)
178+
179+
# Exit loop when 'q' key is pressed
180+
if cv2.waitKey(1) & 0xFF == ord('q'):
181+
break
182+
183+
# Release resources
184+
cap.release()
185+
cv2.destroyAllWindows()
186+
187+
if __name__ == '__main__':
188+
main()

demos/video/style-transfer/mirror.chpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use CTypes;
1212

1313
extern proc run_mirror(): int;
1414

15+
extern record cvVideoCapture {}
16+
17+
extern proc get_video_capture(): cvVideoCapture;
1518

1619
proc main(args: [] string) {
1720
writeln("Hello, world!");
Lines changed: 122 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,156 @@
11
#include "mirror.h"
2-
2+
// #include <SDL2/SDL.h>
33
#include <opencv2/opencv.hpp>
44
#include <iostream>
55

6+
7+
8+
void displayMirror(cv::VideoCapture &cap, const std::string& windowName) {
9+
cv::Mat frame;
10+
while (true) {
11+
// Capture a new frame from the camera
12+
cap >> frame;
13+
if (frame.empty()) {
14+
std::cout << "Error: Blank frame grabbed" << std::endl;
15+
break;
16+
}
17+
18+
// Show the frame in the window
19+
cv::imshow(windowName, frame);
20+
21+
22+
// Wait for 30ms. Exit if any key is pressed.
23+
if (cv::waitKey(30) >= 0) {
24+
std::cout << "Key pressed, exiting..." << std::endl;
25+
break;
26+
}
27+
}
28+
cap.release();
29+
cv::destroyAllWindows();
30+
}
31+
32+
33+
// int displayMirrorLoopSDL() {
34+
// if (SDL_Init(SDL_INIT_VIDEO) < 0) {
35+
// SDL_Log("Could not initialize SDL: %s", SDL_GetError());
36+
// return -1;
37+
// }
38+
39+
// // 2) Open default webcam via OpenCV
40+
// cv::VideoCapture cap(0);
41+
// if (!cap.isOpened()) {
42+
// SDL_Log("Could not open webcam");
43+
// SDL_Quit();
44+
// return -1;
45+
// }
46+
47+
// // Get camera resolution
48+
// int w = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));
49+
// int h = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));
50+
51+
// // 3) Create a borderless SDL2 window
52+
// SDL_Window* window = SDL_CreateWindow(
53+
// "Webcam",
54+
// SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
55+
// w, h,
56+
// SDL_WINDOW_BORDERLESS | SDL_WINDOW_SHOWN
57+
// );
58+
59+
// SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
60+
// // Create a streaming texture in RGB24 format
61+
// SDL_Texture* texture = SDL_CreateTexture(
62+
// renderer,
63+
// SDL_PIXELFORMAT_RGB24,
64+
// SDL_TEXTUREACCESS_STREAMING,
65+
// w, h
66+
// );
67+
68+
// // 4) Main loop: grab frame, convert, update texture, render
69+
// bool running = true;
70+
// SDL_Event ev;
71+
// while (running) {
72+
// // Handle events
73+
// while (SDL_PollEvent(&ev)) {
74+
// if (ev.type == SDL_QUIT) {
75+
// running = false;
76+
// }
77+
// }
78+
79+
// // Grab frame (BGR), convert to RGB
80+
// cv::Mat frame;
81+
// cap >> frame;
82+
// if (frame.empty()) break;
83+
// cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
84+
85+
// // Update SDL texture with the raw pixel data
86+
// SDL_UpdateTexture(texture, nullptr, frame.data, frame.step);
87+
88+
// // Render it
89+
// SDL_RenderClear(renderer);
90+
// SDL_RenderCopy(renderer, texture, nullptr, nullptr);
91+
// SDL_RenderPresent(renderer);
92+
// }
93+
94+
// // Cleanup
95+
// SDL_DestroyTexture(texture);
96+
// SDL_DestroyRenderer(renderer);
97+
// SDL_DestroyWindow(window);
98+
// SDL_Quit();
99+
// return 0;
100+
// }
101+
6102
// extern "C" void run_mirror() asm ("run_mirror");
7103
extern "C" int run_mirror(void) {
8-
// cv::VideoCapture cap(0); // Open the default camera (0)
104+
105+
// return displayMirrorLoopSDL();
106+
107+
// cv::VideoCapture cap(0); // Open the default camera
9108
// if (!cap.isOpened()) {
10-
// std::cerr << "Error: Could not open camera." << std::endl;
109+
// std::cerr << "Error: Could not open camera" << std::endl;
110+
// return -1;
11111
// }
12112

13-
// cv::Mat frame;
14-
// while (true) {
15-
// cap >> frame; // Capture a new frame
16-
// if (frame.empty()) {
17-
// std::cerr << "Error: Could not capture frame." << std::endl;
18-
// break;
19-
// }
113+
// // Create a window to display the video
114+
// const std::string windowName = "Mirror";
115+
// cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);
20116

21-
// cv::imshow("Webcam", frame); // Display the captured frame
22-
// if (cv::waitKey(30) >= 0) break; // Exit on any key press
23-
// }
117+
// // // Start displaying the video
118+
// displayMirror(cap, windowName);
119+
120+
// return 0;
24121

25-
// cap.release(); // Release the camera
26-
// cv::destroyAllWindows(); // Close all OpenCV windows
27122

28-
cv::VideoCapture cap(0);
123+
cv::VideoCapture cap(0); // Open the default camera
29124
if (!cap.isOpened()) {
30-
std::cerr << "Error: Could not open webcam" << std::endl;
125+
std::cerr << "Error: Could not open camera" << std::endl;
31126
return -1;
32127
}
33128

34-
cv::Mat frame;
35-
const std::string window_name = "Webcam";
36-
37129
// Create a window to display the video
38-
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
130+
const std::string windowName = "Mirror";
131+
cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);
39132

133+
cv::Mat frame;
40134
while (true) {
41135
// Capture a new frame from the camera
42136
cap >> frame;
43137
if (frame.empty()) {
44-
std::cerr << "Error: Blank frame grabbed" << std::endl;
138+
std::cout << "Error: Blank frame grabbed" << std::endl;
45139
break;
46140
}
47141

48142
// Show the frame in the window
49-
cv::imshow(window_name, frame);
143+
cv::imshow(windowName, frame);
144+
50145

51146
// Wait for 30ms. Exit if any key is pressed.
52-
if (cv::waitKey(30) >= 0) break;
147+
if (cv::waitKey(30) >= 0) {
148+
std::cout << "Key pressed, exiting..." << std::endl;
149+
break;
150+
}
53151
}
54-
55-
// Release the camera and destroy the window
56152
cap.release();
57153
cv::destroyAllWindows();
58-
59154
return 0;
60-
61155
}
62156

0 commit comments

Comments
 (0)