Use SharpDX to capture screenshot of rotated monitor - c#

The code below successfully takes screenshots of the monitors attached to my Windows 10 laptop computer, so long as the monitors are not "flipped". When the monitors are flipped to any orientation except "landscape" the captured images are all black pixels (r,g,b,a = 0,0,0,255)
How can I modify the code below so that it will also work with flipped monitors?
Target Framework is:
Net 4.8
Referenced packages are:
SharpDX 4.2.0
SharpDX.Direct2D1 4.2.0
SharpDX.Direct3D11 4.2.0
SharpDX.DXGI 4.2.0
SharpDX.DXGI 4.2.0
using System;
using System.IO;
using System.Runtime.ExceptionServices;
using SharpDX;
namespace ScreenCast {
internal static class Program {
[STAThread]
private static void Main() {
var captureCount = 0;
using var factory = new SharpDX.DXGI.Factory4();
foreach (var adapter in factory.Adapters1) {
using var device = new SharpDX.Direct3D11.Device(adapter);
foreach (var output in adapter.Outputs) {
if (output.Description.IsAttachedToDesktop) {
var description = output.Description;
using var output1 = output.QueryInterface<SharpDX.DXGI.Output1>();
Capture($"{captureCount++}.bmp", device, output1);
}
output.Dispose();
}
adapter.Dispose();
}
}
private static void Capture(string outputFileName, SharpDX.Direct3D11.Device device, SharpDX.DXGI.Output1 output1) {
int width = output1.Description.DesktopBounds.Right - output1.Description.DesktopBounds.Left;
int height = output1.Description.DesktopBounds.Bottom - output1.Description.DesktopBounds.Top;
using var stagingScreenTexture = new SharpDX.Direct3D11.Texture2D(device, new SharpDX.Direct3D11.Texture2DDescription {
Width = width,
Height = height,
CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.Read,
BindFlags = SharpDX.Direct3D11.BindFlags.None,
Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm,
OptionFlags = SharpDX.Direct3D11.ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1,
SampleDescription = { Count = 1, Quality = 0 },
Usage = SharpDX.Direct3D11.ResourceUsage.Staging
});
using var duplicatedOutput = output1.DuplicateOutput(device);
SharpDX.DXGI.Resource screenResource = null;
SharpDX.DXGI.OutputDuplicateFrameInformation duplicateFrameInformation;
AcquireFrame(duplicatedOutput, out duplicateFrameInformation, out screenResource);
duplicatedOutput.ReleaseFrame();
AcquireFrame(duplicatedOutput, out duplicateFrameInformation, out screenResource);
// copy resource into memory that can be accessed by the CPU
using var screenTexture = screenResource.QueryInterface<SharpDX.Direct3D11.Texture2D>();
device.ImmediateContext.CopyResource(screenTexture, stagingScreenTexture);
// Get the desktop capture texture
var mapSource = device.ImmediateContext.MapSubresource(stagingScreenTexture, 0, SharpDX.Direct3D11.MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
using var bmp = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
var bmpBounds = new System.Drawing.Rectangle(0, 0, width, height);
var bmpData = bmp.LockBits(bmpBounds, System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat);
var src = mapSource.DataPointer;
var dest = bmpData.Scan0;
for (var y = 0; y < height; y++) {
SharpDX.Utilities.CopyMemory(dest, src, width * 4);
src += mapSource.RowPitch;
dest += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
bmp.Save(outputFileName);
device.ImmediateContext.UnmapSubresource(stagingScreenTexture, 0);
screenResource.Dispose();
duplicatedOutput.ReleaseFrame();
// Display the texture using system associated viewer
System.Diagnostics.Process.Start(Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, outputFileName)));
}
static void AcquireFrame(SharpDX.DXGI.OutputDuplication duplication, out SharpDX.DXGI.OutputDuplicateFrameInformation info, out SharpDX.DXGI.Resource resource) {
while (true) {
try {
duplication.AcquireNextFrame(100, out info, out resource);
return;
} catch (SharpDXException x) {
if (x.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
ExceptionDispatchInfo.Capture(x).Throw();
}
}
}
}
}

First, I only used SharpDX for a few days, so I'm by no means an expert, but I ran into a similar problem when capturing from rotated monitor and from what I've been able to deduce captured frame is not rotated.
e.g. Your monitor is rotated 90 deg to portrait (Width x Height 1080x1920) so you'd expect the captured frame to be portrait as well, right? Nope, you get the 1920 x 1080 landscape bitmap so your screen width= bitmap height and vice versa:
Here is the code I used in my capture class, still work in progress:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using Device = SharpDX.Direct3D11.Device;
using MapFlags = SharpDX.Direct3D11.MapFlags;
namespace EXM.ExampleCapture
{
public class DXScreenCaptureUtil {
private static ImageCodecInfo jpegCodec = ImageCodecInfo.GetImageEncoders()
.First(c => c.FormatID == ImageFormat.Jpeg.Guid);
private static EncoderParameters jpegParams = new EncoderParameters() { Param = new[] { new EncoderParameter(Encoder.Quality, 60L) } };
//Cache objects
private static Factory1 factory = new Factory1();
private static Adapter adapter;
private static Device device;
/// <summary>
/// Gets target device (Display) based on the rectangle we want to capture
/// </summary>
/// <param name="sourceRect">Rectangle we want to capture</param>
/// <returns>Screen which contains the area we want to capture or null if no device contains our area of interest</returns>
private static Screen GetTargetScreen(Rectangle sourceRect) {
foreach (var scr in Screen.AllScreens)
{
if (sourceRect.X >= scr.Bounds.X && sourceRect.Y >= scr.Bounds.Y
&& sourceRect.Right <= scr.Bounds.Width + scr.Bounds.X
&& sourceRect.Bottom <= scr.Bounds.Height + scr.Bounds.Y
)
{
return scr;
}
}
return null;
}
public static (byte[], int) Capture(Rectangle sourceRect, int jpegQuality) {
Screen targetScreen = GetTargetScreen(sourceRect);
if (targetScreen == null) {
throw new Exception($#"Could not find target screen for capture rectangle {sourceRect}");
}
//This is to instruct client receiving the image to rotate it, seems like a reasonable thing to offload it to client and save a bit of CPU time on server
int rotation = 0;
byte[] imageBytes = null;
// Width/Height of desktop to capture
int width = targetScreen.Bounds.Width;
int height = targetScreen.Bounds.Height;
Rectangle cropRect = new Rectangle(sourceRect.X - targetScreen.Bounds.X, sourceRect.Y - targetScreen.Bounds.Y, sourceRect.Width, sourceRect.Height);
// Create DXGI Factory1
if (adapter == null) { adapter = factory.Adapters.Where(x => x.Outputs.Any(o => o.Description.DeviceName == targetScreen.DeviceName)).FirstOrDefault(); }
// Create device from Adapter
if (device == null) { device = new Device(adapter); }
//using (var output = adapter.Outputs.Where(o => o.Description.DeviceName == targetScreen.DeviceName).FirstOrDefault()) //This creates a memory leak!
Output output = null;
//I'm open to suggestions here:
for (int i = 0; i < adapter.GetOutputCount(); i++) {
output = adapter.GetOutput(i);
if (output.Description.DeviceName == targetScreen.DeviceName) {
break;
}
else {
output.Dispose();
}
}
using (var output1 = output.QueryInterface<Output1>()) {
if (output1.Description.Rotation == DisplayModeRotation.Rotate90) {
width = targetScreen.Bounds.Height;
height = targetScreen.Bounds.Width;
int offsetX = targetScreen.Bounds.X - sourceRect.X;
cropRect = new Rectangle(
sourceRect.Y - targetScreen.Bounds.Y,
targetScreen.Bounds.Width - (sourceRect.Width + offsetX),
sourceRect.Height, sourceRect.Width);
rotation = 90;
}
else if (output1.Description.Rotation == DisplayModeRotation.Rotate270) {
width = targetScreen.Bounds.Height;
height = targetScreen.Bounds.Width;
int offsetY = targetScreen.Bounds.Y - sourceRect.Y;
cropRect = new Rectangle(
targetScreen.Bounds.Height - (sourceRect.Height + offsetY),
targetScreen.Bounds.X - sourceRect.X,
sourceRect.Height, sourceRect.Width);
rotation = 270;
}
else if (output1.Description.Rotation == DisplayModeRotation.Rotate180) {
rotation = 180;
}
// Create Staging texture CPU-accessible
var textureDesc = new Texture2DDescription {
CpuAccessFlags = CpuAccessFlags.Read,
BindFlags = BindFlags.None,
Format = Format.B8G8R8A8_UNorm,
Width = width,
Height = height,
OptionFlags = ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1,
SampleDescription = { Count = 1, Quality = 0 },
Usage = ResourceUsage.Staging
};
using (var screenTexture = new Texture2D(device, textureDesc))
//Duplicate the output
using (var duplicatedOutput = output1.DuplicateOutput(device)) {
bool captureDone = false;
SharpDX.DXGI.Resource screenResource = null;
OutputDuplicateFrameInformation duplicateFrameInformation;
for (int i = 0; !captureDone; i++) {
try {
//Try to get duplicated frame within given time
duplicatedOutput.AcquireNextFrame(1000, out duplicateFrameInformation, out screenResource);
//Ignore first call, this always seems to return a black frame
if (i == 0) {
screenResource.Dispose();
continue;
}
//copy resource into memory that can be accessed by the CPU
using (var screenTexture2D = screenResource.QueryInterface<Texture2D>()) {
device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);
}
//Get the desktop capture texture
var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None);
var boundsRect = new System.Drawing.Rectangle(0, 0, width, height);
//Create Drawing.Bitmap
using (var bitmap = new System.Drawing.Bitmap(width, height, PixelFormat.Format32bppArgb)) {
//Copy pixels from screen capture Texture to GDI bitmap
var bitmapData = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
var sourcePtr = mapSource.DataPointer;
var destinationPtr = bitmapData.Scan0;
for (int y = 0; y < height; y++) {
//Copy a single line
Utilities.CopyMemory(destinationPtr, sourcePtr, width * 4);
//Advance pointers
sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
destinationPtr = IntPtr.Add(destinationPtr, bitmapData.Stride);
}
//Release source and dest locks
bitmap.UnlockBits(bitmapData);
device.ImmediateContext.UnmapSubresource(screenTexture, 0);
//Save the output
imageBytes = CropBitmapToJPEGBytes(bitmap, cropRect, jpegQuality);
}
//Capture done
captureDone = true;
}
catch (SharpDXException e) {
if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) {
throw;
}
}
finally {
//Dispose manually
if (screenResource != null) {
screenResource.Dispose();
}
duplicatedOutput.ReleaseFrame();
}
}
}
}
output.Dispose();
return (imageBytes, rotation);
}
/// <summary>
/// Crop bitmap
/// </summary>
/// <param name="orig">Original bitmap</param>
/// <param name="cropRect">Crop rectangle</param>
/// <returns>Cropped bitmap</returns>
static byte[] CropBitmapToJPEGBytes(Bitmap orig, Rectangle cropRect, int jpegQuality) {
EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, (long)jpegQuality);
jpegParams.Param[0] = qualityParam;
byte[] imageBytes;
using (Bitmap nb = new Bitmap(cropRect.Width, cropRect.Height)) {
using (Graphics g = Graphics.FromImage(nb)) {
g.DrawImage(orig, -cropRect.X, -cropRect.Y);
using (MemoryStream s = new MemoryStream()) {
nb.Save(s, jpegCodec, jpegParams);
imageBytes = s.ToArray();
}
}
}
return imageBytes;
}
}
}

Related

How to draw the diffrenet pixels in yellow only when the pie is rotating above this pixels?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Doppler_Radar
{
public partial class Form1 : Form
{
int myPiePercent = 15;
float distanceFromCenterPixels;
float distanceFromCenterKm = 200F;
public Form1()
{
InitializeComponent();
pictureBox1.Image = Image.FromFile(#"D:\New folder (4)\Weather Radar\WithClouds.bmp");
timer1.Enabled = true;
distanceFromCenterPixels = (float)(183d * (double)distanceFromCenterKm / 200d);
pictureBox1.Image = CalcDifference(new Bitmap(pictureBox1.Image),
new Bitmap(#"D:\New folder (4)\Weather Radar\WithoutClouds.bmp"));
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
DrawPieOnPicturebox(e.Graphics);
}
public void DrawPieOnPicturebox(Graphics myPieGraphic)
{
Color myPieColors = Color.FromArgb(150, Color.LightGreen);
Size myPieSize = new Size((int)distanceFromCenterPixels, (int)distanceFromCenterPixels);
Point myPieLocation = new Point((pictureBox1.Width - myPieSize.Width) / 2, (pictureBox1.Height - myPieSize.Height) / 2);
DrawMyPie(myPiePercent, myPieColors, myPieGraphic, myPieLocation, myPieSize);
}
public void DrawMyPie(int myPiePerecent, Color myPieColor, Graphics myPieGraphic, Point
myPieLocation, Size myPieSize)
{
using (SolidBrush brush = new SolidBrush(myPieColor))
{
myPieGraphic.FillPie(brush, new Rectangle(myPieLocation, myPieSize), Convert.ToSingle(myPiePerecent * 360 / 100), Convert.ToSingle(15 * 360 / 100));
}
}
private void timer1_Tick(object sender, EventArgs e)
{
myPiePercent++;
pictureBox1.Invalidate();
}
public Bitmap CalcDifference(Bitmap bmp1, Bitmap bmp2)
{
Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
BitmapData bitmapdata = bmp1.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
IntPtr source = bitmapdata.Scan0;
BitmapData data2 = bmp2.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
IntPtr ptr2 = data2.Scan0;
int length = (bmp1.Width * bmp1.Height) * 4;
byte[] destination = new byte[length];
byte[] buffer2 = new byte[length];
Marshal.Copy(source, destination, 0, length);
Marshal.Copy(ptr2, buffer2, 0, length);
for (int i = 0; i < length; i += 4)
{
if (((destination[i] == buffer2[i]) && (destination[i + 1] == buffer2[i + 1])) && (destination[i + 2] == buffer2[i + 2]))
{
destination[i] = 0xff;
destination[i + 1] = 0xff;
destination[i + 2] = 0xff;
destination[i + 3] = 0;
}
else
{
destination[i] = 0;
destination[i + 1] = 255;
destination[i + 2] = 255;
}
}
Marshal.Copy(destination, 0, source, length);
bmp1.UnlockBits(bitmapdata);
bmp2.UnlockBits(data2);
return bmp1;
}
}
}
this color in the yellow the diffrenet pixels all the time because i'm calling the method CalcDifference in the constructor. but what i want is that only when the rotating pie is over/above this diffrenet pixels then color this diffrenet pixels that are under the pie and not all the diffrenet pixels.
on the left side is when coloring the diffrenet pixels with the method CalcDifference.
on the right side the image at it's original.
the goal is to make a doppler radar effect that detect the clouds when the pie is rotating abvoe them.
Yes, you can use a little trick to change the colors of the clouds under the pie and skip the others.
To optimize your CalcDifference method and speed things up, get from each image only the pie region to process. The rest of the image does not change and you don't need to traverse the data of that region.
Pass the clipped images to the CalcDifference method and change the colors of the clouds in that region. All of them. Note, I have changed the method to convert the original images to 32bppArgb images if needed so you can pass images of different formats that the LockBits supports to process.
Assign the full-size image with clouds to the .Image property of the PictureBox.
Here's the trick. Handle the PictureBox.Paint event and create a GraphicsPath object, add the pie and pass it to Graphics.SetClip method. Draw the yellow clouds image then reset the clip (Graphics.ResetClip) to draw the pie.
public partial class SomeForm : Form
{
private float startAngle;
private readonly SolidBrush brPie;
private Rectangle pieRect;
private Bitmap imgWithYellowClouds;
public SomeForm()
{
InitializeComponent();
brPie = new SolidBrush(Color.FromArgb(150, Color.LightGreen));
picWithoutClouds.Image = Image.FromFile(#"...");
picWithClouds.Image = picCanvas.Image = Image.FromFile(#"...");
CreateYellowCloudsImage();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
brPie.Dispose();
imgWithYellowClouds?.Dispose();
}
private void picCanvas_Resize(object sender, EventArgs e) =>
CreateYellowCloudsImage();
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
if (pieRect.Width < 1 || pieRect.Height < 1 ||
imgWithYellowClouds == null) return;
var g = e.Graphics;
using (var gp = new GraphicsPath())
{
gp.AddPie(pieRect, startAngle, 360f / 8);
g.SetClip(gp);
g.DrawImage(imgWithYellowClouds, pieRect);
g.ResetClip();
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPath(brPie, gp);
}
}
private void SomeButton(object sender, EventArgs e)
{
timer1.Enabled = !timer1.Enabled;
}
private void timer1_Tick(object sender, EventArgs e)
{
// Change as needed...
startAngle = (startAngle + 10) % 360;
picCanvas.Invalidate();
}
public Bitmap CalcDifference(Bitmap bmp1, Bitmap bmp2)
{
var disImg1 = !Is32bppArgbFormat(bmp1);
var disImg2 = !Is32bppArgbFormat(bmp2);
var img1 = disImg1 ? To32bppArgbFormat(bmp1) : new Bitmap(bmp1);
var img2 = disImg2 ? To32bppArgbFormat(bmp2) : bmp2;
var img1Data = img1.LockBits(
new Rectangle(Point.Empty, img1.Size),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
var img2Data = img2.LockBits(
new Rectangle(Point.Empty, img2.Size),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
var img1Buffer = new byte[img1Data.Stride * img1Data.Height];
var img2Buffer = new byte[img2Data.Stride * img2Data.Height];
Marshal.Copy(img1Data.Scan0, img1Buffer, 0, img1Buffer.Length);
Marshal.Copy(img2Data.Scan0, img2Buffer, 0, img2Buffer.Length);
img2.UnlockBits(img2Data);
for (int i = 0; i + 4 < img1Buffer.Length; i += 4)
{
if (img1Buffer[i] == img2Buffer[i] &&
img1Buffer[i + 1] == img2Buffer[i + 1] &&
img1Buffer[i + 2] == img2Buffer[i + 2])
{
img1Buffer[i] = 0;
img1Buffer[i + 1] = 0;
img1Buffer[i + 2] = 0;
img1Buffer[i + 3] = 0;
}
else
{
img1Buffer[i] = 0;
img1Buffer[i + 1] = 255;
img1Buffer[i + 2] = 255;
}
}
Marshal.Copy(img1Buffer, 0, img1Data.Scan0, img1Buffer.Length);
img1.UnlockBits(img1Data);
if (disImg2) img2.Dispose();
return img1;
}
private bool Is32bppArgbFormat(Bitmap bmp) =>
Image.GetPixelFormatSize(bmp.PixelFormat) == 32 &&
bmp.PixelFormat != PixelFormat.Indexed;
private Bitmap To32bppArgbFormat(Bitmap src)
{
var bmp = new Bitmap(src.Width, src.Height, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(bmp))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(src, new Rectangle(0, 0, bmp.Width, bmp.Height),
0, 0, src.Width, src.Height, GraphicsUnit.Pixel);
return bmp;
}
}
private void CreateYellowCloudsImage()
{
var cs = picCanvas.ClientSize;
// Change as needed...
var sz = new Size(128, 128);
pieRect = new Rectangle(
(cs.Width - sz.Width) / 2,
(cs.Height - sz.Height) / 2,
sz.Width, sz.Height);
// To get the image rectangle regardless of the SizeMode property
// so we can get the pie's region to process...
var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
BindingFlags.NonPublic | BindingFlags.Instance);
var imageRect = (Rectangle)method.Invoke(picCanvas,
new object[] { picCanvas.SizeMode });
var cx = picCanvas.Image.Width / (float)imageRect.Width;
var cy = picCanvas.Image.Height / (float)imageRect.Height;
var r2 = RectangleF.Intersect(imageRect, pieRect);
r2.Offset(-imageRect.X, -imageRect.Y);
var cloneRect = new RectangleF(
r2.X * cx,
r2.Y * cy,
r2.Width * cx,
r2.Height * cy);
imgWithYellowClouds?.Dispose();
imgWithYellowClouds = null;
using (var cloulds = (picWithClouds.Image as Bitmap)
.Clone(cloneRect, picWithClouds.Image.PixelFormat))
using (var noClouds = (picWithoutClouds.Image as Bitmap)
.Clone(cloneRect, picWithoutClouds.Image.PixelFormat))
{
// The yellow clouds image...
imgWithYellowClouds = CalcDifference(cloulds, noClouds);
}
}
}
Taken from your previous questions for this demo.

Finding bitmap inside a bitmap in C# - Doesn't work on a server?

I am trying to automate something with my C# application, for which I use a bitmap detection system to detect if an icon has appeared on screen. This works perfectly on a PC. However, when I put the application on a server, it never works. I am using a Google Cloud instance with a Tesla K80, 2 vcpus running Windows server 2012.
Here is my code:
// Capture the current screen as a bitmap
public static Bitmap CaptureScreen()
{
// Bitmap format
Bitmap ScreenCapture = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
// Capture screen
Graphics GFX = Graphics.FromImage(ScreenCapture);
GFX.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y, 0, 0,
ScreenCapture.Size, CopyPixelOperation.SourceCopy);
return ScreenCapture;
}
// Find a list of all the points of a bitmap within another bitmap
public static List<Point> FindBitmapsEntry(Bitmap SourceBitmap, Bitmap SearchedBitmap)
{
#region Arguments check
if (SourceBitmap == null || SearchedBitmap == null)
throw new ArgumentNullException();
if (SourceBitmap.PixelFormat != SearchedBitmap.PixelFormat)
throw new ArgumentException("Pixel formats aren't equal.");
if (SourceBitmap.Width < SearchedBitmap.Width || SourceBitmap.Height < SearchedBitmap.Height)
throw new ArgumentException("Size of SearchedBitmap is bigger than SourceBitmap!");
#endregion
var PixelFormatSize = Image.GetPixelFormatSize(SourceBitmap.PixelFormat) / 8;
// Copy SourceBitmap to byte array
var SourceBitmapData = SourceBitmap.LockBits(new Rectangle(0, 0, SourceBitmap.Width, SourceBitmap.Height),
ImageLockMode.ReadOnly, SourceBitmap.PixelFormat);
var SourceBitmapByteLength = SourceBitmapData.Stride * SourceBitmap.Height;
var SourceBytes = new byte[SourceBitmapByteLength];
Marshal.Copy(SourceBitmapData.Scan0, SourceBytes, 0, SourceBitmapByteLength);
SourceBitmap.UnlockBits(SourceBitmapData);
// Copy SearchedBitmap to byte array
var SearchingBitmapData =
SearchedBitmap.LockBits(new Rectangle(0, 0, SearchedBitmap.Width, SearchedBitmap.Height),
ImageLockMode.ReadOnly, SearchedBitmap.PixelFormat);
var SearchingBitmapByteLength = SearchingBitmapData.Stride * SearchedBitmap.Height;
var SearchingBytes = new byte[SearchingBitmapByteLength];
Marshal.Copy(SearchingBitmapData.Scan0, SearchingBytes, 0, SearchingBitmapByteLength);
SearchedBitmap.UnlockBits(SearchingBitmapData);
var PointsList = new List<Point>();
// Searching entries, minimizing searching zones
// SourceBitmap.Height - SearchedBitmap.Height + 1
for (var MainY = 0; MainY < SourceBitmap.Height - SearchedBitmap.Height + 1; MainY++)
{
var SourceY = MainY * SourceBitmapData.Stride;
for (var MainX = 0; MainX < SourceBitmap.Width - SearchedBitmap.Width + 1; MainX++)
{
// MainY & MainX - pixel coordinates of SourceBitmap
// SourceY + SourceX = pointer in array SourceBitmap bytes
var SourceX = MainX * PixelFormatSize;
var IsEqual = true;
for (var c = 0; c < PixelFormatSize; c++)
{
// Check through the bytes in pixel
if (SourceBytes[SourceX + SourceY + c] == SearchingBytes[c])
continue;
IsEqual = false;
break;
}
if (!IsEqual) continue;
var ShouldStop = false;
// Find first equation and search deeper
for (var SecY = 0; SecY < SearchedBitmap.Height; SecY++)
{
var SearchY = SecY * SearchingBitmapData.Stride;
var SourceSecY = (MainY + SecY) * SourceBitmapData.Stride;
for (var SecX = 0; SecX < SearchedBitmap.Width; SecX++)
{
// SecX & SecY - coordinates of SearchingBitmap
// SearchX + SearchY = pointer in array SearchingBitmap bytes
var SearchX = SecX * PixelFormatSize;
var SourceSecX = (MainX + SecX) * PixelFormatSize;
for (var c = 0; c < PixelFormatSize; c++)
{
// Check through the bytes in pixel
if (SourceBytes[SourceSecX + SourceSecY + c] == SearchingBytes[SearchX + SearchY + c]) continue;
// Not equal - abort iteration
ShouldStop = true;
break;
}
if (ShouldStop) break;
}
if (ShouldStop) break;
}
if (!ShouldStop) // Bitmap is found
{
PointsList.Add(new Point(MainX, MainY));
}
}
}
return PointsList;
}
And here is how I use it:
Bitmap HighlightBitmap = new Bitmap(Resources.icon);
Bitmap CurrentScreen = CaptureScreen();
List<Point> HighlightPoints = FindBitmapsEntry(CurrentScreen, HighlightBitmap);
with this HighlightPoints[0] is supposed to give me the first point the two bitmaps (icon, screenshot) collide. But as mentioned before, it just doesn't work on the server.
Thanks in advance!
P.S. I am using the server with a RDP so it does have a visual interface to work with

SetResolution does nothing when joining tiff images

I've been trying to adjust the resolution for a new tiff that gets created from a group of other tiffs. I would like to drop the resolution to 100x100 dpi. Everything with the join works properly but the resolution in the final tiff will always be what the resolution is of the files I am joining together (used 300x300 dpi images). I have tried using some suggestions (Set DPI value to Tiff Image in C#) for getting/setting the PropertyItems but that has failed as well. Using the join technique below, what would be the proper way to set the resolution of the final image to 100x100 dpi?
Thank you.
public byte[] JoinTiffImages(
List<byte[]> images)
{
var fPage = FrameDimension.Page;
var nearest =
System.Drawing
.Drawing2D
.InterpolationMode
.NearestNeighbor;
if (images.Count == 0)
{
throw new ImageConverterException(
"Could not join an empty set of images.");
}
else if (images.Count == 1)
{
return images[0];
}
else
{
using (var ms = new MemoryStream())
{
using (var masterBitmap =
(Bitmap)DrawingImage.FromStream(
new MemoryStream(images[0])))
{
//get the codec for tiff files
var info = GetTifCodecInfo();
var enc = Encoder.SaveFlag;
var ep = new EncoderParameters(2);
ep.Param[0] = new EncoderParameter(
enc,
(long)EncoderValue.MultiFrame);
ep.Param[1] = new EncoderParameter(
Encoder.Compression,
(long)TifCompression.ToEncoderValue());
masterBitmap.SetResolution(100.0f, 100.0f);
masterBitmap.Save(ms, info, ep);
//save the intermediate frames
ep.Param[0] = new EncoderParameter(
enc,
(long)EncoderValue.FrameDimensionPage);
for (int i = 1; i < images.Count; i++)
{
using (var nextImg = (Bitmap)DrawingImage.FromStream(
new MemoryStream(images[i])))
{
for (int x = 0;
x < nextImg.GetFrameCount(fPage);
x++)
{
nextImg.SetResolution(100.0f, 100.0f);
nextImg.SelectActiveFrame(fPage, x);
masterBitmap.SaveAdd(nextImg, ep);
}
}
}
ep.Param[0] =
new EncoderParameter(
enc,
(long)EncoderValue.Flush);
//close out the file.
masterBitmap.SaveAdd(ep);
return ms.ToArray();
}
}
}
}
Here is a code that creates a multi-page tif file from a list of images.
It sets the DPI both to the master and all parts according to an input paramter.
private void saveTiff_Click(List<Image> imgList, string saveName, int dpi)
{
//all kudos to : http://bobpowell.net/generating_multipage_tiffs.aspx
foreach (Image img in imgList) ((Bitmap)img).SetResolution(dpi, dpi);
System.Drawing.Imaging.Encoder enc = System.Drawing.Imaging.Encoder.SaveFlag;
Bitmap master = new Bitmap(imgList[0]);
master.SetResolution(dpi, dpi);
ImageCodecInfo info = null;
// lets hope we have an TIF encoder
foreach (ImageCodecInfo ice in ImageCodecInfo.GetImageEncoders())
if (ice.MimeType == "image/tiff") info = ice;
// one parameter: MultiFrame
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
master.Save(saveName, info, ep);
// one parameter: further frames
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.FrameDimensionPage);
for (int i = 1; i < imgList.Count; i++) master.SaveAdd(imgList[i], ep);
// one parameter: flush
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.Flush);
master.SaveAdd(ep);
}
I was able to resolve my issue with some help from the comments. This function will handle the resolution change without the image changing size.
public static byte[] JoinTiffImages(
List<byte[]> images,
float res)
{
var fPage = FrameDimension.Page;
var nearest =
System.Drawing
.Drawing2D
.InterpolationMode
.NearestNeighbor;
if (images.Count == 0)
{
throw new Exception(
"Could not join an empty set of images.");
}
else if (images.Count == 1)
{
return images[0];
}
else
{
var ms = new MemoryStream();
//get the codec for tiff files
var info = GetTifCodecInfo();
var ep = new EncoderParameters(2);
ep.Param[0] = new EncoderParameter(
System.Drawing.Imaging.Encoder.SaveFlag,
(long)EncoderValue.MultiFrame);
ep.Param[1] = new EncoderParameter(
System.Drawing.Imaging.Encoder.Compression,
(long)EncoderValue.CompressionLZW);
using (var masterImg =
(Bitmap)System.Drawing.Image.FromStream(
new MemoryStream(images[0])))
{
using (var resizedMaster =
new Bitmap(
(int)(masterImg.Width *
(res /
masterImg.HorizontalResolution)),
(int)(masterImg.Height *
(res /
masterImg.VerticalResolution))))
{
resizedMaster.SetResolution(
res,
res);
using (var gr = Graphics.FromImage(resizedMaster))
{
gr.InterpolationMode = nearest;
gr.DrawImage(
masterImg,
new Rectangle(
0,
0,
resizedMaster.Width,
resizedMaster.Height),
0,
0,
masterImg.Width,
masterImg.Height,
GraphicsUnit.Pixel);
}
resizedMaster.Save(ms, info, ep);
//save the intermediate frames
ep.Param[0] = new EncoderParameter(
System.Drawing.Imaging.Encoder.SaveFlag,
(long)EncoderValue.FrameDimensionPage);
for (int i = 1; i < images.Count; i++)
{
using (var nextImg =
(Bitmap)System.Drawing.Image.FromStream(
new MemoryStream(images[i])))
{
for (int x = 0;
x < nextImg.GetFrameCount(fPage);
x++)
{
nextImg.SelectActiveFrame(fPage, x);
using (var resizedNext =
new Bitmap(
(int)(nextImg.Width *
(res /
nextImg.HorizontalResolution)),
(int)(nextImg.Height *
(res /
nextImg.VerticalResolution))))
{
resizedNext.SetResolution(
res,
res);
using (var gr =
Graphics.FromImage(resizedNext))
{
gr.InterpolationMode = nearest;
gr.DrawImage(
nextImg,
new Rectangle(
0,
0,
resizedNext.Width,
resizedNext.Height),
0,
0,
nextImg.Width,
nextImg.Height,
GraphicsUnit.Pixel);
}
resizedMaster.SaveAdd(resizedNext, ep);
}
}
}
}
ep.Param[0] =
new EncoderParameter(
System.Drawing.Imaging.Encoder.SaveFlag,
(long)EncoderValue.Flush);
//close out the file.
resizedMaster.SaveAdd(ep);
}
return ms.ToArray();
}
}
}
private static ImageCodecInfo GetTifCodecInfo()
{
foreach (var ice in ImageCodecInfo.GetImageEncoders())
{
if (ice.MimeType == "image/tiff")
{
return ice;
}
}
return null;
}

Resizing Images - WIC vs GDI

EDIT: I've since worked out that neither of these implementations are any good for server-side scenarios: they're not supported by Microsoft. So don't go using them!
So I've provided two separate implementations below. I think I've stuffed up the Windows Imaging Component (WIC) implementation.
Some comments:
The GDI implementation seems to be quicker than WIC - WIC # 0.26s/photo, GDI # 0.14s/photo)
The WIC implementation doesn't see any performance gains when multi-threaded, GDI drops to ~0.10s/photo
Only WIC is supported for server-side processing, however if it doesn't mutli-thread then it doesn't scale well
Run on i7, the photo in question was a typical 1.2MB image created by an Olympus digicam
I drew my inspiration from http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx
Can anyone see anything obvious?
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Mrwa.Bms.Common.Imaging
{
/// <summary>
/// Generates JPEG image previews for any supplied .NET image supported files
/// </summary>
/// <remarks>
/// WIC = Windows Imaging Component
/// http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx
/// </remarks>
public class WicImagePreviewGenerator : IImagePreviewGenerator
{
private const int ScreenDpi = 96;
private BitmapFrame _imageFrame;
public WicImagePreviewGenerator(Stream stream)
{
Contract.Requires(stream != null);
try
{
if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
_imageFrame = decoder.Frames[0];
}
catch (NotSupportedException ex)
{
throw new ArgumentException("The image is corrupt.", "stream", ex);
}
}
public ImagePreviewGeneratorDto Generate(
int pixelSize, int jpegQuality = 80, int dpi = 72,
ImagePreviewGeneratorResizeQualityEnum resizeQuality = ImagePreviewGeneratorResizeQualityEnum.HighQuality)
{
int previewWidth;
int previewHeight;
CalculateDimensions(pixelSize, out previewWidth, out previewHeight);
// create a new target drawing canvas
var width = (int) (previewWidth*(ScreenDpi/(decimal) dpi));
var height = (int) (previewHeight*(ScreenDpi/(decimal) dpi));
var drawing = new ImageDrawing(
_imageFrame,
new Rect(0, 0, width, height));
var group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(group, GetScalingMode(resizeQuality));
group.Children.Add(drawing);
// generate the preview image frame
BitmapFrame previewFrame;
var previewVisual = new DrawingVisual();
using (var previewContext = previewVisual.RenderOpen())
{
previewContext.DrawDrawing(group);
previewContext.Close();
var previewBitmap = new RenderTargetBitmap(
previewWidth, previewHeight,
dpi, dpi,
PixelFormats.Default);
previewBitmap.Render(previewVisual);
previewFrame = BitmapFrame.Create(previewBitmap);
}
// generate the result as a JPG
using (var content = new MemoryStream())
{
var previewEncoder = new JpegBitmapEncoder { QualityLevel = jpegQuality };
previewEncoder.Frames.Add(previewFrame);
previewEncoder.Save(content);
content.Flush();
return new ImagePreviewGeneratorDto
{
Preview = content.ToArray(),
Width = previewWidth,
Height = previewHeight
};
}
}
// not used - retained for reference only
public IEnumerable<byte> GenerateOptimised(int pixelSize, int jpegQuality = 80)
{
int previewWidth;
int previewHeight;
CalculateDimensions(pixelSize, out previewWidth, out previewHeight);
var transform = new TransformedBitmap(
_imageFrame, new ScaleTransform(previewWidth, previewHeight, 0, 0));
var previewFrame = BitmapFrame.Create(transform);
// generate the result as a JPG
using (var result = new MemoryStream())
{
var previewEncoder = new JpegBitmapEncoder { QualityLevel = jpegQuality };
previewEncoder.Frames.Add(previewFrame);
previewEncoder.Save(result);
return result.ToArray();
}
}
private static BitmapScalingMode GetScalingMode(ImagePreviewGeneratorResizeQualityEnum previewQuality)
{
switch (previewQuality)
{
case ImagePreviewGeneratorResizeQualityEnum.HighQuality:
return BitmapScalingMode.HighQuality;
case ImagePreviewGeneratorResizeQualityEnum.HighSpeed:
return BitmapScalingMode.LowQuality;
default:
throw new NotSupportedException("Invalid preview quality specified.");
}
}
private void CalculateDimensions(int pixelSize, out int width, out int height)
{
var originalWidth = _imageFrame.PixelWidth;
var originalHeight = _imageFrame.PixelHeight;
// scale: reduce the longest side down to 'X' pixels and maintain the aspect ratio
if (originalWidth <= pixelSize && originalHeight <= pixelSize)
{
width = originalWidth;
height = originalHeight;
}
else if (originalWidth >= originalHeight)
{
width = pixelSize;
height = (int)((pixelSize / (decimal)originalWidth) * originalHeight);
}
else
{
width = (int)((pixelSize / (decimal)originalHeight) * originalWidth);
height = pixelSize;
}
}
#region IDisposable
private bool _disposed;
~WicImagePreviewGenerator()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// free managed resources
_imageFrame = null;
}
// free unmanaged resources
_disposed = true;
}
#endregion
}
}
using System;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
namespace Mrwa.Bms.Common.Imaging
{
/// <summary>
/// Generates JPEG image previews for any supplied .NET image supported files
/// </summary>
/// <remarks>
/// Feel free to use this Client side. Not officially supported for back-end scenarios.
/// </remarks>
public class GdiPlusImagePreviewGenerator : IImagePreviewGenerator
{
private Image _image;
public GdiPlusImagePreviewGenerator(Stream stream)
{
Contract.Requires(stream != null);
try
{
if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
_image = Image.FromStream(stream);
}
catch (ArgumentException ex)
{
throw new ArgumentException("The image is corrupt.", "stream", ex);
}
}
private void CalculateDimensions(int pixelSize, out int width, out int height)
{
var originalWidth = _image.Width;
var originalHeight = _image.Height;
// scale: reduce the longest side down to 'X' pixels and maintain the aspect ratio
if (originalWidth <= pixelSize && originalHeight <= pixelSize)
{
width = originalWidth;
height = originalHeight;
}
else if (originalWidth >= originalHeight)
{
width = pixelSize;
height = (int)((pixelSize / (decimal)originalWidth) * originalHeight);
}
else
{
width = (int)((pixelSize / (decimal)originalHeight) * originalWidth);
height = pixelSize;
}
}
/// <remarks>
/// Not changing the colour depth; apparently the conversion can be quite poor
/// Don't forget to dispose of the stream
/// </remarks>
public ImagePreviewGeneratorDto Generate(
int pixelSize, int jpegQuality = 80, int dpi = 72,
ImagePreviewGeneratorResizeQualityEnum resizeQuality = ImagePreviewGeneratorResizeQualityEnum.HighQuality)
{
int previewWidth;
int previewHeight;
CalculateDimensions(pixelSize, out previewWidth, out previewHeight);
// resize the image (in terms of pixels) and standardise the DPI
using (var previewImage = new Bitmap(previewWidth, previewHeight))
{
previewImage.SetResolution(dpi, dpi);
using (var graphics = Graphics.FromImage(previewImage))
{
switch (resizeQuality)
{
case ImagePreviewGeneratorResizeQualityEnum.HighQuality:
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
break;
case ImagePreviewGeneratorResizeQualityEnum.HighSpeed:
graphics.SmoothingMode = SmoothingMode.HighSpeed;
graphics.InterpolationMode = InterpolationMode.Low;
graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
break;
default:
throw new NotSupportedException("Invalid Preview Quality Enum supplied.");
}
graphics.DrawImage(_image, new Rectangle(0, 0, previewWidth, previewHeight));
}
// convert to a JPG and reduce the quality
using (var content = new MemoryStream())
{
var jpegEncoder = GetEncoder(ImageFormat.Jpeg);
previewImage.Save(content, jpegEncoder,
new EncoderParameters
{
Param = new[] { new EncoderParameter(Encoder.Quality, jpegQuality) },
});
content.Flush();
// return the stream
return new ImagePreviewGeneratorDto
{
Preview = content.ToArray(),
Width = previewWidth,
Height = previewHeight
};
}
}
}
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
return codecs.FirstOrDefault(codec => codec.FormatID == format.Guid);
}
#region IDisposable
private bool _disposed;
~GdiPlusImagePreviewGenerator()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// free managed resources
if (_image != null)
{
_image.Dispose();
_image = null;
}
}
// free unmanaged resources
_disposed = true;
}
#endregion
}
}
I noticed that BitmapCacheOption.None greatly increases performance when dealing with batch processing.

Recognize objects in image [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Hello I am in the process of doing a school project, where we have a robot driving on the ground in between Flamingo plates. We need to create an algorithm that can identify the locations of these plates, so we can create paths around them (We are using A Star for that).
So far have we worked with AForged Library and we have created the following class, the only problem with this is that when it create the rectangles dose it not take in account that the plates are not always parallel with the camera border, and it that case will it just create a rectangle that cover the whole plate.
So we need to some way find the rotation on the object, or another way to identify this.
I have create an image that might help explain this
Image the describe the problem: http://img683.imageshack.us/img683/9835/imagerectangle.png
Any help on how I can do this would be greatly appreciated.
Any other information or ideers are always welcome.
public class PasteMap
{
private Bitmap image;
private Bitmap processedImage;
private Rectangle[] rectangels;
public void initialize(Bitmap image)
{
this.image = image;
}
public void process()
{
processedImage = image;
processedImage = applyFilters(processedImage);
processedImage = filterWhite(processedImage);
rectangels = extractRectangles(processedImage);
//rectangels = filterRectangles(rectangels);
processedImage = drawRectangelsToImage(processedImage, rectangels);
}
public Bitmap getProcessedImage
{
get
{
return processedImage;
}
}
public Rectangle[] getRectangles
{
get
{
return rectangels;
}
}
private Bitmap applyFilters(Bitmap image)
{
image = new ContrastCorrection(2).Apply(image);
image = new GaussianBlur(10, 10).Apply(image);
return image;
}
private Bitmap filterWhite(Bitmap image)
{
Bitmap test = new Bitmap(image.Width, image.Height);
for (int width = 0; width < image.Width; width++)
{
for (int height = 0; height < image.Height; height++)
{
if (image.GetPixel(width, height).R > 200 &&
image.GetPixel(width, height).G > 200 &&
image.GetPixel(width, height).B > 200)
{
test.SetPixel(width, height, Color.White);
}
else
test.SetPixel(width, height, Color.Black);
}
}
return test;
}
private Rectangle[] extractRectangles(Bitmap image)
{
BlobCounter bc = new BlobCounter();
bc.FilterBlobs = true;
bc.MinWidth = 5;
bc.MinHeight = 5;
// process binary image
bc.ProcessImage( image );
Blob[] blobs = bc.GetObjects(image, false);
// process blobs
List<Rectangle> rects = new List<Rectangle>();
foreach (Blob blob in blobs)
{
if (blob.Area > 1000)
{
rects.Add(blob.Rectangle);
}
}
return rects.ToArray();
}
private Rectangle[] filterRectangles(Rectangle[] rects)
{
List<Rectangle> Rectangles = new List<Rectangle>();
foreach (Rectangle rect in rects)
{
if (rect.Width > 75 && rect.Height > 75)
Rectangles.Add(rect);
}
return Rectangles.ToArray();
}
private Bitmap drawRectangelsToImage(Bitmap image, Rectangle[] rects)
{
BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
foreach (Rectangle rect in rects)
Drawing.FillRectangle(data, rect, Color.Red);
image.UnlockBits(data);
return image;
}
}
You need to analyse the blobs a bit more to find the corners as #kigurai has said. The AForge library allows you to do this, see the section Finding convex hull on this page for more info. The screenshot below (from the page) shows a small sample of what the convex hull is.
(source: aforgenet.com)
You want to take a look at the GetBlobsLeftAndRightEdges function and the GrahamConvexHull class.
If anyone is interested, this is the way I did it.
Blobsprocessing:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using AForge;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Imaging.Textures;
using AForge.Math.Geometry;
namespace CDIO.Library
{
public class Blobsprocessing
{
Bitmap image;
BlobCounter BlobCounter;
Blob[] blobs;
List<Polygon> hulls;
public Blobsprocessing(Bitmap image)
{
this.image = image;
}
public void Process()
{
BlobCounter = new BlobCounter();
processBlobs();
extractConvexHull();
}
public List<Polygon> getHulls()
{
return hulls;
}
private void processBlobs()
{
BlobCounter.FilterBlobs = true;
BlobCounter.MinWidth = 5;
BlobCounter.MinHeight = 5;
// set ordering options
BlobCounter.ObjectsOrder = ObjectsOrder.Size;
// process binary image
BlobCounter.ProcessImage(image);
blobs = BlobCounter.GetObjectsInformation();
}
private void extractConvexHull()
{
GrahamConvexHull hullFinder = new GrahamConvexHull();
// process each blob
hulls = new List<Polygon>();
foreach (Blob blob in blobs)
{
List<IntPoint> leftPoints, rightPoints, edgePoints;
edgePoints = new List<IntPoint>();
// get blob's edge points
BlobCounter.GetBlobsLeftAndRightEdges(blob,
out leftPoints, out rightPoints);
edgePoints.AddRange(leftPoints);
edgePoints.AddRange(rightPoints);
// blob's convex hull
List<IntPoint> hull = hullFinder.FindHull(edgePoints);
hulls.Add(new Polygon(hull));
}
}
}
}
MapFilters:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using AForge;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Imaging.Textures;
using AForge.Math.Geometry;
namespace CDIO.Library
{
public class MapFilters
{
private Bitmap image;
private Bitmap processedImage;
private Rectangle[] rectangels;
public void initialize(Bitmap image)
{
this.image = image;
}
public void process()
{
processedImage = image;
processedImage = applyFilters(processedImage);
processedImage = filterWhite(processedImage);
}
public Bitmap getProcessedImage
{
get
{
return processedImage;
}
}
private Bitmap applyFilters(Bitmap image)
{
image = new ContrastCorrection(2).Apply(image);
image = new GaussianBlur(10, 10).Apply(image);
return image;
}
private Bitmap filterWhite(Bitmap image)
{
Bitmap test = new Bitmap(image.Width, image.Height);
for (int width = 0; width < image.Width; width++)
{
for (int height = 0; height < image.Height; height++)
{
if (image.GetPixel(width, height).R > 200 &&
image.GetPixel(width, height).G > 200 &&
image.GetPixel(width, height).B > 200)
{
test.SetPixel(width, height, Color.White);
}
else
test.SetPixel(width, height, Color.Black);
}
}
return test;
}
}
}
Polygon:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using AForge;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Imaging.Textures;
using AForge.Math.Geometry;
namespace CDIO.Library
{
public class Polygon
{
List<IntPoint> hull;
public Polygon(List<IntPoint> hull)
{
this.hull = hull;
}
public bool inPoly(int x, int y)
{
int i, j = hull.Count - 1;
bool oddNodes = false;
for (i = 0; i < hull.Count; i++)
{
if (hull[i].Y < y && hull[j].Y >= y
|| hull[j].Y < y && hull[i].Y >= y)
{
try
{
if (hull[i].X + (y - hull[i].X) / (hull[j].X - hull[i].X) * (hull[j].X - hull[i].X) < x)
{
oddNodes = !oddNodes;
}
}
catch (DivideByZeroException e)
{
if (0 < x)
{
oddNodes = !oddNodes;
}
}
}
j = i;
}
return oddNodes;
}
public Rectangle getRectangle()
{
int x = -1, y = -1, width = -1, height = -1;
foreach (IntPoint item in hull)
{
if (item.X < x || x == -1)
x = item.X;
if (item.Y < y || y == -1)
y = item.Y;
if (item.X > width || width == -1)
width = item.X;
if (item.Y > height || height == -1)
height = item.Y;
}
return new Rectangle(x, y, width-x, height-y);
}
public Bitmap drawRectangle(Bitmap image)
{
Rectangle rect = getRectangle();
Bitmap clonimage = (Bitmap)image.Clone();
BitmapData data = clonimage.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, image.PixelFormat);
Drawing.FillRectangle (data, rect, getRandomColor());
clonimage.UnlockBits(data);
return clonimage;
}
public Point[] getMap()
{
List<Point> points = new List<Point>();
Rectangle rect = getRectangle();
for (int x = rect.X; x <= rect.X + rect.Width; x++)
{
for (int y = rect.Y; y <= rect.Y + rect.Height; y++)
{
if (inPoly(x, y))
points.Add(new Point(x, y));
}
}
return points.ToArray();
}
public float calculateArea()
{
List<IntPoint> list = new List<IntPoint>();
list.AddRange(hull);
list.Add(hull[0]);
float area = 0.0f;
for (int i = 0; i < hull.Count; i++)
{
area += list[i].X * list[i + 1].Y - list[i].Y * list[i + 1].X;
}
area = area / 2;
if (area < 0)
area = area * -1;
return area;
}
public Bitmap draw(Bitmap image)
{
Bitmap clonimage = (Bitmap)image.Clone();
BitmapData data = clonimage.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, image.PixelFormat);
Drawing.Polygon(data, hull, Color.Red);
clonimage.UnlockBits(data);
return clonimage;
}
static Random random = new Random();
int Color1, Color2, Color3;
public Color getRandomColor()
{
Color1 = random.Next(0, 255);
Color2 = random.Next(0, 255);
Color3 = random.Next(0, 255);
Color color = Color.FromArgb(Color1, Color2, Color3);
Console.WriteLine("R: " + Color1 + " G: " + Color2 + " B: " + Color3 + " = " + color.Name);
return color;
}
}
}
The most straight forward solution is probably to find the corners of each detected blob and then geometrically calculate which point-pairs make up the different sides of the squares.
This assumes that the camera is looking straight down such that a square is actually a square in the image (no perspective distorsion).
I am however a bit curious why you need to know the rotation of the rectangles. In all the example images the rectangles are more or less aligned with the image borders, so a bounding box for a rectangle blob would be very close to what you are trying to find. At least it should be good enough for path finding.
You should be using neural networks.
See: http://en.wikipedia.org/wiki/Neural_network

Categories