|
|
|
|
|
|
|
|
|
|
|
|
|
#include <iostream> |
|
#include <fstream> |
|
|
|
#include <opencv2/imgproc.hpp> |
|
#include <opencv2/highgui.hpp> |
|
#include <opencv2/dnn.hpp> |
|
|
|
using namespace cv; |
|
using namespace dnn; |
|
using namespace std; |
|
|
|
class Lama { |
|
public: |
|
Lama(const string& modelPath) { |
|
loadModel(modelPath); |
|
} |
|
|
|
|
|
void process(const Mat& image, const Mat& mask, Mat& result) { |
|
double aspectRatio = static_cast<double>(image.rows) / static_cast<double>(image.cols); |
|
|
|
Mat image_blob = blobFromImage(image, 1.0/255.0, Size(512, 512), Scalar(0, 0, 0), false, false, CV_32F); |
|
Mat mask_blob = blobFromImage(mask, 1.0, Size(512, 512), Scalar(0), false, false); |
|
|
|
mask_blob = (mask_blob > 0); |
|
mask_blob.convertTo(mask_blob, CV_32F); |
|
mask_blob = mask_blob/255.0; |
|
|
|
net.setInput(image_blob, "image"); |
|
net.setInput(mask_blob, "mask"); |
|
|
|
Mat output = net.forward(); |
|
|
|
postProcess(output, result, aspectRatio); |
|
} |
|
private: |
|
Net net; |
|
|
|
|
|
void loadModel(const string modelPath) { |
|
net = readNetFromONNX(modelPath); |
|
net.setPreferableBackend(DNN_BACKEND_DEFAULT); |
|
net.setPreferableTarget(DNN_TARGET_CPU); |
|
} |
|
|
|
void postProcess(const Mat& output, Mat& result, double aspectRatio) { |
|
Mat output_transposed(3, &output.size[1], CV_32F, const_cast<void*>(reinterpret_cast<const void*>(output.ptr<float>()))); |
|
|
|
vector<Mat> channels; |
|
for (int i = 0; i < 3; ++i) { |
|
channels.push_back(Mat(output_transposed.size[1], output_transposed.size[2], CV_32F, |
|
output_transposed.ptr<float>(i))); |
|
} |
|
merge(channels, result); |
|
result.convertTo(result, CV_8U); |
|
|
|
int h = static_cast<int>(512 * aspectRatio); |
|
resize(result, result, Size(512, h)); |
|
} |
|
}; |
|
|
|
|
|
const string about = "This sample demonstrates image inpainting with lama inpainting technique.\n\n"; |
|
|
|
const string keys = |
|
"{help h | | show help message}" |
|
"{input i | | Path to input image}" |
|
"{ model | inpainting_lama_2024jan.onnx | Path to the lama onnx model file }"; |
|
|
|
bool drawing = false; |
|
Mat maskGray; |
|
int brush_size = 25; |
|
|
|
static void drawMask(int event, int x, int y, int, void*) { |
|
if (event == EVENT_LBUTTONDOWN) { |
|
drawing = true; |
|
} else if (event == EVENT_MOUSEMOVE) { |
|
if (drawing) { |
|
circle(maskGray, Point(x, y), brush_size, Scalar(255), -1); |
|
} |
|
} else if (event == EVENT_LBUTTONUP) { |
|
drawing = false; |
|
} |
|
} |
|
|
|
int main(int argc, char **argv) |
|
{ |
|
CommandLineParser parser(argc, argv, keys); |
|
|
|
if (parser.has("help")) |
|
{ |
|
cout<<about<<endl; |
|
parser.printMessage(); |
|
return 0; |
|
} |
|
parser = CommandLineParser(argc, argv, keys); |
|
parser.about(about); |
|
|
|
const string model = parser.get<String>("model"); |
|
|
|
int height = 512; |
|
int width = 512; |
|
int stdSize = 20; |
|
int stdWeight = 400; |
|
int stdImgSize = 512; |
|
int imgWidth = -1; |
|
int fontSize = 50; |
|
int fontWeight = 500; |
|
|
|
FontFace fontFace("sans"); |
|
Lama lama(model); |
|
|
|
Mat image = imread(parser.get<String>("input")); |
|
if (image.empty()) { |
|
cerr << "Error: Input image could not be loaded." << endl; |
|
return -1; |
|
} |
|
|
|
imgWidth = min(image.rows, image.cols); |
|
fontSize = min(fontSize, (stdSize*imgWidth)/stdImgSize); |
|
fontWeight = min(fontWeight, (stdWeight*imgWidth)/stdImgSize); |
|
|
|
maskGray = Mat::zeros(image.size(), CV_8U); |
|
|
|
namedWindow("Draw Mask"); |
|
setMouseCallback("Draw Mask", drawMask); |
|
|
|
const string label = "Draw the mask on the image. Press space bar when done "; |
|
|
|
for(;;) { |
|
Mat displayImage = image.clone(); |
|
Mat overlay = image.clone(); |
|
|
|
double alpha = 0.5; |
|
Rect r = getTextSize(Size(), label, Point(), fontFace, fontSize, fontWeight); |
|
r.height += 2 * fontSize; |
|
r.width += 10; |
|
rectangle(overlay, r, Scalar::all(255), FILLED); |
|
addWeighted(overlay, alpha, displayImage, 1 - alpha, 0, displayImage); |
|
putText(displayImage, label, Point(10, fontSize), Scalar(0,0,0), fontFace, fontSize, fontWeight); |
|
putText(displayImage, "Press 'i' to increase and 'd' to decrease brush size", Point(10, 2*fontSize), Scalar(0,0,0), fontFace, fontSize, fontWeight); |
|
|
|
displayImage.setTo(Scalar(255, 255, 255), maskGray > 0); |
|
imshow("Draw Mask", displayImage); |
|
|
|
char key = waitKey(1); |
|
if (key == 'i') { |
|
brush_size += 1; |
|
cout << "Brush size increased to " << brush_size << endl; |
|
} else if (key == 'd') { |
|
brush_size = max(1, brush_size - 1); |
|
cout << "Brush size decreased to " << brush_size << endl; |
|
} else if (key == ' ') { |
|
break; |
|
} else if (key == 27){ |
|
return -1; |
|
} |
|
} |
|
destroyAllWindows(); |
|
|
|
Mat result; |
|
lama.process(image, maskGray, result); |
|
|
|
imshow("Inpainted Output", result); |
|
waitKey(0); |
|
|
|
return 0; |
|
} |
|
|