Skip to content

StringToImageDemo

vtk-examples/Cxx/Rendering/StringToImageDemo


Description

This example demonstrates the use of vtkFreeTypeTools to populate an image with multiple strings converted into images. The final image is created using vtkImageBlend to blend each string image into the final image. If a string image does not fit in the final image or overlaps with an image in final, that sting image is skipped.

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

StringToImageDemo.cxx

#include <vtkCamera.h>
#include <vtkFreeTypeTools.h>
#include <vtkImageBlend.h>
#include <vtkImageCanvasSource2D.h>
#include <vtkImageData.h>
#include <vtkImageIterator.h>
#include <vtkImageViewer2.h>
#include <vtkMinimalStandardRandomSequence.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkStdString.h>
#include <vtkTextProperty.h>

#include <array>
// #include <random>

int main(int argc, char* argv[])
{
  vtkStdString text;
  if (argc < 2)
  {
    text = "Old Guys Rule";
  }
  else
  {
    text = argv[1];
  }

  // Create a blank, black image
  vtkNew<vtkImageCanvasSource2D> drawing;
  drawing->SetScalarTypeToUnsignedChar();
  drawing->SetNumberOfScalarComponents(3);
  drawing->SetExtent(0, 640, 0, 480, 0, 0);
  drawing->SetDrawColor(0, 0, 0);
  drawing->FillBox(0, 629, 0, 479);

  // Create an image that will hold the final image
  vtkNew<vtkImageBlend> final;
  final->AddInputConnection(drawing->GetOutputPort());
  final->SetOpacity(0, 1.0);
  final->Update();

  // Create an image of the string
  int dpi = 150;
  vtkFreeTypeTools* freeType = vtkFreeTypeTools::GetInstance();
  freeType->ScaleToPowerTwoOff();

  // Setup a property for the strings containing fixed parameters
  vtkNew<vtkNamedColors> colors;
  vtkNew<vtkTextProperty> textProperty;
  textProperty->SetColor(colors->GetColor3d("Tomato").GetData());
  textProperty->SetVerticalJustificationToCentered();
  textProperty->SetJustificationToCentered();

  // Distributions for random parameters
  // std::mt19937 mt(4355412); // Standard mersenne_twister_engine
  // std::uniform_int_distribution<> extentXDist(0, 640);
  // std::uniform_int_distribution<> extentYDist(0, 480);
  // std::uniform_int_distribution<> fontDist(6, 42);
  // std::uniform_real_distribution<double> orientationDist(-90, 90);

  vtkNew<vtkMinimalStandardRandomSequence> randomSequence;
  randomSequence->SetSeed(8775070);

  // For each string, create an image and see if it overlaps with other images,
  // if so, skip it
  int tried = 600;
  int accepted = 0;
  for (int i = 0; i < tried; ++i)
  {
    // int fontSize = fontDist(mt);
    int fontSize = static_cast<int>(randomSequence->GetRangeValue(6, 42));
    randomSequence->Next();
    // textProperty->SetOrientation(orientationDist(mt));
    textProperty->SetOrientation(randomSequence->GetRangeValue(-90, 90));
    randomSequence->Next();
    textProperty->SetFontSize(fontSize);

    vtkNew<vtkImageData> textImage;
    freeType->RenderString(textProperty, text, dpi, textImage.GetPointer());

    // Set the extent of the text image
    std::array<int, 4> bb;
    freeType->GetBoundingBox(textProperty, text, dpi, bb.data());
    // int offsetX = extentXDist(mt);
    // int offsetY = extentYDist(mt);
    int offsetX = static_cast<int>(randomSequence->GetRangeValue(0, 640));
    randomSequence->Next();
    int offsetY = static_cast<int>(randomSequence->GetRangeValue(0, 480));
    randomSequence->Next();
    // Make sure the text image will fit on the final image
    if (offsetX + bb[1] - bb[0] < 639 && offsetY + bb[3] - bb[2] < 479)
    {
      textImage->SetExtent(offsetX, offsetX + bb[1] - bb[0], offsetY,
                           offsetY + bb[3] - bb[2], 0, 0);
      vtkNew<vtkImageData> image;
      final->Update();

      // Does the text image overlap with images on the final image
      vtkImageIterator<unsigned char> finalIt(final->GetOutput(),
                                              textImage->GetExtent());
      unsigned char R;
      bool good = true;
      while (!finalIt.IsAtEnd())
      {
        auto finalSpan = finalIt.BeginSpan();
        while (finalSpan != finalIt.EndSpan())
        {
          R = *finalSpan++;
          finalSpan++;
          finalSpan++;
          if (R != 0)
          {
            good = false;
            break;
          }
        }
        if (!good)
        {
          break;
        }
        finalIt.NextSpan();
      }
      if (good)
      {
        accepted++;
        image->DeepCopy(textImage);
        final->AddInputData(image);
        final->SetOpacity(i + 1, 1.0); // text: 100% opaque
        final->Update();
      }
    }
  }
  std::cout << "Tried " << tried << ", but only accepted " << accepted
            << std::endl;
  // Display the result
  vtkNew<vtkRenderWindowInteractor> interactor;

  vtkNew<vtkImageViewer2> imageViewer;
  imageViewer->SetInputData(final->GetOutput());
  imageViewer->SetSize(640, 512);
  imageViewer->SetupInteractor(interactor);
  imageViewer->GetRenderer()->SetBackground(
      colors->GetColor3d("Wheat").GetData());
  imageViewer->GetRenderer()->ResetCamera();
  imageViewer->GetRenderWindow()->SetWindowName("StringToImageDemo");

  auto camera = imageViewer->GetRenderer()->GetActiveCamera();
  camera->ParallelProjectionOn();
  camera->SetParallelScale(640 * 0.4);
  imageViewer->GetRenderWindow()->Render();
  interactor->Initialize();
  interactor->Start();

  return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)

project(StringToImageDemo)

find_package(VTK COMPONENTS 
  CommonColor
  CommonCore
  CommonDataModel
  ImagingCore
  ImagingSources
  InteractionImage
  InteractionStyle
  RenderingContextOpenGL2
  RenderingCore
  RenderingFreeType
  RenderingGL2PSOpenGL2
  RenderingOpenGL2
)

if (NOT VTK_FOUND)
  message(FATAL_ERROR "StringToImageDemo: Unable to find the VTK build folder.")
endif()

# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
add_executable(StringToImageDemo MACOSX_BUNDLE StringToImageDemo.cxx )
  target_link_libraries(StringToImageDemo PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
  TARGETS StringToImageDemo
  MODULES ${VTK_LIBRARIES}
)

Download and Build StringToImageDemo

Click here to download StringToImageDemo and its CMakeLists.txt file. Once the tarball StringToImageDemo.tar has been downloaded and extracted,

cd StringToImageDemo/build

If VTK is installed:

cmake ..

If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:

cmake -DVTK_DIR:PATH=/home/me/vtk_build ..

Build the project:

make

and run it:

./StringToImageDemo

WINDOWS USERS

Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.