3

I am using C# and Magick.Net to annotate images, as follows:

var text = "Variable text";
var img = new MagickImage("image.jpg");
img.FontPointsize = 50;
img.FillColor = new MagickColor(Color.White);
img.Annotate(text, Gravity.Northwest);

Annotation works, however text is not always easily readable, as it can blend with the image.

The ImageMagick manual has a full section suggesting solutions for this:

  • Outlined Label

    convert dragon.gif -gravity south \
      -stroke '#000C' -strokewidth 2 -annotate 0 'Faerie Dragon' \
      -stroke  none   -fill white    -annotate 0 'Faerie Dragon' \
      anno_outline.jpg
    
  • Draw Dim Box

    convert dragon.gif \
      -fill '#0008' -draw 'rectangle 5,128,114,145' \
      -fill white   -annotate +10+141 'Faerie Dragon' \
      anno_dim_draw.jpg
    

(I'd use this method only if nothing else works, because it requires the rectangle width and height to be explicitly defined.)

  • Undercolor Box

    convert dragon.gif  -fill white  -undercolor '#00000080'  -gravity South \
      -annotate +0+5 ' Faerie Dragon '     anno_undercolor.jpg
    
  • Composited Label

    convert -background '#00000080' -fill white label:'Faerie Dragon' miff:- |\
          composite -gravity south -geometry +0+3 \
          -   dragon.gif   anno_composite.jpg
    
  • Auto-Sized Caption

    width=`identify -format %w dragon.gif`; \
    convert -background '#0008' -fill white -gravity center -size ${width}x30 \
      caption:"Faerie Dragons love hot apple pies\!" \
      dragon.gif +swap -gravity south -composite  anno_caption.jpg
    
  • Fancy Label

    convert -size 100x14 xc:none -gravity center \
      -stroke black -strokewidth 2 -annotate 0 'Faerie Dragon' \
      -background none -shadow 100x3+0+0 +repage \
      -stroke none -fill white     -annotate 0 'Faerie Dragon' \
      dragon.gif  +swap -gravity south -geometry +0-3 \
      -composite  anno_fancy.jpg
    

Any of the above approaches would be fine with me. However, I can't seem to find the required functionality exposed in the .Net API. For example, I tried setting BackgroundColor, before calling Annotate. This did not produce any effect:

img.BackgroundColor = new MagickColor(Color.Black);

I would like some pointers about how to implement any method that enhances readability of the annotation text, using Magick.Net.

dlemstra
  • 7,813
  • 2
  • 27
  • 43
user1942447
  • 103
  • 1
  • 7
  • Have you tried to also set the StrokeColor of the image? – dlemstra Sep 25 '14 at 18:41
  • That was a good tip, @dlemstra . `StrokeColor` added an outline around each letter, and this improves readability. For my current application, though, I prefer Composited Label as shown below. Also, thank you for creating Magick.Net. – user1942447 Sep 26 '14 at 04:23

3 Answers3

4

I ended up implementing the Composited Label option, as follows:

var text = "Variable text";
var img = new MagickImage("image.jpg");

using (var imgText = new MagickImage())
{
     imgText.FontPointsize = 50;
     imgText.BackgroundColor = new MagickColor(Color.Black);
     imgText.FillColor = new MagickColor(Color.White);
     imgText.Read("label:" + text);
     img.Composite(text, Gravity.Northwest);
}

The trick is to 'read' an image, but supply the label: notation in place of a filename. Once this is done, we can just combine the generated image with the original one.

Other than adding a black background to the text annotation, this code produces the same result as the one posted in the question.

user1942447
  • 103
  • 1
  • 7
1

For anyone else trying to figure out how to put text on images.

This is similar to the code I use to add text to images with Magick.NET.

Use this code as a LINQPad snippet. Use the Magick.NET-Q16-AnyCPU nuget package.

void Main()
{
    TextRender();
}

public void TextRender()
{
    //If you use a transparent color here, you won't see the text
    var imageBackgroundColor = new MagickColor("White");

    using (MagickImage image = new MagickImage(imageBackgroundColor, 400, 400))
    {
        var drawable = new DrawableText(0,10,"Line One\nLine Two\nLine Three");
        var gravity = new DrawableGravity(Gravity.North);
        var font = new DrawableFont("Arial");
        var antialias = new DrawableTextAntialias(true);
        var size = new DrawablePointSize(50);
        var color = new DrawableFillColor(Color.Black);
        //If needed
        //var strokeColor = new DrawableStrokeColor(Color.White);

        image.Annotate("Some annotation", Gravity.Center);

        image.Draw(drawable, gravity, font, antialias, size, color);//, strokeColor);

        var imageFileName = @"c:\temp\tempImage.jpg";

        //Write the file to disk
        image.Write(imageFileName);

        //For use in linqpad only
        //Load the written file from disk and show it in the Results window
        var image2 = (Bitmap) Image.FromFile(imageFileName, true);
        image2.Dump();
    }
}

This is the result:

enter image description here

hawkeyecoder
  • 571
  • 4
  • 10
  • Thanks for snippet! In case you don't want or can't use System.Drawing, you can replace : var size = new DrawableFontPointSize(12); and use MagickColors.Black instead of Color. – Nigrimmist Dec 29 '21 at 18:34
1

Just another one way, similar to @user1942447

string textToAdd = "ultra wide text with /r/n multiline row";

var readSettings = new MagickReadSettings
{
    Font = "Arial",
    TextGravity = Gravity.Center,
    BackgroundColor = MagickColors.Transparent,
    Height = 200, // height of text box
    Width = 300 // width of text box
};

using (var tImage = new MagickImage())
{
    using (var caption = new MagickImage($"label:{textToAdd}", readSettings))
    {
        //image is your main image where you need to put text
        image.Composite(caption, 10, 20, CompositeOperator.Over);    
        image.Write(AppContext.BaseDirectory + Guid.NewGuid() + ".png");
    }
}

Text will be re-sized automatically depending on text box size. In case you need strict font size, use FontPointsize property, but text may go out of borders.

Nigrimmist
  • 10,289
  • 4
  • 52
  • 53