mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge pull request #27592 from Naresh-19:dnn-sample-seemore-sr
Added image super-resolution samples using seemoredetails model #27592 Based on "See More Details: Efficient Image Super-Resolution by Experts Mining" (ICML 2024) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake This pull request adds a new sample named seemore_superres under samples/dnn/, implemented in both Python and C++. The sample demonstrates image super-resolution using the Seemoredetails model with OpenCV’s DNN module. ### Files Added: - samples/dnn/seemore_superres.cpp - samples/dnn/seemore_superres.py - Updated samples/dnn/models.yml ### Functionality: - Performs image upscaling(4x) using a specified Seemoredetails ONNX model. - Accepts image path and ONNX model path as command-line arguments. - Outputs the original and super-resolved images side by side for visual comparison. ### Sample Usage: *C++* ./seemore_superres --input=path/to/image.jpg ` *Python* python seemore_superres.py --input=path/to/image.jpg `
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 <opencv2/dnn.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#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<int> newShape = {output.size[1], output.size[2], output.size[3]};
|
||||
squeezed = output.reshape(0, newShape);
|
||||
}
|
||||
else
|
||||
{
|
||||
squeezed = output.clone();
|
||||
}
|
||||
|
||||
Mat outputImage;
|
||||
vector<Mat> channels(3);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
channels[2-i] = Mat(squeezed.size[1], squeezed.size[2], CV_32F,
|
||||
squeezed.ptr<float>(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<String>("@alias");
|
||||
string zooFile = samples::findFile(parser.get<String>("zoo"));
|
||||
|
||||
keys += genPreprocArguments(modelName, zooFile);
|
||||
parser = CommandLineParser(argc, argv, keys);
|
||||
|
||||
float scale = parser.get<float>("scale");
|
||||
Scalar mean = parser.get<Scalar>("mean");
|
||||
bool swapRB = parser.get<bool>("rgb");
|
||||
String backend = parser.get<String>("backend");
|
||||
String target = parser.get<String>("target");
|
||||
String sha1 = parser.get<String>("sha1");
|
||||
string model = findModel(parser.get<String>("model"), sha1);
|
||||
int width = parser.get<int>("width");
|
||||
int height = parser.get<int>("height");
|
||||
string inputPath = findFile(parser.get<String>("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;
|
||||
}
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user