I am using Cudafy.NET to perform image processing on the GPU. I have set up my class along with the first function (PerformBarrelCorrection running on the CPU) to set up the multiple threads to perform the logic calculation for each pixel in the image.
However every time I launch the function on the GPU it throws an exception:
An exception of type 'Cudafy.Host.CudafyHostException' occurred in
Cudafy.NET.dll but was not handled in user code
Additional information: CUDA.NET exception: ErrorInvalidValue.
Here is the class in its entirety commented to show the line on which the exception is thrown.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cudafy;
using Cudafy.Host;
using Cudafy.Translator;
using System.Drawing;
using System.IO;
namespace AForgeTrial
{
class GPUProcessing
{
public CudafyModule km;
public GPGPU gpu;
public GPUProcessing() {
km = CudafyTranslator.Cudafy();
gpu = CudafyHost.GetDevice(CudafyModes.Target, CudafyModes.DeviceId);
gpu.LoadModule(km);
}
public Bitmap PerformBarrelCorrection(Bitmap source, double strength, float zoom, int imageWidth, int imageHeight)
{
byte[] bmp = ImageToByteArray2(source);
int halfWidth = imageWidth / 2;
int halfHeight = imageHeight / 2;
double correctionRadius = Math.Sqrt(((imageWidth * imageWidth) + (imageHeight * imageHeight)) / strength);
byte[] dev_src_bmp = gpu.CopyToDevice(bmp);
byte[] dst_bmp = new byte[bmp.Length];
byte[] dev_dst_bmp = gpu.Allocate<byte>(dst_bmp);
double[] correctPass = new double[1];
correctPass[0] = correctionRadius;
double[] dev_correctionRadius = gpu.CopyToDevice<double>(correctPass);
float[] zoomPass = new float[1];
zoomPass[0] = zoom;
float[] dev_zoom = gpu.CopyToDevice<float>(zoomPass);
int[] halfWidthPass = new int[1];
halfWidthPass[0] = halfWidth;
int[] dev_halfWidth = gpu.CopyToDevice<int>(halfWidthPass);
int[] halfHeightPass = new int[1];
halfHeightPass[0] = imageHeight;
int[] dev_halfHeight = gpu.CopyToDevice<int>(halfHeightPass);
//int blksize = ((bmp.Length / 3) / 128) + 1;
// EXCEPTION HAPPENS ON THE LINE BELOW
gpu.Launch((bmp.Length / 3), 1).BarrelCorrectionSingleOperation(dev_src_bmp, dev_dst_bmp, dev_correctionRadius, dev_zoom, dev_halfWidth, dev_halfHeight);
gpu.CopyFromDevice(dev_dst_bmp, dst_bmp);
// Convert dst_bmp to Bitmap and return it
Bitmap result;
using (MemoryStream ms = new MemoryStream(dst_bmp))
{
result = new Bitmap(ms);
}
return result;
}
[Cudafy]
public static void BarrelCorrectionSingleOperation(GThread thread, byte[] src_bmp, byte[] dst_bmp, double[] correctionRadius, float[] zoom, int[] halfWidth, int[] halfHeight)
{
// Move a single byte from source to destination or fill if required
}
public static byte[] ImageToByteArray(Bitmap img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
public static byte[] ImageToByteArray2(Bitmap img)
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
stream.Close();
byteArray = stream.ToArray();
}
return byteArray;
}
public void Close() {
gpu.FreeAll();
}
}
}
Anyone know anything about this?? Thanks in advance.
Solved my own question for anyone interested although its very niche, my code was full of holes.. This successfully performs real-time lens correction using Cudafy.
public void PerformBarrelCorrection(double strength, float zoom)
{
CheckIsSet();
int halfWidth = Width / 2;
int halfHeight = Height / 2;
double correctionRadius = Math.Sqrt(((Width * Width) + (Height * Height)) / strength);
_gpu.Launch(Width, Height).BarrelCorrectionSingleOperation(_gdata.SourceImage, _gdata.ResultImage, correctionRadius, zoom, halfWidth, halfHeight);
}
[Cudafy]
public static void BarrelCorrectionSingleOperation(GThread thread, byte[] src_bmp, byte[] dst_bmp, double correctionRadius, float zoom, int halfWidth, int halfHeight)
{
// Move a single byte from source to destination or fill if required
int x = thread.blockIdx.x;
int y = thread.threadIdx.x;
int newX = x - halfWidth;
int newY = y - halfHeight;
double distance = Math.Sqrt((newX * newX) + (newY * newY));
double r = distance / correctionRadius;
double theta;
if (r == 0)
{
theta = 1;
}
else
{
theta = Math.Atan(r) / r;
}
int sourceX = (int)(halfWidth + theta * newX * zoom);
int sourceY = (int)(halfHeight + theta * newY * zoom);
dst_bmp[(y * ((halfWidth * 2) * 4)) + (x * 4)] = src_bmp[(sourceY * ((halfWidth * 2) * 4)) + (sourceX * 4)];
dst_bmp[(y * (((halfWidth * 2) * 4)) + (x * 4)) + 1] = src_bmp[((sourceY * ((halfWidth * 2) * 4)) + (sourceX * 4)) + 1];
dst_bmp[(y * (((halfWidth * 2) * 4)) + (x * 4)) + 2] = src_bmp[((sourceY * ((halfWidth * 2) * 4)) + (sourceX * 4)) + 2];
dst_bmp[(y * (((halfWidth * 2) * 4)) + (x * 4)) + 3] = src_bmp[((sourceY * ((halfWidth * 2) * 4)) + (sourceX * 4)) + 3];
}
Related
I came across the following code taken from here:
using System;
using System.Drawing;
class Program
{
static void Main()
{
Bitmap img1 = new Bitmap("Lenna50.jpg");
Bitmap img2 = new Bitmap("Lenna100.jpg");
if (img1.Size != img2.Size)
{
Console.Error.WriteLine("Images are of different sizes");
return;
}
float diff = 0;
for (int y = 0; y < img1.Height; y++)
{
for (int x = 0; x < img1.Width; x++)
{
diff += (float)Math.Abs(img1.GetPixel(x, y).R - img2.GetPixel(x, y).R) / 255;
diff += (float)Math.Abs(img1.GetPixel(x, y).G - img2.GetPixel(x, y).G) / 255;
diff += (float)Math.Abs(img1.GetPixel(x, y).B - img2.GetPixel(x, y).B) / 255;
}
}
Console.WriteLine("diff: {0} %", 100 * diff / (img1.Width * img1.Height * 3));
}
}
Unfortunately, this is really slow. Is anyone aware of a faster way of calculating the distance between 2 images? Thanks!
To provide some more context as well. I am working on something like this:
https://rogerjohansson.blog/2008/12/07/genetic-programming-evolution-of-mona-lisa/
I evolve SVGs which are then translated into a Bitmap and compared to the target image.
Just came across the aforgenet library - see for example:
enter link description here
PS:
I start to rewrite the above using LockBits. The code below is my current attempt but I am a bit stuck. Please note, that bmp1 is the 'target picture' and does not really change - hence the copying can be factored out/only needs to be done once. The Bitmap bmp2 is passed in and to be compared with bmp1 (there are 100s of bmp2s). Ultimately, I would like to determine the similarity between bmp1 and bmp2 using some distance (e.g. Euclidean distance of bytes?). Any pointers regarding this and how to speed the code up would be very much appreciated. Thanks!
public double Compare(Bitmap bmp1, Bitmap bmp2)
{
BitmapData bitmapData1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadWrite, bmp1.PixelFormat);
BitmapData bitmapData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadWrite, bmp2.PixelFormat);
IntPtr ptr1 = bitmapData1.Scan0;
int bytes1 = bitmapData1.Stride * bmp1.Height;
byte[] rgbValues1 = new byte[bytes1];
byte[] r1 = new byte[bytes1 / 3];
byte[] g1 = new byte[bytes1 / 3];
byte[] b1 = new byte[bytes1 / 3];
Marshal.Copy(ptr1, rgbValues1, 0, bytes1);
bmp1.UnlockBits(bitmapData1);
IntPtr ptr2 = bitmapData2.Scan0;
int bytes2 = bitmapData2.Stride * bmp2.Height;
byte[] rgbValues2 = new byte[bytes2];
byte[] r2 = new byte[bytes2 / 3];
byte[] g2 = new byte[bytes2 / 3];
byte[] b2 = new byte[bytes2 / 3];
Marshal.Copy(ptr2, rgbValues2, 0, bytes2);
bmp2.UnlockBits(bitmapData2);
int stride = bitmapData1.Stride;
for (int column = 0; column < bitmapData1.Height; column++)
{
for (int row = 0; row < bitmapData1.Width; row++)
{
//????
}
}
return 0;
}
PPS:
I (think I) made some more progress. The following code seems to work:
using System.Drawing;
using System.Drawing.Imaging;
using Color = System.Drawing.Color;
namespace ClassLibrary1
{
public unsafe class BitmapComparer : IBitmapComparer
{
private readonly Color[] _targetBitmapColors;
private readonly int _width;
private readonly int _height;
private readonly int _maxPointerLength;
private readonly PixelFormat _pixelFormat;
public BitmapComparer(Bitmap targetBitmap)
{
_width = targetBitmap.Width;
_height = targetBitmap.Height;
_maxPointerLength = _width * _height;
_pixelFormat = targetBitmap.PixelFormat;
_targetBitmapColors = new Color[_maxPointerLength];
var bData = targetBitmap.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadWrite, _pixelFormat);
var scan0 = (byte*) bData.Scan0.ToPointer();
for (var i = 0; i < _maxPointerLength; i += 4)
{
_targetBitmapColors[i] = Color.FromArgb(scan0[i + 2], scan0[i + 1], scan0[i + 0]);
}
targetBitmap.UnlockBits(bData);
}
// https://rogerjohansson.blog/2008/12/09/genetic-programming-mona-lisa-faq/
public double Compare(Bitmap bitmapToCompareWith)
{
var bData = bitmapToCompareWith.LockBits(new Rectangle(0, 0, _width, _height), ImageLockMode.ReadWrite, _pixelFormat);
var scan0 = (byte*) bData.Scan0.ToPointer();
double distance = 0;
for (var i = 0; i < _maxPointerLength; i += 4)
{
distance +=
( ((_targetBitmapColors[i].R - scan0[i + 2]) * (_targetBitmapColors[i].R - scan0[i + 2]))
+ ((_targetBitmapColors[i].G - scan0[i + 1]) * (_targetBitmapColors[i].G - scan0[i + 1]))
+ ((_targetBitmapColors[i].B - scan0[i + 0]) * (_targetBitmapColors[i].B - scan0[i + 0])));
}
bitmapToCompareWith.UnlockBits(bData);
return distance;
}
}
}
Using all pixels always will be time-consuming. What if you use a randomly selected pixels sample of the images. Also, you can apply hierarchical image granularity. In this way, you will get more information about the details presented in the images.
I am also working on a similar project. It is available in GitHub under the name Ellipses-Image-Approximator.
Something like this:
package eu.veldsoft.ellipses.image.approximator;
import java.awt.image.BufferedImage;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
/**
* Compare to raster images by using Euclidean distance between the pixels but
* in probabilistic sampling on hierarchical image detailization.
*
* #author Todor Balabanov
*/
class HierarchicalProbabilisticImageComparator implements ImageComparator {
/** A pseudo-random number generator instance. */
private static final Random PRNG = new Random();
/**
* Euclidean distance color comparator instance.
*/
private static final ColorComparator EUCLIDEAN = new EuclideanColorComparator();
/** Recursive descent depth level. */
private int depthLevel = 1;
/**
* Size of the sample in percentages from the size of the population (from
* 0.0 to 1.0).
*/
private double samplePercent = 0.1;
/** A supportive array for the first image pixels. */
private int aPixels[] = null;
/** A supportive array for the second image pixels. */
private int bPixels[] = null;
/**
* Constructor without parameters for default members' values.
*/
public HierarchicalProbabilisticImageComparator() {
this(1, 0.1);
}
/**
* Constructor with all parameters.
*
* #param depthLevel
* Recursive descent depth level.
* #param samplePercent
* Size of the sample in percentages from the size of the
* population (from 0.0 to 1.0).
*/
public HierarchicalProbabilisticImageComparator(int depthLevel,
double samplePercent) {
super();
this.depthLevel = depthLevel;
this.samplePercent = samplePercent;
}
private double distance(int width, int level, int minX, int minY, int maxX,
int maxY) {
/*
* At the bottom of the recursive descent, distance is zero, and
* descending is canceled.
*/
if (level > depthLevel) {
return 0;
}
/* Rectangle's boundaries should be observed. */
if (maxX <= minX || maxY <= minY) {
return 0;
}
/*
* Sample size calculated according formula.
*
* https://www.surveymonkey.com/mp/sample-size-calculator/
*/
int sampleSize = (int) ((maxX - minX) * (maxY - minY) * samplePercent);
/* Generate unique indices of pixels with the size of the sample. */
Set<Integer> indices = new HashSet<Integer>();
while (indices.size() < sampleSize) {
int x = minX + PRNG.nextInt(maxX - minX + 1);
int y = minY + PRNG.nextInt(maxY - minY + 1);
indices.add(y * width + x);
}
/* The Euclidean distance of the randomly selected pixels. */
double sum = 0;
for (int index : indices) {
sum += EUCLIDEAN.distance(aPixels[index], bPixels[index]);
}
/* Do a recursive descent. */
return (sum / sampleSize) * level
+ distance(width, level + 1, minX, minY,
maxX - (maxX - minX) / 2, maxY - (maxY - minY) / 2)
+ distance(width, level + 1, maxX - (maxX - minX) / 2, minY,
maxX, maxY - (maxY - minY) / 2)
+ distance(width, level + 1, minX, maxY - (maxY - minY) / 2,
maxX - (maxX - minX) / 2, maxY)
+ distance(width, level + 1, maxX - (maxX - minX) / 2,
maxY - (maxY - minY) / 2, maxX, maxY);
}
/**
* {#inheritDoc}
*/
#Override
public double distance(BufferedImage a, BufferedImage b) {
if (a.getWidth() != b.getWidth()) {
throw new RuntimeException("Images width should be identical!");
}
if (a.getHeight() != b.getHeight()) {
throw new RuntimeException("Images height should be identical!");
}
aPixels = a.getRGB(0, 0, a.getWidth(), a.getHeight(), null, 0,
a.getWidth());
bPixels = b.getRGB(0, 0, b.getWidth(), b.getHeight(), null, 0,
b.getWidth());
/* Do a recursive calculation. */
return distance(Math.min(a.getWidth(), b.getWidth()), 1, 0, 0,
Math.min(a.getWidth() - 1, b.getWidth() - 1),
Math.min(a.getHeight() - 1, b.getHeight() - 1));
}
}
As others have pointed out, you can use BitMap.LockBits and use pointers instead of GetPixel. The following runs about 200 times faster than the original approach:
static float CalculateDifference(Bitmap bitmap1, Bitmap bitmap2)
{
if (bitmap1.Size != bitmap2.Size)
{
return -1;
}
var rectangle = new Rectangle(0, 0, bitmap1.Width, bitmap1.Height);
BitmapData bitmapData1 = bitmap1.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap1.PixelFormat);
BitmapData bitmapData2 = bitmap2.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap2.PixelFormat);
float diff = 0;
var byteCount = rectangle.Width * rectangle.Height * 3;
unsafe
{
// scan to first byte in bitmaps
byte* pointer1 = (byte*)bitmapData1.Scan0.ToPointer();
byte* pointer2 = (byte*)bitmapData2.Scan0.ToPointer();
for (int x = 0; x < byteCount; x++)
{
diff += (float)Math.Abs(*pointer1 - *pointer2) / 255;
pointer1++;
pointer2++;
}
}
bitmap1.UnlockBits(bitmapData1);
bitmap2.UnlockBits(bitmapData2);
return 100 * diff / byteCount;
}
Project: intelligence scissors.
The first part of the project is to load an image (in the form of RGBPixel 2d array) then to construct a graph of weights to use it later to determine the shortest path between 2 points on the image (the 2 points will be determined by an anchor and a free point..In short i will have the source and the destination points).
I have a function that open the image and return a RGBPixel 2d array already made.Now image is loaded i want to construct the graph to do the i should use a function called CalculatePixelEnergies here is the code
public static Vector2D CalculatePixelEnergies(int x, int y, RGBPixel[,] ImageMatrix)
{
if (ImageMatrix == null) throw new Exception("image is not set!");
Vector2D gradient = CalculateGradientAtPixel(x, y, ImageMatrix);
double gradientMagnitude = Math.Sqrt(gradient.X * gradient.X + gradient.Y * gradient.Y);
double edgeAngle = Math.Atan2(gradient.Y, gradient.X);
double rotatedEdgeAngle = edgeAngle + Math.PI / 2.0;
Vector2D energy = new Vector2D();
energy.X = Math.Abs(gradientMagnitude * Math.Cos(rotatedEdgeAngle));
energy.Y = Math.Abs(gradientMagnitude * Math.Sin(rotatedEdgeAngle));
return energy;
}
This function use CalculateGradientAtPixel, Here is the code in case you want it.
private static Vector2D CalculateGradientAtPixel(int x, int y, RGBPixel[,] ImageMatrix)
{
Vector2D gradient = new Vector2D();
RGBPixel mainPixel = ImageMatrix[y, x];
double pixelGrayVal = 0.21 * mainPixel.red + 0.72 * mainPixel.green + 0.07 * mainPixel.blue;
if (y == GetHeight(ImageMatrix) - 1)
{
//boundary pixel.
for (int i = 0; i < 3; i++)
{
gradient.Y = 0;
}
}
else
{
RGBPixel downPixel = ImageMatrix[y + 1, x];
double downPixelGrayVal = 0.21 * downPixel.red + 0.72 * downPixel.green + 0.07 * downPixel.blue;
gradient.Y = pixelGrayVal - downPixelGrayVal;
}
if (x == GetWidth(ImageMatrix) - 1)
{
//boundary pixel.
gradient.X = 0;
}
else
{
RGBPixel rightPixel = ImageMatrix[y, x + 1];
double rightPixelGrayVal = 0.21 * rightPixel.red + 0.72 * rightPixel.green + 0.07 * rightPixel.blue;
gradient.X = pixelGrayVal - rightPixelGrayVal;
}
return gradient;
}
In my code of graph construction i decided to make a 2d double array to hold the weights, here what i do but it seems to be a wrong construction
public static double [,] calculateWeights(RGBPixel[,] ImageMatrix)
{
double[,] weights = new double[1000, 1000];
int height = ImageOperations.GetHeight(ImageMatrix);
int width = ImageOperations.GetWidth(ImageMatrix);
for (int y = 0; y < height - 1; y++)
{
for (int x = 0; x < width - 1; x++)
{
Vector2D e;
e = ImageOperations.CalculatePixelEnergies(x, y, ImageMatrix);
weights[y + 1, x] = 1 / e.X;
weights[y, x + 1] = 1 / e.Y;
}
}
return weights;
}
an example for an image
an other example for an image
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 5 years ago.
Improve this question
For my assignment I need to make to make a color picker that looks like this:
I've got the wheel part but can't figure out how to draw the triangle.
using System.Drawing;
using System.Drawing.Imaging;
void Main()
{
double hue = 3.3;
double sat = 0.4;
double val = 0.9;
var wheel = new ColorPicker(400);
var img = wheel.DrawImage(hue, sat, val);
using (var g = Graphics.FromImage(img))
{
var pen = val < 0.5 ? Pens.White : Pens.Black;
var wheelPosition = wheel.GetWheelPosition(hue);
g.DrawEllipse(pen, (float)wheelPosition.X - 5, (float)wheelPosition.Y - 5, 10, 10);
var trianglePosition = wheel.GetTrianglePosition(sat, val);
g.DrawEllipse(pen, (float)trianglePosition.X - 5, (float)trianglePosition.Y - 5, 10, 10);
}
img.Dump(); // LINQPad extension method
}
public class ColorPicker
{
public int Size { get; }
public int CenterX => Size / 2;
public int CenterY => Size / 2;
public int InnerRadius => Size * 5 / 12;
public int OuterRadius => Size / 2;
public ColorPicker(int size = 400)
{
Size = size;
}
public enum Area
{
Outside,
Wheel,
Triangle
}
public struct PickResult
{
public Area Area { get; set; }
public double? Hue { get; set; }
public double? Sat { get; set; }
public double? Val { get; set; }
}
public PickResult Pick(double x, double y)
{
var distanceFromCenter = Math.Sqrt((x - CenterX) * (x - CenterX) + (y - CenterY) * (y - CenterY));
var sqrt3 = Math.Sqrt(3);
if (distanceFromCenter > OuterRadius)
{
// Outside
return new PickResult { Area = Area.Outside };
}
else if (distanceFromCenter > InnerRadius)
{
// Wheel
var angle = Math.Atan2(y - CenterY, x - CenterX) + Math.PI / 2;
if (angle < 0) angle += 2 * Math.PI;
var hue = angle;
return new PickResult { Area = Area.Wheel, Hue = hue };
}
else
{
// Inside
var x1 = (x - CenterX) * 1.0 / InnerRadius;
var y1 = (y - CenterY) * 1.0 / InnerRadius;
if (0 * x1 + 2 * y1 > 1) return new PickResult { Area = Area.Outside };
else if (sqrt3 * x1 + (-1) * y1 > 1) return new PickResult { Area = Area.Outside };
else if (-sqrt3 * x1 + (-1) * y1 > 1) return new PickResult { Area = Area.Outside };
else
{
// Triangle
var sat = (1 - 2 * y1) / (sqrt3 * x1 - y1 + 2);
var val = (sqrt3 * x1 - y1 + 2) / 3;
return new PickResult { Area = Area.Triangle, Sat = sat, Val = val };
}
}
}
public Image DrawImage(double hue = 0.0, double sat = 1.0, double val = 1.0)
{
var img = new Bitmap(Size, Size, PixelFormat.Format32bppArgb);
for (int y = 0; y < Size; y++)
{
for (int x = 0; x < Size; x++)
{
Color color;
var result = Pick(x, y);
if (result.Area == Area.Outside)
{
// Outside
color = Color.Transparent;
}
else if (result.Area == Area.Wheel)
{
// Wheel
color = HSV(result.Hue.Value, sat, val, 1);
}
else
{
// Triangle
color = HSV(hue, result.Sat.Value, result.Val.Value, 1);
}
img.SetPixel(x, y, color);
}
}
return img;
}
private Color HSV(double hue, double sat, double val, double alpha)
{
var chroma = val * sat;
var step = Math.PI / 3;
var interm = chroma * (1 - Math.Abs((hue / step) % 2.0 - 1));
var shift = val - chroma;
if (hue < 1 * step) return RGB(shift + chroma, shift + interm, shift + 0, alpha);
if (hue < 2 * step) return RGB(shift + interm, shift + chroma, shift + 0, alpha);
if (hue < 3 * step) return RGB(shift + 0, shift + chroma, shift + interm, alpha);
if (hue < 4 * step) return RGB(shift + 0, shift + interm, shift + chroma, alpha);
if (hue < 5 * step) return RGB(shift + interm, shift + 0, shift + chroma, alpha);
return RGB(shift + chroma, shift + 0, shift + interm, alpha);
}
private Color RGB(double red, double green, double blue, double alpha)
{
return Color.FromArgb(
Math.Min(255, (int)(alpha * 256)),
Math.Min(255, (int)(red * 256)),
Math.Min(255, (int)(green * 256)),
Math.Min(255, (int)(blue * 256)));
}
public PointD GetWheelPosition(double hue)
{
double middleRadius = (InnerRadius + OuterRadius) / 2;
return new PointD
{
X = CenterX + middleRadius * Math.Sin(hue),
Y = CenterY - middleRadius * Math.Cos(hue)
};
}
public PointD GetTrianglePosition(double sat, double val)
{
var sqrt3 = Math.Sqrt(3);
return new PointD
{
X = CenterX + InnerRadius * (2 * val - sat * val - 1) * sqrt3 / 2,
Y = CenterY + InnerRadius * (1 - 3 * sat * val) / 2
};
}
}
public class PointD
{
public double X { get; set; }
public double Y { get; set; }
}
Result:
I found some great code here on SO and have implemented it in my app and although it is "working", the blur effect is not strong enough to actually realise (without focusing very hard on it) that it has actually blurred anything.
The code is (quite long, sorry):
Bitmap screenshot = null;
void BlurForm()
{
/*
The best time (I assume) is to change the blur when the user as left the window,
as opposed to doing it when they want to open the window, since this may be a
little intensive.
*/
screenshot = null;
blurred.Image = null; // This variable is the PictureBox that's on the Form
screenshot = Screenshot.TakeSnapshot(this);
BitmapFilter.GaussianBlur(screenshot, 4);
blurred.Image = screenshot;
blurred.SendToBack();
}
public class ConvMatrix
{
public int TopLeft = 0, TopMid = 0, TopRight = 0;
public int MidLeft = 0, Pixel = 1, MidRight = 0;
public int BottomLeft = 0, BottomMid = 0, BottomRight = 0;
public int Factor = 1;
public int Offset = 0;
public void SetAll(int nVal)
{
TopLeft = TopMid = TopRight = MidLeft = Pixel = MidRight = BottomLeft = BottomMid = BottomRight = nVal;
}
}
public class BitmapFilter
{
private static bool Conv3x3(Bitmap b, ConvMatrix m)
{
// Avoid divide by zero errors
if (0 == m.Factor) return false;
Bitmap bSrc = (Bitmap)b.Clone();
// GDI+ still lies to us - the return format is BGR, NOT RGB.
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
int stride2 = stride * 2;
System.IntPtr Scan0 = bmData.Scan0;
System.IntPtr SrcScan0 = bmSrc.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
byte* pSrc = (byte*)(void*)SrcScan0;
int nOffset = stride + 6 - b.Width * 3;
int nWidth = b.Width - 2;
int nHeight = b.Height - 2;
int nPixel;
for (int y = 0; y < nHeight; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
nPixel = ((((pSrc[2] * m.TopLeft) + (pSrc[5] * m.TopMid) + (pSrc[8] * m.TopRight) +
(pSrc[2 + stride] * m.MidLeft) + (pSrc[5 + stride] * m.Pixel) + (pSrc[8 + stride] * m.MidRight) +
(pSrc[2 + stride2] * m.BottomLeft) + (pSrc[5 + stride2] * m.BottomMid) + (pSrc[8 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[5 + stride] = (byte)nPixel;
nPixel = ((((pSrc[1] * m.TopLeft) + (pSrc[4] * m.TopMid) + (pSrc[7] * m.TopRight) +
(pSrc[1 + stride] * m.MidLeft) + (pSrc[4 + stride] * m.Pixel) + (pSrc[7 + stride] * m.MidRight) +
(pSrc[1 + stride2] * m.BottomLeft) + (pSrc[4 + stride2] * m.BottomMid) + (pSrc[7 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[4 + stride] = (byte)nPixel;
nPixel = ((((pSrc[0] * m.TopLeft) + (pSrc[3] * m.TopMid) + (pSrc[6] * m.TopRight) +
(pSrc[0 + stride] * m.MidLeft) + (pSrc[3 + stride] * m.Pixel) + (pSrc[6 + stride] * m.MidRight) +
(pSrc[0 + stride2] * m.BottomLeft) + (pSrc[3 + stride2] * m.BottomMid) + (pSrc[6 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[3 + stride] = (byte)nPixel;
p += 3;
pSrc += 3;
}
p += nOffset;
pSrc += nOffset;
}
}
b.UnlockBits(bmData);
bSrc.UnlockBits(bmSrc);
return true;
}
public static bool GaussianBlur(Bitmap b, int nWeight /* default to 4*/)
{
ConvMatrix m = new ConvMatrix();
m.SetAll(1);
m.Pixel = nWeight;
m.TopMid = m.MidLeft = m.MidRight = m.BottomMid = 2;
m.Factor = nWeight + 12;
return BitmapFilter.Conv3x3(b, m);
}
}
class Screenshot
{
public static Bitmap TakeSnapshot(Control ctl)
{
Bitmap bmp = new Bitmap(ctl.Size.Width, ctl.Size.Height);
using (Graphics g = System.Drawing.Graphics.FromImage(bmp))
{
g.CopyFromScreen(
ctl.PointToScreen(ctl.ClientRectangle.Location),
new Point(0, 0), ctl.ClientRectangle.Size
);
}
return bmp;
}
}
I've tried changing every single value in there and it doesn't matter which value(s) I change, I just can't get it to have a stronger blur effect. How can I increase the strength of the Gaussian Blur?
The Form is 80% Opaque, so the blur effect you would see is the windows wallpaper or whatever is behind the form - but blurred. Only problem (as I've said) is that the blur is not strong at all.
After just glancing at the code, it looks to me like the blur filter is hardcoded to only operate in a 3x3 area at a time, severely restricting how blurred it can make an image.
Unless I'm mistaken, the only way to significantly increase the amount of blurring is to find a more sophisticated implementation.
Is there any open source C# code or library to present a graphical waveform given a byte array?
This is as open source as it gets:
public static void DrawNormalizedAudio(ref float[] data, PictureBox pb,
Color color)
{
Bitmap bmp;
if (pb.Image == null)
{
bmp = new Bitmap(pb.Width, pb.Height);
}
else
{
bmp = (Bitmap)pb.Image;
}
int BORDER_WIDTH = 5;
int width = bmp.Width - (2 * BORDER_WIDTH);
int height = bmp.Height - (2 * BORDER_WIDTH);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Black);
Pen pen = new Pen(color);
int size = data.Length;
for (int iPixel = 0; iPixel < width; iPixel++)
{
// determine start and end points within WAV
int start = (int)((float)iPixel * ((float)size / (float)width));
int end = (int)((float)(iPixel + 1) * ((float)size / (float)width));
float min = float.MaxValue;
float max = float.MinValue;
for (int i = start; i < end; i++)
{
float val = data[i];
min = val < min ? val : min;
max = val > max ? val : max;
}
int yMax = BORDER_WIDTH + height - (int)((max + 1) * .5 * height);
int yMin = BORDER_WIDTH + height - (int)((min + 1) * .5 * height);
g.DrawLine(pen, iPixel + BORDER_WIDTH, yMax,
iPixel + BORDER_WIDTH, yMin);
}
}
pb.Image = bmp;
}
This function will produce something like this:
This takes an array of samples in floating-point format (where all sample values range from -1 to +1). If your original data is actually in the form of a byte[] array, you'll have to do a little bit of work to convert it to float[]. Let me know if you need that, too.
Update: since the question technically asked for something to render a byte array, here are a couple of helper methods:
public float[] FloatArrayFromStream(System.IO.MemoryStream stream)
{
return FloatArrayFromByteArray(stream.GetBuffer());
}
public float[] FloatArrayFromByteArray(byte[] input)
{
float[] output = new float[input.Length / 4];
for (int i = 0; i < output.Length; i++)
{
output[i] = BitConverter.ToSingle(input, i * 4);
}
return output;
}
Update 2: I forgot there's a better way to do this:
public float[] FloatArrayFromByteArray(byte[] input)
{
float[] output = new float[input.Length / 4];
Buffer.BlockCopy(input, 0, output, 0, input.Length);
return output;
}
I'm just so in love with for loops, I guess.
I modified MusiGenesis's solution a little bit.
This gave me a much better result, especially with house music :)
public static Bitmap DrawNormalizedAudio(List<float> data, Color foreColor, Color backColor, Size imageSize)
{
Bitmap bmp = new Bitmap(imageSize.Width, imageSize.Height);
int BORDER_WIDTH = 0;
float width = bmp.Width - (2 * BORDER_WIDTH);
float height = bmp.Height - (2 * BORDER_WIDTH);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(backColor);
Pen pen = new Pen(foreColor);
float size = data.Count;
for (float iPixel = 0; iPixel < width; iPixel += 1)
{
// determine start and end points within WAV
int start = (int)(iPixel * (size / width));
int end = (int)((iPixel + 1) * (size / width));
if (end > data.Count)
end = data.Count;
float posAvg, negAvg;
averages(data, start, end, out posAvg, out negAvg);
float yMax = BORDER_WIDTH + height - ((posAvg + 1) * .5f * height);
float yMin = BORDER_WIDTH + height - ((negAvg + 1) * .5f * height);
g.DrawLine(pen, iPixel + BORDER_WIDTH, yMax, iPixel + BORDER_WIDTH, yMin);
}
}
return bmp;
}
private static void averages(List<float> data, int startIndex, int endIndex, out float posAvg, out float negAvg)
{
posAvg = 0.0f;
negAvg = 0.0f;
int posCount = 0, negCount = 0;
for (int i = startIndex; i < endIndex; i++)
{
if (data[i] > 0)
{
posCount++;
posAvg += data[i];
}
else
{
negCount++;
negAvg += data[i];
}
}
posAvg /= posCount;
negAvg /= negCount;
}
with adapted code from robby and using Graphics.Fill/DrawClosedCurve with antialiasing, I get a pretty good looking result.
here's the code:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Soundfingerprinting.Audio.Services
{
public static class AudioVisualizationService
{
public class WaveVisualizationConfiguration
{
public Nullable<Color> AreaColor { get; set; }
public Nullable<Color> EdgeColor { get; set; }
public int EdgeSize { get; set; }
public Nullable<Rectangle> Bounds { get; set; }
public double Overlap { get; set; }
public int Step { get; set; }
}
public static void DrawWave(float[] data, Bitmap bitmap, WaveVisualizationConfiguration config = null)
{
Color areaColor = Color.FromArgb(0x7F87CEFA);// Color.LightSkyBlue; semi transparent
Color edgeColor = Color.DarkSlateBlue;
int edgeSize = 2;
int step = 2;
double overlap = 0.10f; // would better use a windowing function
Rectangle bounds = Rectangle.FromLTRB(0, 0, bitmap.Width, bitmap.Height);
if (config != null)
{
edgeSize = config.EdgeSize;
if (config.AreaColor.HasValue)
areaColor = config.AreaColor.GetValueOrDefault();
if (config.EdgeColor.HasValue)
edgeColor = config.EdgeColor.GetValueOrDefault();
if (config.Bounds.HasValue)
bounds = config.Bounds.GetValueOrDefault();
step = Math.Max(1, config.Step);
overlap = config.Overlap;
}
float width = bounds.Width;
float height = bounds.Height;
using (Graphics g = Graphics.FromImage(bitmap))
{
Pen edgePen = new Pen(edgeColor);
edgePen.LineJoin = LineJoin.Round;
edgePen.Width = edgeSize;
Brush areaBrush = new SolidBrush(areaColor);
float size = data.Length;
PointF[] topCurve = new PointF[(int)width / step];
PointF[] bottomCurve = new PointF[(int)width / step];
int idx = 0;
for (float iPixel = 0; iPixel < width; iPixel += step)
{
// determine start and end points within WAV
int start = (int)(iPixel * (size / width));
int end = (int)((iPixel + step) * (size / width));
int window = end - start;
start -= (int)(overlap * window);
end += (int)(overlap * window);
if (start < 0)
start = 0;
if (end > data.Length)
end = data.Length;
float posAvg, negAvg;
averages(data, start, end, out posAvg, out negAvg);
float yMax = height - ((posAvg + 1) * .5f * height);
float yMin = height - ((negAvg + 1) * .5f * height);
float xPos = iPixel + bounds.Left;
if (idx >= topCurve.Length)
idx = topCurve.Length - 1;
topCurve[idx] = new PointF(xPos, yMax);
bottomCurve[bottomCurve.Length - idx - 1] = new PointF(xPos, yMin);
idx++;
}
PointF[] curve = new PointF[topCurve.Length * 2];
Array.Copy(topCurve, curve, topCurve.Length);
Array.Copy(bottomCurve, 0, curve, topCurve.Length, bottomCurve.Length);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillClosedCurve(areaBrush, curve, FillMode.Winding, 0.15f);
if (edgeSize > 0)
g.DrawClosedCurve(edgePen, curve, 0.15f, FillMode.Winding);
}
}
private static void averages(float[] data, int startIndex, int endIndex, out float posAvg, out float negAvg)
{
posAvg = 0.0f;
negAvg = 0.0f;
int posCount = 0, negCount = 0;
for (int i = startIndex; i < endIndex; i++)
{
if (data[i] > 0)
{
posCount++;
posAvg += data[i];
}
else
{
negCount++;
negAvg += data[i];
}
}
if (posCount > 0)
posAvg /= posCount;
if (negCount > 0)
negAvg /= negCount;
}
}
}
In NAudio, there is code to draw audio waveforms in both WinForms and WPF. Have a look at the demo projects for examples of how to use it.
I've been a fan of ZedGraph for many years and have used it to display all kinds of data in various projects.
The following sample code graphs an array of doubles varying between -1 and 1:
void DisplayWaveGraph(ZedGraphControl graphControl, double[] waveData)
{
var pane = graphControl.GraphPane;
pane.Chart.Border.IsVisible = false;
pane.Chart.Fill.IsVisible = false;
pane.Fill.Color = Color.Black;
pane.Margin.All = 0;
pane.Title.IsVisible = false;
pane.XAxis.IsVisible = false;
pane.XAxis.Scale.Max = waveData.Length - 1;
pane.XAxis.Scale.Min = 0;
pane.YAxis.IsVisible = false;
pane.YAxis.Scale.Max = 1;
pane.YAxis.Scale.Min = -1;
var timeData = Enumerable.Range(0, waveData.Length)
.Select(i => (double) i)
.ToArray();
pane.AddCurve(null, timeData, waveData, Color.Lime, SymbolType.None);
graphControl.AxisChange();
}
The above sample mimics the style of an audio editor by suppressing the axes and changing the colors to produce the following: