How to add tiling / repeat watermark with ImageSharp? - c#

I want add tiling/repeat watermark to image by using ImageSharp 1.0.0-beta7.
I need figure out how many watermarks and how many points where watermark display. Then use DrawText function to draw one by one.
Is there any extension or library to fill tiling / repeat watermark once with ImageSharp ?

Here's the solution using ImageSharp 2.1, ImageSharp.Drawing 1.0-beta14, and SixLabors.Fonts 1.0-beta16
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Processing;
using Color = SixLabors.ImageSharp.Color;
using PointF = SixLabors.ImageSharp.PointF;
//enumerate all jpeg files
var files = Directory.EnumerateFiles(Environment.CurrentDirectory).Where(x => x.EndsWith(".jpg") || x.EndsWith(".jpeg"));
//make a watermark of the filename on the files
foreach (var file in files)
{
if (file.Contains(" - Watermark"))
{
continue;
}
Console.WriteLine($"Working on {file}");
var original = Path.GetFileNameWithoutExtension(file);
var directory = Path.GetDirectoryName(file);
var extension = Path.GetExtension(file);
var filename = Path.GetFileNameWithoutExtension(file);
filename += " - Watermark";
//load file with imagesharp
using (var image = Image.Load(file))
{
var fontColl = new FontCollection().AddSystemFonts();
FontFamily fontFamily1 = fontColl.Families.FirstOrDefault(x => x.Name == "Arial");
var font = new Font(fontFamily1, 12, FontStyle.Bold);
TextOptions textoptions = new(font);
var drawingoptions = new DrawingOptions();
drawingoptions.GraphicsOptions.BlendPercentage = 0.5f;
//find out how big the text is
var textSize = TextMeasurer.Measure(original, textoptions);
var pointf = new PointF(0, 0);
int i = 0;
//loop over the height of the image
for (float y = 0; y < image.Height; y += (int)textSize.Height)
{
//loop over the width of the image
for (float x = 0; x <= image.Width; x += (int)textSize.Width)
{
//draw the text on the image
pointf = new PointF(x, y);
//alternate the start of the odd rows to offset the text
if (i % 2 == 0)
pointf.X -= textSize.Width / 2;
image.Mutate(x => x.DrawText(drawingoptions, original, font, Color.White, pointf));
}
i++;
}
//save image
image.Save($"{directory}\\{filename}{extension}");
}
}
Console.ReadLine();

Related

out of memory error for image watermark in ASP.NET using C#

I insert a watermark for the photos on my site and my photos are in A4 resolution and I don't want reduce the resolution of my photos.
but when I try to watermark a photo, I get an "out of memory" error.
My watermark codes are like this. Please help if you can.
The error appears on the third line of the (using (Graphics imageGraphics = Graphics.FromImage(image))) block of code:
using (Image image = Image.FromFile(HttpContext.Current.Server.MapPath(RawImage)))
using (Image watermarkImage = Image.FromFile(HttpContext.Current.Server.MapPath(qrCode)))
using (Graphics imageGraphics = Graphics.FromImage(image))
using (TextureBrush watermarkBrush = new TextureBrush(watermarkImage))
{
int x = 0;
int y = 0;
x = ((image.Width - 75) - (watermarkImage.Width));
y = ((image.Height - 75) - (watermarkImage.Height));
watermarkBrush.TranslateTransform(x, y);
imageGraphics.FillRectangle(watermarkBrush, new Rectangle(new Point(x, y), new Size(watermarkImage.Width + 1,watermarkImage.Height)));
Random rnd = new Random();
string datetime = DateTime.Now.ToShortDateString().Replace("/", "") + "_" + DateTime.Now.ToShortTimeString().Replace(":", "").Replace(" ", "").Replace("AM", "").Replace("PM", "").Replace("ب.ظ", "").Replace("ق.ظ", "");
image.Save(HttpContext.Current.Server.MapPath(finalLink.Replace("https://savisgroups.com","~")));
return (finalLink);
}

CvInvoke.Undistort Is making the image more distorted, not less

My first time working with OpenCV. The code below uses the Emgu package, but I've also tried it using OpenCVSharp and have got exactly the same results.
I am calibrating the camera using 6 chessboard photos, then using the calibration to Undistort an image. The test image is a photo of some squared paper. The camera really isn't very distorted - I can't tell that it is distorted by eye, only when I draw a straight line over the lines in a paint program.
However - the resulting image comes out way more distorted than the original - adding a lot of barrel distortion.
When I run the commented out lines, the "DrawChessboardCorners" show that it is identifying the points perfectly.
What am I doing wrong here?
static void Main(string[] args)
{
Size patternSize = new Size(9, 6);
List<VectorOfPointF> ListOfCornerPoints = new List<VectorOfPointF>();
DirectoryInfo dir = new DirectoryInfo(#"Z:\Simon\Dropbox\Apps\OpenCVPlay\Image Processing\Chessboard Pattern Photos With Pixel");
Size calibrationImageSize = new Size(0,0) ;
foreach (var file in dir.GetFiles())
{
VectorOfPointF corners = new VectorOfPointF();
var calibrationImage = new Image<Rgb, byte>(file.FullName);
bool find = CvInvoke.FindChessboardCorners(calibrationImage, patternSize, corners, CalibCbType.AdaptiveThresh | CalibCbType.FilterQuads);
//CvInvoke.DrawChessboardCorners(calibrationImage, patternSize, corners, find);
//calibrationImage.Save(#"Z:\Simon\Dropbox\Apps\OpenCVPlay\Image Processing\Chessboard Pattern Photos With Pixel\test\" + file.Name);
ListOfCornerPoints.Add(corners);
calibrationImageSize = calibrationImage.Size;
}
PointF[][] arrayOfCornerPoints = ListOfCornerPoints.Select(a => a.ToArray()).ToArray();
var modelPoints = CreateModelPoints(ListOfCornerPoints.Count, patternSize.Width, patternSize.Height);
var arrayOfModelPoints = modelPoints.Select(a => a.ToArray()).ToArray();
Matrix<double> cameraDistortionCoeffs = new Matrix<double>(5, 1);
Matrix<double> cameraMatrix = new Matrix<double>(3, 3);
Mat[] rotationVectors;
Mat[] translationVectors;
CvInvoke.CalibrateCamera(
arrayOfModelPoints,
arrayOfCornerPoints,
calibrationImageSize,
cameraMatrix,
cameraDistortionCoeffs,
CalibType.Default,
new MCvTermCriteria(),
out rotationVectors,
out translationVectors);
var imageToProcess = new Image<Rgb, byte>(#"Z:\Simon\Dropbox\Apps\OpenCVPlay\Image Processing\Test data\squarePaper.jpg");
Rectangle Rect2 = new Rectangle();
var newCameraMatrix = CvInvoke.GetOptimalNewCameraMatrix(cameraMatrix, cameraDistortionCoeffs, calibrationImageSize, 1, imageToProcess.Size, ref Rect2, true);
Image<Rgb, byte> processedImage = imageToProcess.Clone();
CvInvoke.Undistort(imageToProcess, processedImage, cameraMatrix, cameraDistortionCoeffs);
processedImage.Save(#"Z:\Simon\Dropbox\Apps\OpenCVPlay\Image Processing\Test data\squarePaperFixed.jpg");
}
static List<List<MCvPoint3D32f>> CreateModelPoints(int length, int chessboardCols, int chessboardRows)
{
var modelPoints = new List<List<MCvPoint3D32f>>();
for (var k = 0; k < length; k++)
{
var chessboard = new List<MCvPoint3D32f>();
for (var y = 0; y < chessboardRows; y++)
{
for (var x = 0; x < chessboardCols; x++)
{
chessboard.Add(new MCvPoint3D32f(x, y, 0));
}
}
modelPoints.Add(chessboard);
}
return modelPoints;
}

Totally stumpted - System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'

I've written a quick winform app that can take some entered text, generate images based on all the system fonts and then consolidate those images into a single image to give examples of the fonts. For ease, I've separated the two functions, one to generate the images and another to consolidate them so I can remove fonts I don't want in the single image. It was all working yesterday, but now when it comes to saving the consolidated image ("complete.save("consolidated.png");) it gives the useless GDI+ error. I've checked paths and access, all are fine and correct. Nothing is locking the image, so I'm totally at a loss as what is causing this. Any ideas? Code below
StringBuilder sb = new StringBuilder();
List<string> files = FileSystemUtilities.ListFiles("fonts");
int height = 0;
int width = 0;
Bitmap test = new Bitmap(1000, 1000);
Graphics gTest = Graphics.FromImage(test);
Font font = new Font("Arial", 128);
int numWidth = 0;
int count = 1;
foreach (var file in files)
{
sb.AppendFormat("{0}\r", FileSystemUtilities.GetFileName(file).Replace(".png", string.Empty));
Bitmap bitmap = new Bitmap(file);
height = height + bitmap.Height + 10;
if (width < bitmap.Width)
{
width = bitmap.Width;
}
SizeF numSize = gTest.MeasureString(Convert.ToString(count), font);
if (numWidth < numSize.Width)
{
numWidth = Convert.ToInt32(numSize.Width + 1);
}
bitmap.Dispose();
count++;
}
test.Dispose();
gTest.Dispose();
numWidth = numWidth + 10;
count = 1;
Bitmap complete = new Bitmap(width + numWidth, height);
Graphics g = Graphics.FromImage(complete);
g.FillRectangle(Brushes.White, 0, 0, complete.Width, complete.Height);
int y = 0;
foreach (var file in files)
{
Bitmap bitmap = new Bitmap(file);
g.DrawString(Convert.ToString(count) + ".", font, Brushes.Black, 0, y);
g.DrawImage(bitmap, numWidth, y);
y = y + bitmap.Height + 10;
bitmap.Dispose();
count++;
}
string filename = "consolidated.png";
if (File.Exists(filename))
{
File.Delete(filename);
}
g.Dispose();
complete.Save("consolidated.png");
complete.Dispose();
TextFileUtilities.WriteTextFile("consolidated.txt", sb.ToString());
Turns out it was due to excessive height... I couldn't see it. Thank you for all that commented and helped me out.

Creating a Mosaic out of multiple images increases the luminance of the final image

I am trying to create a collage of images with Magick.net. I am using MagickImageCollection and .Mosaic(). I tried already a few of the functions provided by MagickImageCollection but all of them increase the brightness of the final image. The only one that worked so far was .Montage(), but with .Montage() I don't get the padding right.
How do I need to configure it, that .Mosaic() keeps the colors as they are in the single images?
using (var collection = new MagickImageCollection())
{
for (var i = 0; i < thumbnailCount; i++)
{
var image = new MagickImage(TempThumbPathFor(i));
image.Resize(256, 0);
var posX = (image.Page.Width + margin) * (i % 2);
var posY = (image.Page.Height + margin) * (i / 2);
image.Page = new MagickGeometry(posX, posY, new Percentage(100), new Percentage(100));
collection.Add(image);
}
using (var result = collection.Mosaic())
{
result.Write(newPath);
}
}
Collage of images with washed out colors:
For more information why the problem occurred in the first place have a look at this issue: GitHub
Figured out how to create a montage with padding and proper color. Couldn't get it to work with .Mosaic but with .Montage().
The important part is to add the margin to X, Y, Height and Width and call .Trim() on the final image. You will most likely have to play around a bit with the margin to get a balanced looking padding between the images, but other than that it works quite well.
const int margin = 2;
MagickGeometry geometry = null;
using (var collection = new MagickImageCollection())
{
for (var i = 0; i < thumbnailCount; i++)
{
var image = new MagickImage(TempThumbPathFor(i));
image.Resize(256, 0);
collection.Add(image);
if (i == 0)
{
geometry = image.BoundingBox;
geometry.X += margin;
geometry.Width += margin;
geometry.Y += margin;
geometry.Height += margin - 1;
}
}
using (var result = collection.Montage(new MontageSettings()
{
Geometry = geometry,
BackgroundColor = MagickColor.FromRgb(255, 255, 255)
}))
{
result.Trim();
result.Write(newPath);
}
}

Use StorageFile to get Bitmap Dimensions before using it to read image?

Okay, I'm trying to do some work analyzing heuristics of an image in a WPF app. When the user chooses the image file location, I want to open the image in the codebehind, check the colors of the image, and output them in an application. However to properly check the pixels of the image I need to acquire the dimensions to load it to a WriteableBitmap that can get me the pixel colors.
When I open a stream to the file a second time the app just hangs. Here's the code:
private static async Task<Dictionary<Color, int>> GetColorCountsAsync(string path)
{
var colorCounts = new Dictionary<Color, int>();
var absolutePath = string.Format(#"{0}\{1}",Directory.GetCurrentDirectory(),path);
var dimension = await GetBitmapDimensions(absolutePath); //Method below - opens via StorageFile
var file = await StorageFile.GetFileFromPathAsync(absolutePath); //Hangs forever
using (var stream = await file.OpenStreamForReadAsync().ConfigureAwait(false))
{
var pixelWidth = dimension.Width;
var pixelHeight = dimension.Height;
var bitmap = new WriteableBitmap(pixelWidth, pixelHeight);
bitmap.SetSource(stream.AsRandomAccessStream());
using (var buffer = bitmap.PixelBuffer.AsStream())
{
var pixels = new byte[4 * pixelWidth * pixelHeight];
buffer.Read(pixels, 0, pixels.Length);
for (var y = 0; y < pixelHeight; y++)
{
for (var x = 0; x < pixelWidth; x++)
{
var index = ((y * pixelWidth) + x) * 4;
var alpha = pixels[index + 4];
var red = pixels[index + 2];
var green = pixels[index + 1];
var blue = pixels[index + 0];
var color = Color.FromArgb(alpha, red, green, blue);
if (!colorCounts.ContainsKey(color))
{
colorCounts.Add(color, 0);
}
colorCounts[color] = colorCounts[color] + 1;
}
}
}
}
return colorCounts;
}
private static async Task<Dimension> GetBitmapDimensions(string absolutePath)
{
var file = await StorageFile.GetFileFromPathAsync(absolutePath);
var bitmapImage = new BitmapImage();
using (var fileStream = await file.OpenAsync(FileAccessMode.Read))
{
bitmapImage.SetSource(fileStream);
}
return new Dimension { Height = bitmapImage.PixelHeight, Width = bitmapImage.PixelWidth };
}
I can't close the bitmap image nor dispose it - what's causing the app to freeze?
You don't need the bitmap in the correct dimensions, as SetStream() is an extension method. It just needs an object first, call it like this:
var bitmap = new WriteableBitmap(1,1).SetSource(stream.AsRandomAccessStream());

Categories

Resources