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:
Related
I am building a program where I can convert black&white images to a terrain.
I have a working code, but because Unity has a limit on the number of vertices a mesh can have The code doesn't work for big images.
So, I tried to split the image to smaller images and create mesh for each part. it works, but I get weird spacing between my meshes and I am not sure why.
Code:
public static class MeshGenerator
{
private static Transform CreateGameObjectWithMesh(Mesh m)
{
var go = new GameObject();
go.AddComponent<MeshFilter>().mesh = m;
go.AddComponent<MeshRenderer>();
m.RecalculateNormals();
return go.transform;
}
public static void Generate(float[,] heightsData)
{
int width = heightsData.GetLength(0);
int height = heightsData.GetLength(1);
float topLeftX = (width - 1) / -2f;
float topLeftZ = (height - 1) / 2f;
// Just for testing, I am working with 256X256 texture and split it to 4 parts. (would be modified when working)
for (int i = 0; i < 4; i++)
{
var startX = (i % 2) * 128;
var startY = i < 2 ? 0 : 128;
StartMeshCreationThread(mesh =>
{
var t = CreateGameObjectWithMesh(mesh);
t.position = new Vector3(topLeftX + startX + 128f / 2f, 0, topLeftZ - startY - 128f / 2f);
}, startX, startY, 128, 128, heightsData,
new Vector2(topLeftX, topLeftZ), (x, y) => new Vector2(x / (float) width, y / (float) height));
}
}
// Would create a new thread in the future.
private static void StartMeshCreationThread(Action<Mesh> onFinishedCallback, int startX, int startY, int width,
int height, float[,] heightsData, Vector2 orgTopLeft, Func<int, int, Vector2> getUVPosition)
{
var meshData = new MeshData (width, height);
float topLeftX = (width - 1) / -2f;
float topLeftZ = (height - 1) / 2f;
int vertexIndex = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
var vertexPosition = new Vector3(topLeftX + x, heightsData[x + startX, y + startY] * 10f, topLeftZ - y);
meshData.AddVertexData(vertexIndex, vertexPosition, getUVPosition(x + startX, y + startY));
if (x < width - 1 && y < height - 1)
{
meshData.AddTriangle (vertexIndex, vertexIndex + width + 1, vertexIndex + width);
meshData.AddTriangle (vertexIndex + width + 1, vertexIndex, vertexIndex + 1);
}
vertexIndex++;
}
}
onFinishedCallback(meshData.CreateMesh());
}
private class MeshData
{
private readonly Vector3[] vertices;
private readonly Vector2[] uvs;
private readonly int[] triangles;
private int triangleIndex;
public MeshData(int width, int height)
{
vertices = new Vector3[width * height];
uvs = new Vector2[width * height];
triangles = new int[(width - 1) * (height - 1) * 6];
triangleIndex = 0;
}
public void AddVertexData(int index, Vector3 vertexPosition, Vector2 uvPosition)
{
vertices[index] = vertexPosition;
uvs[index] = uvPosition;
}
public void AddTriangle(int v1, int v2, int v3)
{
triangles[triangleIndex + 0] = v1;
triangles[triangleIndex + 1] = v2;
triangles[triangleIndex + 2] = v3;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
var mesh = new Mesh {vertices = vertices, triangles = triangles, uv = uvs};
return mesh;
}
}
}
Result (for 256X256) texture:
(main source)
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;
}
I am trying to implement Hough Line Transform.
Input. I am using the following image as input. This single line is expected to produce only one intersection of sine waves in the output.
Desired behavior. my source code is expected to produce the following output as it was generated by the sample application of AForge framework.
Here, we can see:
the dimension of the output is identical to the input image.
the intersection of sine waves are seen at almost at the center.
the intersection pattern of waves is very small and simple.
Present behavior. My source code is producing the following output which is different than that of the output generated by AForge.
the intersection is not at the center.
the wave patterns are also different.
Why is my code producing a different output?
.
Source Code
I have written the following code myself. The following is a Minimal, Complete, and Verifiable source code.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int inWidth = image.GetLength(0);
int inHeight = image.GetLength(1);
int inWidthHalf = inWidth / 2;
int inHeightHalf = inHeight / 2;
int outWidth = (int)Math.Sqrt(inWidth * inWidth + inHeight * inHeight);
int outHeight = 180;
int outHeightHalf = outHeight / 2;
houghMap = new int[outWidth, outHeight];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < inHeight; y++) //|
{ //|
for (int x = 0; x < inWidth; x++)//<-----------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves. So, it may
// vary from -90 to +90 degrees.
for (int theta = -outHeightHalf; theta < outHeightHalf; theta++)
{
double rad = theta * Math.PI / 180;
// respective radius value is computed
//int radius = (int)Math.Round(Math.Cos(rad) * (x - inWidthHalf) - Math.Sin(rad) * (y - inHeightHalf));
//int radius = (int)Math.Round(Math.Cos(rad) * (x + inWidthHalf) - Math.Sin(rad) * (y + inHeightHalf));
int radius = (int)Math.Round(Math.Cos(rad) * (x) - Math.Sin(rad) * (outHeight - y));
// if the radious value is between 1 and
if ((radius > 0) && (radius <= outWidth))
{
houghMap[radius, theta + outHeightHalf]++;
}
}
}
}
}
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Bitmap bitmap = (Bitmap)pictureBox1.Image as Bitmap;
int[,] intImage = ToInteger(bitmap);
HoughMap houghMap = new HoughMap();
houghMap.image = intImage;
houghMap.Compute();
int[,] normalized = Rescale(houghMap.houghMap);
Bitmap hough = ToBitmap(normalized, bitmap.PixelFormat);
pictureBox2.Image = hough;
}
public static int[,] Rescale(int[,] image)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
int minVal = 0;
int maxVal = 0;
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
double conv = imageCopy[i, j];
minVal = (int)Math.Min(minVal, conv);
maxVal = (int)Math.Max(maxVal, conv);
}
}
int minRange = 0;
int maxRange = 255;
int[,] array2d = new int[Width, Height];
for (int j = 0; j < Height; j++)
{
for (int i = 0; i < Width; i++)
{
array2d[i, j] = (maxRange - minRange) * (imageCopy[i,j] - minVal) / (maxVal - minVal) + minRange;
}
}
return array2d;
}
public int[,] ToInteger(Bitmap input)
{
int Width = input.Width;
int Height = input.Height;
int[,] array2d = new int[Width, Height];
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
Color cl = input.GetPixel(x, y);
int gray = (int)Convert.ChangeType(cl.R * 0.3 + cl.G * 0.59 + cl.B * 0.11, typeof(int));
array2d[x, y] = gray;
}
}
return array2d;
}
public Bitmap ToBitmap(int[,] image, PixelFormat pixelFormat)
{
int[,] imageCopy = (int[,])image.Clone();
int Width = imageCopy.GetLength(0);
int Height = imageCopy.GetLength(1);
Bitmap bitmap = new Bitmap(Width, Height, pixelFormat);
for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
int iii = imageCopy[x, y];
Color clr = Color.FromArgb(iii, iii, iii);
bitmap.SetPixel(x, y, clr);
}
}
return bitmap;
}
}
I have solved the problem from this link. The source code from this link is the best one I have ever came across.
public class HoughMap
{
public int[,] houghMap { get; private set; }
public int[,] image { get; set; }
public void Compute()
{
if (image != null)
{
// get source image size
int Width = image.GetLength(0);
int Height = image.GetLength(1);
int centerX = Width / 2;
int centerY = Height / 2;
int maxTheta = 180;
int houghHeight = (int)(Math.Sqrt(2) * Math.Max(Width, Height)) / 2;
int doubleHeight = houghHeight * 2;
int houghHeightHalf = houghHeight / 2;
int houghWidthHalf = maxTheta / 2;
houghMap = new int[doubleHeight, maxTheta];
// scanning through each (x,y) pixel of the image--+
for (int y = 0; y < Height; y++) //|
{ //|
for (int x = 0; x < Width; x++)//<-------------+
{
if (image[x, y] != 0)//if a pixel is black, skip it.
{
// We are drawing some Sine waves.
// It may vary from -90 to +90 degrees.
for (int theta = 0; theta < maxTheta; theta++)
{
double rad = theta *Math.PI / 180;
// respective radius value is computed
int rho = (int)(((x - centerX) * Math.Cos(rad)) + ((y - centerY) * Math.Sin(rad)));
// get rid of negative value
rho += houghHeight;
// if the radious value is between
// 1 and twice the houghHeight
if ((rho > 0) && (rho <= doubleHeight))
{
houghMap[rho, theta]++;
}
}
}
}
}
}
}
}
Just look at this C++ code, and this C# code. So, complicated and messy that my brain got arrested. Especially, the C++ one. I never anticipated someone to store 2D values in a 1D array.
I am trying to use libtiff.net to read elevation data from a GeoTIFF file.
So far I have mostly just been able to read metadata from the file using the example at libtiff.net's webpage.
But howto read elevation data I do not understand... I tried first reading with Tiff.ReadScanline() as described here but the file I have seems to be stored differently (probably in tiles if I understand it correctly)
Here is the metadata (as far as I have been able to read) (the tiff file is from the danish terrain elevation data set):
Tiff c:\Users***\DTM_1km_6170_500.tif, page 0 has following tags set:
IMAGEWIDTH System.Int32 : 2500
IMAGELENGTH System.Int32 : 2500
BITSPERSAMPLE System.Int16 : 32
COMPRESSION BitMiracle.LibTiff.Classic.Compression : ADOBE_DEFLATE
PHOTOMETRIC BitMiracle.LibTiff.Classic.Photometric : MINISBLACK
STRIPOFFSETS System.UInt64[] : System.UInt64[]
SAMPLESPERPIXEL System.Int16 : 1
STRIPBYTECOUNTS System.UInt64[] : System.UInt64[]
PLANARCONFIG BitMiracle.LibTiff.Classic.PlanarConfig : CONTIG
PREDICTOR BitMiracle.LibTiff.Classic.Predictor : FLOATINGPOINT
TILEWIDTH System.Int32 : 256
TILELENGTH System.Int32 : 256
TILEOFFSETS System.UInt64[] : System.UInt64[]
TILEBYTECOUNTS System.UInt64[] : System.UInt64[]
SAMPLEFORMAT BitMiracle.LibTiff.Classic.SampleFormat : IEEEFP
DATATYPE System.Int16 : 3
GEOTIFF_MODELPIXELSCALETAG System.Int32 : 3 GEOTIFF_MODELPIXELSCALETAG
System.Byte[] : Ù?Ù?
GEOTIFF_MODELTIEPOINTTAG System.Int32 : 6 GEOTIFF_MODELTIEPOINTTAG
System.Byte[] : A ^WA
34735 System.Int32 : 36 34735 System.Byte[] :
± ± #° èd )#
34736 System.Int32 : 3 34736 System.Byte[] :
34737 System.Int32 : 30 34737 System.Byte[] : ETRS89 / UTM zone
32N|ETRS89|
42113 System.Int32 : 6 42113 System.Byte[] : -9999
The code I have written so far is as follows:
namespace GeoTIFFReader
{
public class GeoTIFF
{
private double[,] heightmap;
private double dx;
private double dy;
private double startx;
private double starty;
public GeoTIFF(string fn)
{
using (Tiff tiff = Tiff.Open(fn, "r"))
{
if (tiff == null)
{
// Error - could not open
return;
}
int width = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
int height = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
heightmap = new double[width, height];
FieldValue[] modelPixelScaleTag = tiff.GetField(TiffTag.GEOTIFF_MODELPIXELSCALETAG);
FieldValue[] modelTiePointTag = tiff.GetField(TiffTag.GEOTIFF_MODELTIEPOINTTAG);
byte[] modelPixelScale = modelPixelScaleTag[1].GetBytes();
dx = BitConverter.ToDouble(modelPixelScale, 0);
dy = BitConverter.ToDouble(modelPixelScale, 8) * -1;
byte[] modelTransformation = modelTiePointTag[1].GetBytes();
double originLon = BitConverter.ToDouble(modelTransformation, 24);
double originLat = BitConverter.ToDouble(modelTransformation, 32);
startx = originLon + dx / 2.0;
starty = originLat + dy / 2.0;
double curx = startx;
double cury = starty;
FieldValue[] bitsPerSampleTag = tiff.GetField(TiffTag.BITSPERSAMPLE);
FieldValue[] tilewtag = tiff.GetField(TiffTag.TILEWIDTH);
FieldValue[] tilehtag = tiff.GetField(TiffTag.TILELENGTH);
int tilew = tilewtag[0].ToInt();
int tileh = tilehtag[0].ToInt();
var tile = new byte[tilew*tileh];
//var scanline = new byte[tiff.ScanlineSize()]; Does not work... wrong format
for (int il = 0; il < height; il++)
{
//tiff.ReadScanline(scanline, il); // Load il'th line of data
for (int ir = 0; ir < width; ir++)
{
// Here I would like to read each pixel data that contains elevation in gray-scale in f32 as far I as I understand from metadata
//object value = scanline[ir];
//heightmap[ir, il] = double.Parse(value.ToString());
}
}
Console.WriteLine(heightmap.ToString());
}
}
}
}
So if anyone knows howto extract this data, that would be very much appreciated.
So I stumbled across some hinting that lead me to find an answer to the specific question..:
int tileSize = tiff.TileSize();
for (int iw = 0; iw < nWidth; iw += tilew)
{
for (int ih = 0; ih < nHeight; ih += tileh)
{
byte[] buffer = new byte[tileSize];
tiff.ReadTile(buffer, 0, iw, ih, 0, 0);
for (int itw = 0; itw < tilew; itw++)
{
int iwhm = ih + itw;
if (iwhm > nWidth - 1)
{
break;
}
for (int ith = 0; ith < tileh; ith++)
{
int iyhm = iw + ith;
if (iyhm > nHeight - 1)
{
break;
}
heightMap[iwhm, iyhm] =
BitConverter.ToSingle(buffer, (itw * tileh + ith) * 4);
}
}
}
}
EDIT 2018-09-20:
#Graviton - Sorry for the long response time.. but here is the complete class I have used with a constructor that takes the filename as input and populates the heightMap (but #Nazonokaizijin looks a bit nicer and slimmer):
using System;
using BitMiracle.LibTiff.Classic;
using System.IO;
namespace GeoTIFFReader
{
public class GeoTIFF
{
private float[,] heightMap;
public float[,] HeightMap
{
get { return heightMap; }
private set { heightMap = value; }
}
private int nWidth;
public int NWidth
{
get { return nWidth; }
private set { nWidth = value; }
}
private int nHeight;
public int NHeight
{
get { return nHeight; }
private set { nHeight = value; }
}
private double dW;
public double DW
{
get { return dW; }
private set { dW = value; }
}
private double dH;
public double DH
{
get { return dH; }
private set { dH = value; }
}
private double startW;
public double StartW
{
get { return startW; }
private set { startW = value; }
}
private double startH;
public double StartH
{
get { return startH; }
private set { startH = value; }
}
public GeoTIFF(string fn)
{
using (Tiff tiff = Tiff.Open(fn, "r"))
{
if (tiff == null)
{
// Error - could not open
return;
}
nWidth = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
nHeight = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
heightMap = new float[nWidth, nHeight];
FieldValue[] modelPixelScaleTag = tiff.GetField(TiffTag.GEOTIFF_MODELPIXELSCALETAG);
FieldValue[] modelTiePointTag = tiff.GetField(TiffTag.GEOTIFF_MODELTIEPOINTTAG);
byte[] modelPixelScale = modelPixelScaleTag[1].GetBytes();
dW = BitConverter.ToDouble(modelPixelScale, 0);
dH = BitConverter.ToDouble(modelPixelScale, 8) * -1;
byte[] modelTransformation = modelTiePointTag[1].GetBytes();
double originLon = BitConverter.ToDouble(modelTransformation, 24);
double originLat = BitConverter.ToDouble(modelTransformation, 32);
startW = originLon + dW / 2.0;
startH = originLat + dH / 2.0;
FieldValue[] tileByteCountsTag = tiff.GetField(TiffTag.TILEBYTECOUNTS);
long[] tileByteCounts = tileByteCountsTag[0].TolongArray();
FieldValue[] bitsPerSampleTag = tiff.GetField(TiffTag.BITSPERSAMPLE);
int bytesPerSample = bitsPerSampleTag[0].ToInt() / 8;
FieldValue[] tilewtag = tiff.GetField(TiffTag.TILEWIDTH);
FieldValue[] tilehtag = tiff.GetField(TiffTag.TILELENGTH);
int tilew = tilewtag[0].ToInt();
int tileh = tilehtag[0].ToInt();
int tileWidthCount = nWidth / tilew;
int remainingWidth = nWidth - tileWidthCount * tilew;
if (remainingWidth > 0)
{
tileWidthCount++;
}
int tileHeightCount = nHeight / tileh;
int remainingHeight = nHeight - tileHeightCount * tileh;
if (remainingHeight > 0)
{
tileHeightCount++;
}
int tileSize = tiff.TileSize();
for (int iw = 0; iw < nWidth; iw += tilew)
{
for (int ih = 0; ih < nHeight; ih += tileh)
{
byte[] buffer = new byte[tileSize];
tiff.ReadTile(buffer, 0, iw, ih, 0, 0);
for (int itw = 0; itw < tilew; itw++)
{
int iwhm = ih + itw;
if (iwhm > nWidth - 1)
{
break;
}
for (int ith = 0; ith < tileh; ith++)
{
int iyhm = iw + ith;
if (iyhm > nHeight - 1)
{
break;
}
heightMap[iwhm, iyhm] =
BitConverter.ToSingle(buffer, (itw * tileh + ith) * 4);
}
}
}
}
}
}
}
}
You can do it something like this:
private void ReadTiff()
{
Tiff tiff = Tiff.Open("myfile.tif", "r");
if (tiff == null)
return;
//Get the image size
int imageWidth = tiff.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
int imageHeight = tiff.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
//Get the tile size
int tileWidth = tiff.GetField(TiffTag.TILEWIDTH)[0].ToInt();
int tileHeight = tiff.GetField(TiffTag.TILELENGTH)[0].ToInt();
int tileSize = tiff.TileSize();
//Pixel depth
int depth = tileSize / (tileWidth * tileHeight);
byte[] buffer = new byte[tileSize];
for (int y = 0; y < imageHeight; y += tileHeight)
{
for (int x = 0; x < imageWidth; x += tileWidth)
{
//Read the value and store to the buffer
tiff.ReadTile(buffer, 0, x, y, 0, 0);
for (int kx = 0; kx < tileWidth; kx++)
{
for (int ky = 0; ky < tileHeight; ky++)
{
//Calculate the index in the buffer
int startIndex = (kx + tileWidth * ky) * depth;
if (startIndex >= buffer.Length)
continue;
//Calculate pixel index
int pixelX = x + kx;
int pixelY = y + ky;
if (pixelX >= imageWidth || pixelY >= imageHeight)
continue;
//Get the value for the target pixel
double value = BitConverter.ToSingle(buffer, startIndex);
}
}
}
}
tiff.Close();
}
It has a lib (in c#) complementary to LibTiff that makes the elevation query given a latitude/longitude, available at GeoTiffCOG:
GeoTiff geoTiff = new GeoTiff(file_tiff);
double value = geoTiff.GetElevationAtLatLon(latitude, longitude);
It also supports Cloud Optimized GeoTiff (COG) by adding URI as an argument.
I believe the problem is the PREDICTOR. Instead of placing the data in the file, it is LZW encoding of the differences in the data, with FLOATINGPOINT predictor. This was proprietary with Adobe. I have been searching for code do decrypt PREDICTOR_FLOATINGPOINT, myself. I think this github link
may have library code that will read and decode it.
The class is a bit long and working.
The cone is rotating 360 degrees and any clouds inside the cone area are painting in yellow.
But what I want to do now is that the background image will be in black and only when the cone is rotating over a cloud/s then light them in yellow only when the cone is passing over them one he moved on close the detected clouds. I mean like display the detected clouds only when the cone is passing over them.
What I'm getting today is the original image and when the cone is moving over clouds it's making them yellow:
This screenshot showing what happen when the cone is over clouds.
And this screenshot showing when the cone is not over any clouds:
Now what I want to do is instead displaying the original image in the class I called the variable: bmpWithClouds
I need the bmpWithClouds to scan the clouds but after it I want to use empty black image 512,512 resolution and to make that only when the cone is passing over a cloud/s then show them in yellow.
Then later I want to add my own points(pixels = clouds) make them move random in the black image and when the cone will pass over them to show them.
This is how I'm using the DopplerRadar class in form1 constructor:
DopplerRadar dr = new DopplerRadar();
dr.pb1 = pictureBox1;
dr.Init();
And this is the DopplerRadar class a bit long:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using System.Diagnostics;
namespace mws
{
class DopplerRadar
{
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, int count);
private System.Windows.Forms.Timer timer1;
private Stream mymem;
private Bitmap ConvertedBmp;
private Device D3Ddev = null;
private PresentParameters D3Dpp = null;
private DisplayMode DispMode;
private Sprite D3Dsprite = null;
private Texture backTexture = null;
private Texture scannedCloudsTexture = null;
private byte[] argbValuesOfTransparentTexture;
private float distanceFromCenterPixels;
private float distanceFromCenterKm = 200F;
private List<Point> detectedPoints;
private float[] angleArray, distanceArray;
private double angleCalculation, distance;
private Bitmap bmpnew = new Bitmap(512, 512);
private int bytes = 2048 * 512;
public System.Windows.Forms.PictureBox pb1;
public void Init()
{
timer1 = new System.Windows.Forms.Timer();
timer1.Interval = 10;
timer1.Enabled = false;
timer1.Tick += Timer1_Tick;
ConvertedBmp = ConvertTo24(#"c:\temp\anim3.gif");
mymem = ToStream(ConvertedBmp, ImageFormat.Bmp);
distanceFromCenterPixels = (float)(/*183d*/ ((double)200 / 1.09289617486) * (double)distanceFromCenterKm / 200d);
argbValuesOfTransparentTexture = new byte[bytes];
InitializeDirectX(pb1);
FindPoints();
//initialize angleArray
angleArray = new float[detectedPoints.Count];
distanceArray = new float[detectedPoints.Count];
for (int i = 0; i < detectedPoints.Count; i++)
{
CalculateAngleAndDistance(detectedPoints[i].X, detectedPoints[i].Y, out angleCalculation, out distance);
angleArray[i] = (float)angleCalculation;
distanceArray[i] = (float)distance;
}
timer1.Enabled = true;
}
static float angleF_ = 0.0F;
private void Timer1_Tick(object sender, EventArgs e)
{
if (angleF_ > 360F)
{
angleF_ -= 360F;
}
ReturnTexture(scannedCloudsTexture, detectedPoints, angleArray, angleF_, bmpnew);
DisplayOnScreen(angleF_);
// To change direction to change += to -=
// To change speed to raise the value 1.0d
angleF_ += 1.0F;
}
private Bitmap ConvertTo24(string inputFileName)
{
Stopwatch sw = new Stopwatch();
sw = Stopwatch.StartNew();
Bitmap bmpIn = (Bitmap)Bitmap.FromFile(inputFileName);
Bitmap converted = new Bitmap(bmpIn.Width, bmpIn.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(converted))
{
// Prevent DPI conversion
g.PageUnit = GraphicsUnit.Pixel;
// Draw the image
g.DrawImageUnscaled(bmpIn, 0, 0);
}
//converted.Save(outputFileName, ImageFormat.Bmp);
sw.Stop();
return converted;
}
public static Stream ToStream(Image image, ImageFormat formaw)
{
var stream = new MemoryStream();
image.Save(stream, formaw);
stream.Position = 0;
return stream;
}
public Boolean InitializeDirectX(System.Windows.Forms.PictureBox pb1)
{
DispMode = Manager.Adapters[Manager.Adapters.Default.Adapter].CurrentDisplayMode;
D3Dpp = new PresentParameters();
D3Dpp.BackBufferFormat = DispMode.Format;
D3Dpp.PresentFlag = PresentFlag.LockableBackBuffer;
D3Dpp.SwapEffect = SwapEffect.Discard;
D3Dpp.PresentationInterval = PresentInterval.One; //wait for vertical sync. Synchronizes the painting with
//monitor refresh rate for smoooth animation
D3Dpp.Windowed = true; //the application has borders
try
{
D3Ddev = new Device(Manager.Adapters.Default.Adapter, DeviceType.Hardware, pb1.Handle,
CreateFlags.SoftwareVertexProcessing, D3Dpp);
//D3Ddev.VertexFormat = CustomVertex.PositionColored.Format;
D3Ddev.RenderState.Lighting = false;
D3Ddev.RenderState.CullMode = Cull.CounterClockwise;
//load imagesBmp to panelTexture
//panelTexture = Texture.FromBitmap(D3Ddev, imagesBmp, Usage.Dynamic, Pool.Default)
backTexture = TextureLoader.FromStream(D3Ddev, mymem);
//scannerTexture = TextureLoader.FromFile(D3Ddev, #"D:\Buttons\Radar\radar.png");
scannedCloudsTexture = new Texture(D3Ddev, 512, 512, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default);
//sprite is used to draw the texture
D3Dsprite = new Sprite(D3Ddev);
return true;
}
catch
{
return false;
}
}
Bitmap bmpn;
float angle = 0;
private void DisplayOnScreen(float angleF)
{
if (angle < 360)
{
bmpn = new Bitmap(512, 512);
angle++;
}
else
{
angle = 361;
}
Surface backbuffer;
Brush myBrush = new SolidBrush(Color.FromArgb(110, 0, 255, 0)); //semi transparent color to draw the rotating cone
Graphics g;
//clear the backbuffer with Color.FromArgb(56, 56, 56). This is the double buffer mechanism. Drawing to offscreen
//backbuffer and in the end flipping it to main one which is our panelContainer
D3Ddev.Clear(ClearFlags.Target, Color.FromArgb(56, 56, 56), 1, 0);
D3Ddev.BeginScene();
//Draw Sprites
//////////////////////////////////////////////////////
D3Dsprite.Begin(SpriteFlags.AlphaBlend);
// bitmap with clouds
D3Dsprite.Draw2D(backTexture, new PointF(0, 0), 0F, new PointF(0F, 0F), Color.White);
//the part of clouds that are inside the cone
D3Dsprite.Draw2D(scannedCloudsTexture, new PointF(0F, 0F), 0F, new PointF(0F, 0F), Color.White);
//rotate cone
//D3Dsprite.Draw2D(scannerTexture, new PointF(104.5F, 0F), angle, new PointF(256F, 255F), Color.White);
D3Dsprite.Flush();
D3Dsprite.End();
//////////////////////////////////////////////////////
//Draw the cone.
using (backbuffer = D3Ddev.GetBackBuffer(0, 0, BackBufferType.Mono))
{
using (g = backbuffer.GetGraphics())
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPie(myBrush, 256F - distanceFromCenterPixels, 255F - distanceFromCenterPixels,
distanceFromCenterPixels * 2F, distanceFromCenterPixels * 2F, angleF - 23F, 46F);
if (angle <= 360)
{
}
}
}
D3Ddev.EndScene();
D3Ddev.Present(); //performs the flipping
}
private void CalculateAngleAndDistance(int x, int y, out double angle, out double distance)
{
Double dbl = -1.0d;
Point center = new Point(256, 255);
distance = Math.Sqrt((double)((center.Y - y) * (center.Y - y) + (center.X - x) * (center.X - x)));
if (y == center.Y && x > center.X)
{
dbl = 0d;
angle = dbl;
return;
}
else if (x == center.X && y > center.Y)
{
dbl = 90d;
angle = dbl;
return;
}
else if (y == center.Y && x < center.X)
{
dbl = 180d;
angle = dbl;
return;
}
else if (x == center.X && y < center.Y)
{
dbl = 279d;
angle = dbl;
return;
}
else if (x == center.X && y == center.Y)
{
angle = dbl;
return;
}
if (x > center.X && y > center.Y) //1
{
dbl = Math.Atan(((double)y - (double)center.Y) / ((double)x - (double)center.X));
dbl = 180d * dbl / Math.PI;
}
else if (x < center.X && y > center.Y) //2
{
dbl = Math.Atan(((double)y - (double)center.Y) / ((double)center.X - (double)x));
dbl = 180d * dbl / Math.PI;
dbl = 180d - dbl;
}
else if (x < center.X && y < center.Y) //3
{
dbl = Math.Atan(((double)center.Y - (double)y) / ((double)center.X - (double)x));
dbl = 180d * dbl / Math.PI;
dbl += 180d;
}
else //4
{
dbl = Math.Atan(((double)center.Y - (double)y) / ((double)x - (double)center.X));
dbl = 180d * dbl / Math.PI;
dbl = 360d - dbl;
}
angle = dbl;
}
private void ReturnTexture(Texture texture_take, List<Point> lstPnt, float[] anglArr, float angle, Bitmap bmpNew)
{
int i, j, stride = 2048;
float angleBefore, angleAfter;
GraphicsStream textureStream;
Boolean bl = false;
if (bmpNew.Width != 512 && bmpNew.Height != 512)
throw new Exception("Bitmaps must be of same size.");
//sets texture to complete transparent
unsafe
{
fixed (byte* p = argbValuesOfTransparentTexture)
{
MemSet((IntPtr)p, 0x0, argbValuesOfTransparentTexture.Length);
}
}
angleAfter = angle + 23F;
if (angleAfter >= 360F)
{
angleAfter -= 360F;
}
angleBefore = angleAfter - 46;
if (angleBefore < 0F)
{
angleBefore += 360F;
bl = true;
}
BitmapData bmD = bmpNew.LockBits(new Rectangle(0, 0, bmpNew.Width, bmpNew.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
unsafe
{
byte* p = (byte*)bmD.Scan0.ToPointer();
//checks all points and draws yellow only those who are inside the cone
for (i = 0; i < lstPnt.Count - 1; i++)
{
if (anglArr[i] == -1F)
{
continue;
}
if (bl == true)
{
if (anglArr[i] <= angleAfter || anglArr[i] >= angleBefore) //if point angle is inside cone. Cone angle is 46 degrees
{
if (distanceArray[i] <= distanceFromCenterPixels)
{
j = lstPnt[i].Y * stride + lstPnt[i].X * 4;
//yellow
argbValuesOfTransparentTexture[j + 0] = (byte)0;
argbValuesOfTransparentTexture[j + 1] = (byte)255;
argbValuesOfTransparentTexture[j + 2] = (byte)255;
argbValuesOfTransparentTexture[j + 3] = (byte)255;
p[j] = (byte)0;
p[j + 1] = (byte)0;
p[j + 2] = (byte)255;
p[j + 3] = (byte)255;
}
}
}
else
{
if (anglArr[i] <= angleAfter && anglArr[i] >= angleBefore) //if point angle is inside cone. Cone angle is 46 degrees
{
if (distanceArray[i] <= distanceFromCenterPixels)
{
j = lstPnt[i].Y * stride + lstPnt[i].X * 4;
//yellow
argbValuesOfTransparentTexture[j + 0] = (byte)0;
argbValuesOfTransparentTexture[j + 1] = (byte)255;
argbValuesOfTransparentTexture[j + 2] = (byte)255;
argbValuesOfTransparentTexture[j + 3] = (byte)255;
p[j] = (byte)0;
p[j + 1] = (byte)0;
p[j + 2] = (byte)255;
p[j + 3] = (byte)255;
}
}
}
}
}
//if (angle <= 360)
// pictureBox1.Image.Save(#"c:\coneimages\" + angle + ".gif");
bmpNew.UnlockBits(bmD);
{
using (textureStream = texture_take.LockRectangle(0, LockFlags.None))
{
textureStream.Write(argbValuesOfTransparentTexture);
texture_take.UnlockRectangle(0);
}
//if (angle <= 360)
// pictureBox1.Image.Save(#"c:\coneimages\" + angle + ".gif");
}
}
private void FindPoints()
{
//Bitmap bmptest;
GraphicsPath gp = new GraphicsPath();
int x, y, p, j, wdthHght;
int bytes;
//byte error_ = 5;
byte[] rgbValuesWithClouds;
byte[] rgbValuesWithoutClouds;
IntPtr ptr;
Rectangle rect;
BitmapData bitmap_Data;
Bitmap bmpWithClouds; //No memory is allocated
Bitmap bmpWithoutClouds; //No memory is allocated
gp.AddEllipse(new RectangleF(73, 72, 367, 367));
//gp.CloseFigure();
//using the using statement, bmpWithClouds bitmap is automatically disposed at the end of statement. No memory leaks :)
using (bmpWithClouds = new Bitmap(mymem))//#"D:\MyWeatherStation-Images-And-Icons\radartobmp.bmp")) //24 bit bitmap
{
rect = new Rectangle(0, 0, bmpWithClouds.Width, bmpWithClouds.Height);
wdthHght = bmpWithClouds.Width;
//Lock bitmap to copy its color information fast
bitmap_Data = bmpWithClouds.LockBits(rect, ImageLockMode.ReadWrite, bmpWithClouds.PixelFormat);
ptr = bitmap_Data.Scan0;
bytes = bitmap_Data.Stride * bmpWithClouds.Height;
rgbValuesWithClouds = new byte[bytes];
//copy color information to rgbValues array
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValuesWithClouds, 0, bytes);
//we are done copying so unlock bitmap. We dont need it anymore
bmpWithClouds.UnlockBits(bitmap_Data);
}
//using the using statement, bmpWithClouds bitmap is automatically disposed at the end of statement. No memory leaks :)
using (bmpWithoutClouds = new Bitmap(
#"D:\C-Sharp\Download File\Downloading-File-Project-Version-012\Downloading File\bin\x86\Release\WithoutClouds.bmp"))//su + "\\WithoutClouds.bmp")) //24 bit bitmap
{
rect = new Rectangle(0, 0, bmpWithoutClouds.Width, bmpWithoutClouds.Height);
//Lock bitmap to copy its color information fast
bitmap_Data = bmpWithoutClouds.LockBits(rect, ImageLockMode.ReadWrite, bmpWithoutClouds.PixelFormat);
ptr = bitmap_Data.Scan0;
bytes = bitmap_Data.Stride * bmpWithoutClouds.Height;
rgbValuesWithoutClouds = new byte[bytes];
//copy color information to rgbValues array
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValuesWithoutClouds, 0, bytes);
//we are done copying so unlock bitmap. We dont need it anymore
bmpWithoutClouds.UnlockBits(bitmap_Data);
}
// Each position in these arrays, rgbValuesWithoutClouds and rgbValuesWithClouds, corresponds a color. eg
// First pixel Second pixel Third pixel Forth pixel .... // bitmaps
// B G R B G R B G R B G R .... // rgbValues arrays
//bmptest = new Bitmap(512, 512);
detectedPoints = new List<Point>();
for (y = 0; y < wdthHght; y++)
{
j = 0;
for (x = 0; x < wdthHght; x++)
{
p = y * wdthHght * 3 + j;
if (rgbValuesWithClouds[p] != rgbValuesWithoutClouds[p])
{
detectedPoints.Add(new Point(x, y));
//bmptest.SetPixel(x, y, Color.Red);
}
j += 3;
}
}
}
}
}