diff --git a/samples/dnn/models.yml b/samples/dnn/models.yml index 8ffc92cb46..f5f1a33c42 100644 --- a/samples/dnn/models.yml +++ b/samples/dnn/models.yml @@ -521,3 +521,20 @@ modnet: height: 512 rgb: true sample: "alpha_matting" + +################################################################################ +# Super-resolution models. +################################################################################ + +seemoredetails: + load_info: + url: "https://github.com/Naresh-19/opencv-superres-models/raw/main/seemore_x4v2_static512.onnx" + sha1: "584467bd36f5715aa12c3203bba16e4de6392034" + model: "seemore_x4v2_static512.onnx" + mean: [0.0, 0.0, 0.0] + scale: 0.00392 + rgb: true + width: 512 + height: 512 + sample: "super_resolution" + input: true diff --git a/samples/dnn/super_resolution.cpp b/samples/dnn/super_resolution.cpp new file mode 100644 index 0000000000..076801b00d --- /dev/null +++ b/samples/dnn/super_resolution.cpp @@ -0,0 +1,198 @@ +/* +This file is part of OpenCV project. +It is subject to the license terms in the LICENSE file found in the top-level directory +of this distribution and at http://opencv.org/license.html. + +Copyright (C) 2025, Bigvision LLC. + + +This sample demonstrates super-resolution using the SeeMoreDetails model. +The model upscales images by 4x while enhancing details and reducing noise. +Supports image inputs only. + +SeeMoreDetails Repo: https://github.com/eduardzamfir/seemoredetails +*/ +#include +#include +#include +#include + +#include "common.hpp" + +using namespace cv; +using namespace cv::dnn; +using namespace std; + +const int WINDOW_OFFSET_X = 50; +const int WINDOW_OFFSET_Y = 50; +const int WINDOW_SPACING = 50; + +const string param_keys = + "{ help h | | Print help message }" + "{ @alias | seemoredetails | Model alias from models.yml }" + "{ zoo | ../dnn/models.yml | Path to models.yml file }" + "{ input i | chicky_512.png | Path to input image }" + "{ model | | Path to model file }"; + +const string backend_keys = format( + "{ backend | default | Choose one of computation backends: " + "default: automatically (by default), " + "openvino: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), " + "opencv: OpenCV implementation, " + "vkcom: VKCOM, " + "cuda: CUDA, " + "webnn: WebNN }"); + +const string target_keys = format( + "{ target | cpu | Choose one of target computation devices: " + "cpu: CPU target (by default), " + "opencl: OpenCL, " + "opencl_fp16: OpenCL fp16 (half-float precision), " + "vpu: VPU, " + "vulkan: Vulkan, " + "cuda: CUDA, " + "cuda_fp16: CUDA fp16 (half-float preprocess) }"); + +static Mat postprocessOutput(const Mat &output, const Size &originalSize) +{ + Mat squeezed; + if (output.dims == 4 && output.size[0] == 1) + { + vector newShape = {output.size[1], output.size[2], output.size[3]}; + squeezed = output.reshape(0, newShape); + } + else + { + squeezed = output.clone(); + } + + Mat outputImage; + vector channels(3); + for (int i = 0; i < 3; i++) + { + channels[2-i] = Mat(squeezed.size[1], squeezed.size[2], CV_32F, + squeezed.ptr(i)); + } + merge(channels, outputImage); + + outputImage = max(0.0, min(1.0, outputImage)); + outputImage.convertTo(outputImage, CV_8UC3, 255.0); + + Size targetSize(originalSize.width * 4, originalSize.height * 4); + Mat result; + resize(outputImage, result, targetSize); + + return result; +} + +static Mat applySuperResolution(Net &net, const Mat &image, float scale, const Scalar &mean, bool swapRB, int width, int height) +{ + Mat blob = blobFromImage(image, scale, Size(width, height), mean, swapRB, false, CV_32F); + + net.setInput(blob); + Mat output; + net.forward(output); + + return postprocessOutput(output, Size(image.cols, image.rows)); +} + +static double calculateFontScale(const Mat &image) +{ + double baseScale = min(image.cols, image.rows) / 800.0; + return max(0.5, baseScale); +} + +static void processFrame(Net &net, Mat &frame, float scale, const Scalar &mean, bool swapRB, int width, int height) +{ + Mat result = applySuperResolution(net, frame, scale, mean, swapRB, width, height); + + double fontScale = calculateFontScale(frame); + int thickness = max(1, (int)(fontScale * 2)); + + putText(frame, "Original", Point(10, 30), + FONT_HERSHEY_SIMPLEX, fontScale, Scalar(0, 255, 0), thickness); + + double resultFontScale = calculateFontScale(result); + int resultThickness = max(1, (int)(resultFontScale * 2)); + + putText(result, "Super-Resolution 4x", Point(20, 50), + FONT_HERSHEY_SIMPLEX, resultFontScale, Scalar(0, 255, 0), resultThickness); + + imshow("Input", frame); + imshow("Super-Resolution", result); +} + +int main(int argc, char **argv) +{ + const string about = + "This sample demonstrates super-resolution using the SeeMore model.\n" + "The model upscales images by 4x while enhancing details.\n\n" + "Usage examples:\n" + "\t./super_resolution\n" + "\t./super_resolution --input=image.jpg\n" + "\t./super_resolution --input=../data/chicky_512.png\n"; + + string keys = param_keys + backend_keys + target_keys; + + CommandLineParser parser(argc, argv, keys); + if (parser.has("help")) + { + cout << about << endl; + parser.printMessage(); + return 0; + } + + string modelName = parser.get("@alias"); + string zooFile = samples::findFile(parser.get("zoo")); + + keys += genPreprocArguments(modelName, zooFile); + parser = CommandLineParser(argc, argv, keys); + + float scale = parser.get("scale"); + Scalar mean = parser.get("mean"); + bool swapRB = parser.get("rgb"); + String backend = parser.get("backend"); + String target = parser.get("target"); + String sha1 = parser.get("sha1"); + string model = findModel(parser.get("model"), sha1); + int width = parser.get("width"); + int height = parser.get("height"); + string inputPath = findFile(parser.get("input")); + + if (model.empty()) + { + cerr << "Model file not found" << endl; + return -1; + } + + Net net; + try + { + net = readNetFromONNX(model); + net.setPreferableBackend(getBackendID(backend)); + net.setPreferableTarget(getTargetID(target)); + } + catch (const Exception &e) + { + cerr << "Error loading model: " << e.what() << endl; + return -1; + } + + Mat testImage = imread(inputPath); + if (testImage.empty()) + { + cerr << "Cannot load image: " << inputPath << endl; + return -1; + } + + namedWindow("Input", WINDOW_NORMAL); + namedWindow("Super-Resolution", WINDOW_NORMAL); + moveWindow("Input", WINDOW_OFFSET_X, WINDOW_OFFSET_Y); + moveWindow("Super-Resolution", WINDOW_OFFSET_X + testImage.cols + WINDOW_SPACING, WINDOW_OFFSET_Y); + + processFrame(net, testImage, scale, mean, swapRB, width, height); + waitKey(0); + destroyAllWindows(); + + return 0; +} diff --git a/samples/dnn/super_resolution.py b/samples/dnn/super_resolution.py new file mode 100644 index 0000000000..3386dfeeb5 --- /dev/null +++ b/samples/dnn/super_resolution.py @@ -0,0 +1,175 @@ +""" +This file is part of OpenCV project. +It is subject to the license terms in the LICENSE file found in the top-level directory +of this distribution and at http://opencv.org/license.html. + +Copyright (C) 2025, Bigvision LLC. + + +This sample demonstrates super-resolution using the SeeMoreDetails model. +The model upscales images by 4x while enhancing details and reducing noise. +Supports image inputs only. + +SeeMoreDetails Repo: https://github.com/eduardzamfir/seemoredetails +""" + +import cv2 as cv +import argparse +import numpy as np +import os +from common import * + +def get_args_parser(func_args): + backends = ("default", "openvino", "opencv", "vkcom", "cuda") + targets = ( + "cpu", + "opencl", + "opencl_fp16", + "ncs2_vpu", + "hddl_vpu", + "vulkan", + "cuda", + "cuda_fp16", + ) + + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument( + "--zoo", + default=os.path.join(os.path.dirname(os.path.abspath(__file__)), "models.yml"), + help="An optional path to file with preprocessing parameters.", + ) + parser.add_argument( + "--input", help="Path to input image file.", default="chicky_512.png", required=False + ) + parser.add_argument( + "--backend", + default="default", + type=str, + choices=backends, + help="Choose one of computation backends: " + "default: automatically (by default), " + "openvino: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), " + "opencv: OpenCV implementation, " + "vkcom: VKCOM, " + "cuda: CUDA, " + "webnn: WebNN", + ) + parser.add_argument( + "--target", + default="cpu", + type=str, + choices=targets, + help="Choose one of target computation devices: " + "cpu: CPU target (by default), " + "opencl: OpenCL, " + "opencl_fp16: OpenCL fp16 (half-float precision), " + "ncs2_vpu: NCS2 VPU, " + "hddl_vpu: HDDL VPU, " + "vulkan: Vulkan, " + "cuda: CUDA, " + "cuda_fp16: CUDA fp16 (half-float preprocess)", + ) + + args, _ = parser.parse_known_args() + + model_name = "seemoredetails" + add_preproc_args(args.zoo, parser, "super_resolution", model_name) + + parser = argparse.ArgumentParser( + parents=[parser], + description=""" + To run: + Default image: + python super_resolution.py + Image processing: + python super_resolution.py --input=path/to/your/input/image.jpg + + The model performs 4x super-resolution on input images. + """, + formatter_class=argparse.RawTextHelpFormatter, + ) + return parser.parse_args(func_args) + +def load_model(args): + """Load the super-resolution model""" + try: + model_path = findModel(args.model, args.sha1) + net = cv.dnn.readNetFromONNX(model_path) + net.setPreferableBackend(get_backend_id(args.backend)) + net.setPreferableTarget(get_target_id(args.target)) + return net + except Exception as e: + print(f"Error loading model: {e}") + return None + +def postprocess_output(output, args, original_shape=None): + """Postprocess model output to displayable image""" + output = np.squeeze(output, axis=0) + output = np.clip(output, 0, 1) + output = np.transpose(output, (1, 2, 0)) + output = (output * 255).astype(np.uint8) + + output = cv.cvtColor(output, cv.COLOR_RGB2BGR) + + if original_shape is not None: + target_height, target_width = original_shape + upscaled_height, upscaled_width = target_height * 4, target_width * 4 + output = cv.resize(output, (upscaled_width, upscaled_height)) + + return output + +def apply_super_resolution(net, image, args): + """Apply super-resolution to a single image""" + original_shape = image.shape[:2] + + blob = cv.dnn.blobFromImage( + image, + scalefactor=args.scale, + size=(args.width, args.height), + mean=args.mean, + swapRB=args.rgb, + crop=False, + ) + + net.setInput(blob) + output = net.forward() + + result = postprocess_output(output, args, original_shape) + + t, _ = net.getPerfProfile() + label = "Inference time: %.2f ms" % (t * 1000.0 / cv.getTickFrequency()) + cv.putText(result, label, (10, 30), cv.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) + + return result + +def main(func_args=None): + args = get_args_parser(func_args) + + net = load_model(args) + if net is None: + print("Failed to load model.") + return -1 + + input_path = cv.samples.findFile(args.input) + image = cv.imread(input_path) + if image is None: + print(f"Cannot load image: {input_path}") + return -1 + + print(f"Processing image: {input_path}") + result = apply_super_resolution(net, image, args) + + cv.namedWindow("Input", cv.WINDOW_NORMAL) + cv.namedWindow("Super-Resolution Result", cv.WINDOW_NORMAL) + cv.imshow("Input", image) + cv.imshow("Super-Resolution Result", result) + print("Press 'q' to quit...") + while True: + key = cv.waitKey(0) & 0xFF + if key == ord("q"): + break + cv.destroyAllWindows() + return 0 + +if __name__ == "__main__": + main()