No Matches

Prev Tutorial: Shader-Demo
Next Tutorial: Pedestrian-Demo

Original author Amir Hassan (kallaballa) amir@.nosp@m.viel.nosp@m.-zu.o.nosp@m.rg
Compatibility OpenCV >= 4.7

Renders a Star Wars like text crawl using nanovg (OpenGL) and uses OpenCV for a pseudo 3D effect.

Font Demo
// 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 Amir Hassan (kallaballa) <amir@viel-zu.org>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
#include <limits>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
/* Demo parameters */
#ifndef __EMSCRIPTEN__
constexpr long unsigned int WIDTH = 1280;
constexpr long unsigned int HEIGHT = 720;
constexpr long unsigned int WIDTH = 960;
constexpr long unsigned int HEIGHT = 960;
constexpr bool OFFSCREEN = false;
#ifndef __EMSCRIPTEN__
constexpr const char* OUTPUT_FILENAME = "font-demo.mkv";
constexpr double FPS = 60;
using std::cerr;
using std::endl;
using std::string;
using std::vector;
using std::istringstream;
using namespace cv::v4d;
class FontDemoPlan : public Plan {
struct Params {
const cv::Scalar_<float> INITIAL_COLOR = cv::v4d::colorConvert(cv::Scalar(0.15 * 180.0, 128, 255, 255), cv::COLOR_HLS2RGB);
float minStarSize_ = 0.5f;
float maxStarSize_ = 1.5f;
int minStarCount_ = 1000;
int maxStarCount_ = 3000;
float starAlpha_ = 0.3f;
float fontSize_ = 40.0f;
cv::Scalar_<float> textColor_ = INITIAL_COLOR / 255.0;
float warpRatio_ = 1.0f/3.0f;
bool updateStars_ = true;
bool updatePerspective_ = true;
} params_;
//the text to display
vector<string> lines_;
cv::UMat stars_, warped_, frame_;
//transformation matrix
cv::Mat tm_;
//line count
uint32_t cnt_ = 0;
//Total number of lines in the text
int32_t numLines_;
//Height of the text in pixels
int32_t textHeight_;
//y-value of the current line
int32_t y_ = 0;
int32_t translateY_;
void gui(cv::Ptr<V4D> window) override {
window->imgui([](cv::Ptr<V4D> win, ImGuiContext* ctx, Params& params){
using namespace ImGui;
Text("Text Crawl");
SliderFloat("Font Size", &params.fontSize_, 1.0f, 100.0f);
if(SliderFloat("Warp Ratio", &params.warpRatio_, 0.1f, 1.0f))
params.updatePerspective_ = true;
ColorPicker4("Text Color", params.textColor_.val);
if(SliderFloat("Min Star Size", &params.minStarSize_, 0.5f, 1.0f))
params.updateStars_ = true;
if(SliderFloat("Max Star Size", &params.maxStarSize_, 1.0f, 10.0f))
params.updateStars_ = true;
if(SliderInt("Min Star Count", &params.minStarCount_, 1, 1000))
params.updateStars_ = true;
if(SliderInt("Max Star Count", &params.maxStarCount_, 1000, 5000))
params.updateStars_ = true;
if(SliderFloat("Min Star Alpha", &params.starAlpha_, 0.2f, 1.0f))
params.updateStars_ = true;
}, params_);
void setup(cv::Ptr<V4D> window) override {
//The text to display
string txt = cv::getBuildInformation();
//Save the text to a vector
std::istringstream iss(txt);
for (std::string line; std::getline(iss, line); ) {
numLines_ = lines_.size();
textHeight_ = (numLines_ * params_.fontSize_);
void infer(cv::Ptr<V4D> window) override {
auto always = []() { return true; };
auto isTrue = [](const bool& b) { return b; };
window->branch(isTrue, params_.updateStars_);
window->nvg([](const cv::Size& sz, cv::RNG& rng, const Params& params) {
using namespace cv::v4d::nvg;
//draw stars
int numStars = rng.uniform(params.minStarCount_, params.maxStarCount_);
for(int i = 0; i < numStars; ++i) {
const auto& size = rng.uniform(params.minStarSize_, params.maxStarSize_);
strokeColor(cv::Scalar(255, 255, 255, params.starAlpha_ * 255.0f));
circle(rng.uniform(0, sz.width) , rng.uniform(0, sz.height), size / 2.0);
}, window->fbSize(), rng_, params_);
window->fb([](const cv::UMat& frameBuffer, cv::UMat& f){
}, stars_);
window->endbranch(isTrue, params_.updateStars_);
window->branch(isTrue, params_.updatePerspective_);
window->parallel([](cv::Mat& tm, const Params& params){
//Derive the transformation matrix tm for the pseudo 3D effect from quad1 and quad2.
vector<cv::Point2f> quad1 = {{0,0},{WIDTH,0},{WIDTH,HEIGHT},{0,HEIGHT}};
float l = (WIDTH - (WIDTH * params.warpRatio_)) / 2.0;
float r = WIDTH - l;
vector<cv::Point2f> quad2 = {{l, 0.0f},{r, 0.0f},{WIDTH,HEIGHT},{0,HEIGHT}};
tm = cv::getPerspectiveTransform(quad1, quad2);
}, tm_, params_);
window->endbranch(isTrue, params_.updatePerspective_);
window->nvg([](const cv::Size& sz, int32_t& ty, const int32_t& cnt, int32_t& y, const int32_t& textHeight, const std::vector<std::string> lines, const Params& params) {
//How many pixels to translate the text up.
ty = HEIGHT - cnt;
using namespace cv::v4d::nvg;
fillColor(params.textColor_ * 255);
translate(0, ty);
for (size_t i = 0; i < lines.size(); ++i) {
y = (i * params.fontSize_);
if (y + ty < textHeight && y + ty + params.fontSize_ > 0) {
text(sz.width / 2.0, y, lines[i].c_str(), lines[i].c_str() + lines[i].size());
}, window->fbSize(), translateY_, cnt_, y_, textHeight_, lines_, params_);
window->fb([](cv::UMat& framebuffer, cv::UMat& w, cv::UMat& s, cv::Mat& t) {
//Pseudo 3D text effect.
//Combine layers
cv::add(s, w, framebuffer);
}, warped_, stars_, tm_);
window->parallel([](const int32_t& translateY, const int32_t& textHeight, uint32_t& cnt, Params& params) {
params.updatePerspective_ = false;
params.updateStars_ = false;
if(-translateY > textHeight) {
//reset the scroll once the text is out of the picture
cnt = 0;
//Wrap the cnt around if it becomes to big.
if(cnt > std::numeric_limits<uint32_t>().max() / 2.0)
cnt = 0;
}, translateY_, textHeight_, cnt_, params_);
int main() {
try {
cv::Ptr<V4D> window = V4D::make(WIDTH, HEIGHT, "Font Demo", ALL, OFFSCREEN);
#ifndef __EMSCRIPTEN__
auto sink = makeWriterSink(window, OUTPUT_FILENAME, FPS, cv::Size(WIDTH, HEIGHT));
} catch(std::exception& ex) {
cerr << "Exception: " << ex.what() << endl;
return 0;
n-dimensional dense array class
Definition: mat.hpp:811
Random Number Generator.
Definition: core.hpp:2864
int uniform(int a, int b)
returns uniformly distributed integer random number from [a,b) range
Template class for a 4-element vector derived from Vec.
Definition: types.hpp:670
Template class for specifying the size of an image or rectangle.
Definition: types.hpp:335
_Tp height
the height
Definition: types.hpp:363
_Tp width
the width
Definition: types.hpp:362
Definition: mat.hpp:2432
MatSize size
dimensional size of the matrix; accessible in various formats
Definition: mat.hpp:2643
void copyTo(OutputArray m) const
copies the matrix content to "m".
Definition: v4d.hpp:68
void add(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray(), int dtype=-1)
Calculates the per-element sum of two arrays or an array and a scalar.
void max(InputArray src1, InputArray src2, OutputArray dst)
Calculates per-element maximum of two arrays or an array and a scalar.
iiiiii|abcdefgh|iiiiiii with some specified i
Definition: base.hpp:269
std::shared_ptr< _Tp > Ptr
Definition: cvstd_wrapper.hpp:23
const String & getBuildInformation()
Returns full configuration time cmake output.
int64 getTickCount()
Returns the number of ticks.
@ circle
Definition: gr_skig.hpp:62
Definition: imgproc.hpp:617
Mat getPerspectiveTransform(InputArray src, InputArray dst, int solveMethod=DECOMP_LU)
Calculates a perspective transform from four pairs of the corresponding points.
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar &borderValue=Scalar())
Applies a perspective transformation to an image.
Definition: imgproc.hpp:253
PyParams params(const std::string &tag, const std::string &model, const std::string &weights, const std::string &device)
GOpaque< Size > size(const GMat &src)
Gets dimensions from Mat.
Net::Result infer(cv::GOpaque< cv::Rect > roi, T in)
Calculates response for the specified network (template parameter) for the specified region in the so...
Definition: infer.hpp:474
Definition: nvg.hpp:20
void textAlign(int align)
void translate(float x, float y)
void beginPath()
void fontSize(float size)
void fontFace(const char *font)
void strokeColor(const cv::Scalar &bgra)
void fillColor(const cv::Scalar &color)
void clear(const cv::Scalar &bgra=cv::Scalar(0, 0, 0, 255))
float text(float x, float y, const char *string, const char *end)
void strokeWidth(float size)
Definition: backend.hpp:15
cv::Ptr< Sink > makeWriterSink(cv::Ptr< V4D > window, const string &outputFilename, const float fps, const cv::Size &frameSize)
cv::Scalar colorConvert(const cv::Scalar &src, cv::ColorConversionCodes code)
::uint32_t uint32_t
Definition: cvdef.h:849
::int32_t int32_t
Definition: cvdef.h:848