I am trying to work out how to use ConvNetShar correctly to learn some shapes in some images. I have test data consisting of black shapes on white backgrounds, all these shapes belong to 1 class (road) and I have a mixture of images with that and without. I have tried to adapt the 2d demo and it does seem to learn but when it tests it fails...
Below is my code...just wondering if anyone has a working example with images?
Thanks
(code just updated) seems to be learning..still failing tests though...
using System;
using System.Collections.Generic;
using ConvNetSharp.Core;
using ConvNetSharp.Core.Layers.Double;
using ConvNetSharp.Core.Training;
using ConvNetSharp.Volume;
using ConvNetSharp.Volume.Double;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using NDNBackpropNnTrainer;
namespace ClassifyImageDemo
{
internal class Program
{
private static void ClassifyImage()
{
string inputFolder = GetInputFolder();
var filelist = Directory.EnumerateFiles(inputFolder, "*.png");
List<List<double>> matrix = new List<List<double>>();
List<int> expetedList = new List<int>();
int looper = 0;
int width = 1;
int height = 1;
foreach (var fileInImage in filelist)
{
matrix.Add(new List<double>());
Bitmap source = (Bitmap) Image.FromFile(fileInImage);
List<double> innerList = new List<double>();
using (var bmp = new LockBitmap(source))
{
width = bmp.Width;
height = bmp.Height;
for (var y = 0; y < bmp.Height; y++)
{
for (var x = 0; x < bmp.Width; x++)
{
var color = bmp.GetPixel(x, y);
int myR = color.R;
int myG = color.G;
int myB = color.B;
int total = myR + myG + myB;
//Adds new sub List
innerList.Add(total);
}
}
}
matrix[looper]=innerList; //Add values to the sub List at index 0
looper = looper + 1 ;
byte[] myImageRaw = File.ReadAllBytes(fileInImage);
int len = myImageRaw.Length;
var lastOneByte = (byte) myImageRaw[len - 1];
expetedList.Add(lastOneByte);
}
var net = new Net<double>();
net.AddLayer(new InputLayer(1, 1, (width * height)));
net.AddLayer(new FullyConnLayer(6));
net.AddLayer(new TanhLayer());
net.AddLayer(new FullyConnLayer(2));
net.AddLayer(new TanhLayer());
net.AddLayer(new FullyConnLayer(2));
net.AddLayer(new SoftmaxLayer(2));
// Data
var data = new List<double[]>();
var labels = new List<int>();
foreach (var lstInputs in matrix)
{
double[] arrayIn = lstInputs.ToArray();
data.Add(arrayIn);
}
foreach (var lbl in expetedList)
{
labels.Add(lbl);
}
var n = labels.Count;
var trainer = new SgdTrainer<double>(net) { LearningRate = 0.01, L2Decay = 0.001, BatchSize = n };
double loss = 1.0;
// Training
while (loss > 0.00001)
{
loss = ClassifyImageUpdate(width, height, n, data, trainer, labels);
}
// Testing
var netx = BuilderInstance.Volume.From(new double[(width * height) * n], new Shape(1, 1, (width*height), n));
for (var ix = 0; ix < n; ix++)
{
int subLooper2 = 0;
foreach (var dtA2 in data[ix])
{
netx.Set(0, 0, subLooper2, ix, dtA2);
subLooper2 = subLooper2 + 1;
}
}
var result = net.Forward(netx);
var c = net.GetPrediction();
var accurate = c[0] == labels[0];
Console.ReadLine();
}
private static string GetInputFolder()
{
return #"D:\temp\filtered\blackroads\subset_500";
}
private static double ClassifyImageUpdate(int width, int height, int n, List<double[]> data, TrainerBase<double> trainer, List<int> labels)
{
var avloss = 0.0;
int dimensions = width * height;
var netx = BuilderInstance.Volume.SameAs(new Shape(1, 1, dimensions, n));
var hotLabels = BuilderInstance.Volume.SameAs(new Shape(1, 1, 1, n));
for (var ix = 0; ix < n; ix++)
{
hotLabels.Set(0, 0, 0, ix, labels[ix]);
int subLooper = 0;
foreach (var dtA in data[ix])
{
netx.Set(0, 0, subLooper, ix, dtA);
subLooper = subLooper + 1;
}
// netx.Set(0, 0, 1, ix, data[ix][1]);
}
for (var iters = 0; iters < 50; iters++)
{
trainer.Train(netx, hotLabels);
avloss += trainer.Loss;
}
avloss /= 50.0;
Console.WriteLine(" Loss:" + avloss);
return avloss;
}
private static void Main(string[] args)
{
ClassifyImage();
}
}
}
Related
Difficulty in source conversion
I have looked up the internal functions and related documentation supported by the DLL, but I have difficulty finding matching functions or data types.
[Calibration Find value]
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using OpenCvSharp;`
namespace Project_NFT
{
class CalibrateCamera
{
public const string ImageCalibration = "C:\\Users\\JL\\Documents\\Visual Studio 2008\\Projects\\Project NET\\Project NFT\\Image/{0:D2}.jpg";
public CalibrateCamera()
{
const int IMAGE_NUM = 3;
const int PAT_ROW = 7;
const int PAT_COL = 10;
const int PAT_SIZE = PAT_ROW * PAT_COL;
const int ALL_POINTS = IMAGE_NUM * PAT_SIZE;
const float CHESS_SIZE = 24.0f;
CvSize patternSize = new CvSize(PAT_COL, PAT_ROW);
IplImage[] srcImg = new IplImage[IMAGE_NUM];
for (int i = 0; i < IMAGE_NUM; i++)
{
string path = string.Format(ImageCalibration, i);
srcImg[i] = new IplImage(path, LoadMode.Color);
}
CvPoint3D32f[] objects = new CvPoint3D32f[ALL_POINTS];
for (int i = 0; i < IMAGE_NUM; i++)
{
for (int j = 0; j < PAT_ROW; j++)
{
for (int k = 0; k < PAT_COL; k++)
{
objects[(i * PAT_SIZE) + (j * PAT_COL) + k] = new CvPoint3D32f
{
X = j * CHESS_SIZE,
Y = k * CHESS_SIZE,
Z = 0.0f
};
}
}
}
CvMat objectPoints = new CvMat(ALL_POINTS, 3, MatrixType.F32C1, objects);
int foundNum = 0;
List<CvPoint2D32f> allCorners = new List<CvPoint2D32f>(ALL_POINTS);
int[] pCount = new int[IMAGE_NUM];
using (CvWindow window = new CvWindow("Calibration", WindowMode.AutoSize))
{
for (int i = 0; i < IMAGE_NUM; i++)
{
CvPoint2D32f[] corners;
bool found = Cv.FindChessboardCorners(srcImg[i], patternSize, out corners);
Debug.Print("{0:D2}...", i);
if (found)
{
Debug.Print("ok");
foundNum++;
}
else
{
Debug.Print("fail");
}
using (IplImage srcGray = new IplImage(srcImg[i].Size, BitDepth.U8, 1))
{
Cv.CvtColor(srcImg[i], srcGray, ColorConversion.BgrToGray);
Cv.FindCornerSubPix(srcGray, corners, corners.Length, new CvSize(3, 3), new CvSize(-1, -1), new CvTermCriteria(20, 0.03));
Cv.DrawChessboardCorners(srcImg[i], patternSize, corners, found);
pCount[i] = corners.Length;
window.ShowImage(srcImg[i]);
Cv.WaitKey(0);
}
allCorners.AddRange(corners);
}
if (foundNum != IMAGE_NUM)
{
Debug.Assert(false);
}
}
CvMat imagePoints = new CvMat(ALL_POINTS, 1, MatrixType.F32C2, allCorners.ToArray());
CvMat pointCounts = new CvMat(IMAGE_NUM, 1, MatrixType.S32C1, pCount);
CvMat intrinsic, distortion, rotation, translation;
Cv.CalibrateCamera2(objectPoints, imagePoints, pointCounts, new CvSize(640, 480), out intrinsic, out distortion, out rotation, out translation, CalibrationFlag.Default);
CvMat subImagePoints, subObjectPoints;
Cv.GetRows(imagePoints, out subImagePoints, 0, PAT_SIZE);
Cv.GetRows(objectPoints, out subObjectPoints, 0, PAT_SIZE);
Cv.FindExtrinsicCameraParams2(subObjectPoints, subImagePoints, intrinsic, distortion, out rotation, out translation);
using (CvFileStorage fs = new CvFileStorage("camera.xml", null, FileStorageMode.Write))
{
fs.Write("intrinsic", intrinsic);
fs.Write("rotation", rotation);
fs.Write("translation", translation);
fs.Write("distortion", distortion);
}
foreach (IplImage img in srcImg)
{
img.Dispose();
}
Console.WriteLine(File.ReadAllText("camera.xml"));
Console.Read();
}
}
}
[Calibration Apply Value]
CvMat intrinsic, distortion, extrinsic;
CvFileNode param;
using (CvFileStorage fs = new CvFileStorage("camera.xml", null, FileStorageMode.Read))
{
param = Cv.GetFileNodeByName (fs, null, "intrinsic");
intrinsic = fs.Read<CvMat>(param);
param = Cv.GetFileNodeByName (fs, null, "distortion");
distortion = fs.Read<CvMat>(param);
}
Cv.FindExtrinsicCameraParams2(object_points, image_points,intrinsic, distortion,
out rotation_vector, out translation_vector);
The source is the Camera Calibration reference, which is difficult to convert
I have to calculate the spectrum values of an audio.
I used aForge's FFT in Sources/Math/FourierTransform.cs and I used an example of sampling with 16 samples as used in this video to check the results with excel (I tested the results in a spreadsheet like in the video).
FFT:
public enum Direction
{
Forward = 1,
Backward = -1
};
private const int minLength = 2;
private const int maxLength = 16384;
private const int minBits = 1;
private const int maxBits = 14;
private static int[][] reversedBits = new int[maxBits][];
private static Complex[,][] complexRotation = new Complex[maxBits, 2][];
static void Main(string[] args)
{
var Data = new Complex[16];
Data[0] = new Complex(0, 0);
Data[1] = new Complex((float)0.998027, 0);
Data[2] = new Complex((float)0.125333, 0);
Data[3] = new Complex((float)-0.98229, 0);
Data[4] = new Complex((float)-0.24869, 0);
Data[5] = new Complex((float)0.951057, 0);
Data[6] = new Complex((float)0.368125, 0);
Data[7] = new Complex((float)-0.90483, 0);
Data[8] = new Complex((float)-0.48175, 0);
Data[9] = new Complex((float)0.844328, 0);
Data[10] = new Complex((float)0.587785, 0);
Data[11] = new Complex((float)-0.77051, 0);
Data[12] = new Complex((float)-0.68455, 0);
Data[13] = new Complex((float)0.684547, 0);
Data[14] = new Complex((float)0.770513, 0);
Data[15] = new Complex((float)-0.58779, 0);
FFT(Data, Direction.Forward);
for (int a = 0; a <= Data.Length - 1; a++)
{
Console.WriteLine(Data[a].Re.ToString());
}
Console.ReadLine();
}
public static void FFT(Complex[] data, Direction direction)
{
int n = data.Length;
int m = Tools.Log2(n);
// reorder data first
ReorderData(data);
// compute FFT
int tn = 1, tm;
for (int k = 1; k <= m; k++)
{
Complex[] rotation = GetComplexRotation(k, direction);
tm = tn;
tn <<= 1;
for (int i = 0; i < tm; i++)
{
Complex t = rotation[i];
for (int even = i; even < n; even += tn)
{
int odd = even + tm;
Complex ce = data[even];
Complex co = data[odd];
double tr = co.Re * t.Re - co.Im * t.Im;
double ti = co.Re * t.Im + co.Im * t.Re;
data[even].Re += tr;
data[even].Im += ti;
data[odd].Re = ce.Re - tr;
data[odd].Im = ce.Im - ti;
}
}
}
if (direction == Direction.Forward)
{
for (int i = 0; i < n; i++)
{
data[i].Re /= (double)n;
data[i].Im /= (double)n;
}
}
}
private static int[] GetReversedBits(int numberOfBits)
{
if ((numberOfBits < minBits) || (numberOfBits > maxBits))
throw new ArgumentOutOfRangeException();
// check if the array is already calculated
if (reversedBits[numberOfBits - 1] == null)
{
int n = Tools.Pow2(numberOfBits);
int[] rBits = new int[n];
// calculate the array
for (int i = 0; i < n; i++)
{
int oldBits = i;
int newBits = 0;
for (int j = 0; j < numberOfBits; j++)
{
newBits = (newBits << 1) | (oldBits & 1);
oldBits = (oldBits >> 1);
}
rBits[i] = newBits;
}
reversedBits[numberOfBits - 1] = rBits;
}
return reversedBits[numberOfBits - 1];
}
private static Complex[] GetComplexRotation(int numberOfBits, Direction direction)
{
int directionIndex = (direction == Direction.Forward) ? 0 : 1;
// check if the array is already calculated
if (complexRotation[numberOfBits - 1, directionIndex] == null)
{
int n = 1 << (numberOfBits - 1);
double uR = 1.0;
double uI = 0.0;
double angle = System.Math.PI / n * (int)direction;
double wR = System.Math.Cos(angle);
double wI = System.Math.Sin(angle);
double t;
Complex[] rotation = new Complex[n];
for (int i = 0; i < n; i++)
{
rotation[i] = new Complex(uR, uI);
t = uR * wI + uI * wR;
uR = uR * wR - uI * wI;
uI = t;
}
complexRotation[numberOfBits - 1, directionIndex] = rotation;
}
return complexRotation[numberOfBits - 1, directionIndex];
}
// Reorder data for FFT using
private static void ReorderData(Complex[] data)
{
int len = data.Length;
// check data length
if ((len < minLength) || (len > maxLength) || (!Tools.IsPowerOf2(len)))
throw new ArgumentException("Incorrect data length.");
int[] rBits = GetReversedBits(Tools.Log2(len));
for (int i = 0; i < len; i++)
{
int s = rBits[i];
if (s > i)
{
Complex t = data[i];
data[i] = data[s];
data[s] = t;
}
}
}
These are the results after the transformation:
Output FFT results: Excel FFT results:
0,0418315622955561 0,669305
0,0533257974328085 0,716163407
0,137615673627316 0,908647001
0,114642731070279 1,673453043
0,234673940537634 7,474988602
0,0811255020953362 0,880988382
0,138088891589122 0,406276784
0,0623766891658306 0,248854492
0,0272978749126196 0,204227
0,0124250144575261 0,248854492
0,053787064184711 0,406276784
0,00783331226557493 0,880988382
0,0884368745610118 7,474988602
0,0155431246384978 1,673453043
0,0301093757152557 0,908647001
0 0,716163407
The results are not at all similar. Where is it wrong?
Is the implementation of complex (Data) wrong or is the FFT method wrong or other?
Thanks in advance!
First, the resulting FFT is a complex function in general. You're only displaying the real parts in your code but the thing you're comparing to is displaying the magnitudes, so of course they're going to be different: you're comparing apples to oranges.
When you use magnitudes and compare apples to apples, you should get this:
for (int a = 0; a <= Data.Length - 1; a++)
{
Console.WriteLine(Data[a].Magnitude.ToString());
}
...
0.0418315622955561
0.0447602132472683
0.0567904388057513
0.104590813761862
0.46718679147454
0.0550617784710375
0.025392294285886
0.0155534081359397
0.0127641875296831
0.0155534081359397
0.025392294285886
0.0550617784710375
0.46718679147454
0.104590813761862
0.0567904388057513
0.0447602132472683
That looks a little better -- it has the same symmetry property as the Excel output and there appear to be peaks in the same locations.
It almost looks like the scale is off. If I divide each element by the corresponding element from the Excel output, I get:
16
16
16
16
16
16
16
16
16
16
16
16
16
16
16
16
So your results are pretty much correct, just off by a scaling factor.
You're dividing everything by n in the last step of your FFT:
if (direction == Direction.Forward)
{
for (int i = 0; i < n; i++)
{
data[i].Re /= (double)n;
data[i].Im /= (double)n;
}
}
This is conventionally done for the inverse transform, not the forward transform.
In summary, changing the output from Data[a].Re to Data[a].Magnitude and changing the condition at the end of FFT from if (direction == Direction.Forward) to if (direction == Direction.Backward), I get this output:
0.669304996728897
0.716163411956293
0.908647020892022
1.67345302018979
7.47498866359264
0.880988455536601
0.406276708574176
0.248854530175035
0.20422700047493
0.248854530175035
0.406276708574176
0.880988455536601
7.47498866359264
1.67345302018979
0.908647020892022
0.716163411956293
which matches the Excel output.
I am currently exploring neural networks and machine learning and I implemented a basic neural network in c#. Now I wanted to test my back propagation training algorithm with the MNIST database. Although I am having serious trouble reading the files correctly.
Spoiler the code is currently very badly optimised for performance. My aim currently is to grasp the subject and get a structured view how things work before I start throwing out my data structures for faster ones.
To train the network I want to feed it a custom TrainingSet data structure:
[Serializable]
public class TrainingSet
{
public Dictionary<List<double>, List<double>> data = new Dictionary<List<double>, List<double>>();
}
Keys will be my input data (784 pixels per entry(image) which will represent the greyscale values in range from 0 to 1). Values will be my output data (10 entries representing the digits from 0-9 with all entries on 0 except the exspected one at 1)
Now I want to read the MNIST database according to this contract. I am currentl on my 2nd try which is inspired by this blogpost: https://jamesmccaffrey.wordpress.com/2013/11/23/reading-the-mnist-data-set-with-c/ . Sadly it is still producing the same nonsense as my first try scattering the pixels in a strange pattern:
My current reading algorithm:
public static TrainingSet GenerateTrainingSet(FileInfo imagesFile, FileInfo labelsFile)
{
MnistImageView imageView = new MnistImageView();
imageView.Show();
TrainingSet trainingSet = new TrainingSet();
List<List<double>> labels = new List<List<double>>();
List<List<double>> images = new List<List<double>>();
using (BinaryReader brLabels = new BinaryReader(new FileStream(labelsFile.FullName, FileMode.Open)))
{
using (BinaryReader brImages = new BinaryReader(new FileStream(imagesFile.FullName, FileMode.Open)))
{
int magic1 = brImages.ReadBigInt32(); //Reading as BigEndian
int numImages = brImages.ReadBigInt32();
int numRows = brImages.ReadBigInt32();
int numCols = brImages.ReadBigInt32();
int magic2 = brLabels.ReadBigInt32();
int numLabels = brLabels.ReadBigInt32();
byte[] pixels = new byte[numRows * numCols];
// each image
for (int imageCounter = 0; imageCounter < numImages; imageCounter++)
{
List<double> imageInput = new List<double>();
List<double> exspectedOutput = new List<double>();
for (int i = 0; i < 10; i++) //generate empty exspected output
exspectedOutput.Add(0);
//read image
for (int p = 0; p < pixels.Length; p++)
{
byte b = brImages.ReadByte();
pixels[p] = b;
imageInput.Add(b / 255.0f); //scale in 0 to 1 range
}
//read label
byte lbl = brLabels.ReadByte();
exspectedOutput[lbl] = 1; //modify exspected output
labels.Add(exspectedOutput);
images.Add(imageInput);
//Debug view showing parsed image.......................
Bitmap image = new Bitmap(numCols, numRows);
for (int y = 0; y < numRows; y++)
{
for (int x = 0; x < numCols; x++)
{
image.SetPixel(x, y, Color.FromArgb(255 - pixels[x * y], 255 - pixels[x * y], 255 - pixels[x * y])); //invert colors to have 0,0,0 be white as specified by mnist
}
}
imageView.SetImage(image);
imageView.Refresh();
//.......................................................
}
brImages.Close();
brLabels.Close();
}
}
for (int i = 0; i < images.Count; i++)
{
trainingSet.data.Add(images[i], labels[i]);
}
return trainingSet;
}
All images produce a pattern as shown above. It's never the exact same pattern but always seems to have the pixels "pulled" down to the right corner.
That is how I did it:
public static class MnistReader
{
private const string TrainImages = "mnist/train-images.idx3-ubyte";
private const string TrainLabels = "mnist/train-labels.idx1-ubyte";
private const string TestImages = "mnist/t10k-images.idx3-ubyte";
private const string TestLabels = "mnist/t10k-labels.idx1-ubyte";
public static IEnumerable<Image> ReadTrainingData()
{
foreach (var item in Read(TrainImages, TrainLabels))
{
yield return item;
}
}
public static IEnumerable<Image> ReadTestData()
{
foreach (var item in Read(TestImages, TestLabels))
{
yield return item;
}
}
private static IEnumerable<Image> Read(string imagesPath, string labelsPath)
{
BinaryReader labels = new BinaryReader(new FileStream(labelsPath, FileMode.Open));
BinaryReader images = new BinaryReader(new FileStream(imagesPath, FileMode.Open));
int magicNumber = images.ReadBigInt32();
int numberOfImages = images.ReadBigInt32();
int width = images.ReadBigInt32();
int height = images.ReadBigInt32();
int magicLabel = labels.ReadBigInt32();
int numberOfLabels = labels.ReadBigInt32();
for (int i = 0; i < numberOfImages; i++)
{
var bytes = images.ReadBytes(width * height);
var arr = new byte[height, width];
arr.ForEach((j,k) => arr[j, k] = bytes[j * height + k]);
yield return new Image()
{
Data = arr,
Label = labels.ReadByte()
};
}
}
}
Image class:
public class Image
{
public byte Label { get; set; }
public byte[,] Data { get; set; }
}
Some extension methods:
public static class Extensions
{
public static int ReadBigInt32(this BinaryReader br)
{
var bytes = br.ReadBytes(sizeof(Int32));
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToInt32(bytes, 0);
}
public static void ForEach<T>(this T[,] source, Action<int, int> action)
{
for (int w = 0; w < source.GetLength(0); w++)
{
for (int h = 0; h < source.GetLength(1); h++)
{
action(w, h);
}
}
}
}
Usage:
foreach (var image in MnistReader.ReadTrainingData())
{
//use image here
}
or
foreach (var image in MnistReader.ReadTestData())
{
//use image here
}
Why not use a nuget package:
MNIST.IO Just a datareader (disclaimer: my package)
Accord.DataSets Contains classes to download and parse machine learning datasets such as MNIST, News20, Iris. This package is part of the Accord.NET Framework.
I want to make two thread, because I want to divide first for loop into 2 parts. Instead of for (var row = 0; row < area.Height; row++) I want to make for (var row = 0; row < area.Height/2; row++) and for (var row = area.Height/2; row < area.Height; row++) for each loop i want threads. I don't know how to implement this thing. can you help me ?
using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
namespace MandelbrotGenerator {
public class SyncImageGenerator : IImageGenerator {
public void GenerateImage(Area area) {
var sw = Stopwatch.StartNew();
var bitmap = SyncImageGenerator.GenerateSyncroniously(area);
GenerationDone(area, bitmap, sw.Elapsed);
}
public static Bitmap GenerateSyncroniously(Area area) {
int maxIterations;
double zBorder;
double cReal, cImg, zReal, zImg, zNewReal, zNewImg;
maxIterations = Settings.DefaultSettings.MaxIterations;
zBorder = Settings.DefaultSettings.ZBorder * Settings.DefaultSettings.ZBorder;
Bitmap bitmap = new Bitmap(area.Width, area.Height);
for (var row = 0; row < area.Height; row++) {
for (var col = 0; col < area.Width; col++) {
var pixelWidth = (area.MaxReal - area.MinReal) / area.Width;
cReal = area.MinReal + col * pixelWidth;
var pixelHeight = (area.MaxImg - area.MinImg) / area.Height;
cImg = area.MinImg + row * pixelHeight;
zReal = 0.0;
zImg = 0.0;
var iter = 0;
while (zReal*zReal+zImg*zImg<zBorder && iter<maxIterations) {
zNewReal = zReal * zReal - zImg * zImg;
zNewImg = zImg * zReal + zReal * zImg;
zNewReal = zNewReal + cReal;
zNewImg = zNewImg + cImg;
zReal = zNewReal;
zImg = zNewImg;
iter++;
}
bitmap.SetPixel(col, row, ColorSchema.GetColor(iter));
}
}
return bitmap;
}
public event EventHandler<EventArgs<Tuple<Area, Bitmap, TimeSpan>>> ImageGenerated;
private void GenerationDone(Area area, Bitmap bitmap, TimeSpan time) {
if (ImageGenerated != null) {
ImageGenerated(this, new EventArgs<Tuple<Area, Bitmap, TimeSpan>>(Tuple.Create(area, bitmap, time)));
}
}
}
}
Actually, I don't know how to use all these variables and how to share all of them with 2 threads.
Don't directly use threads when there are plenty of good abstractions to choose from. TPL is one, but I prefer Microsoft's Reactive Framework (NuGet "Rx-Main"). With it you can do this:
public static IObservable<Bitmap> GenerateAsynchronously(Area area)
{
int maxIterations = 100;
double zBorder = 1.0;
var compute =
from row in Observable.Range(0, area.Height)
from col in Observable.Range(0, area.Width)
from iter in Observable.Start(() =>
{
var pixelWidth = (area.MaxReal - area.MinReal) / area.Width;
double cReal = area.MinReal + col * pixelWidth;
var pixelHeight = (area.MaxImg - area.MinImg) / area.Height;
double cImg = area.MinImg + row * pixelHeight;
double zReal = 0.0;
double zImg = 0.0;
var i = 0;
while (zReal * zReal + zImg * zImg < zBorder && i < maxIterations)
{
double zNewReal = zReal * zReal - zImg * zImg;
double zNewImg = zImg * zReal + zReal * zImg;
zNewReal = zNewReal + cReal;
zNewImg = zNewImg + cImg;
zReal = zNewReal;
zImg = zNewImg;
i++;
}
return i;
})
select new { row, col, iter };
var query =
from xs in compute.ToArray()
from bm in Observable.Start(() =>
{
Bitmap bitmap = new Bitmap(area.Width, area.Height);
foreach (var x in xs)
{
bitmap.SetPixel(x.col, x.row, ColorSchema.GetColor(x.iter));
}
return bitmap;
})
select bm;
return query;
}
Now you are returning an IObservable<Bitmap> rather than Bitmap. You consume this like so:
var bitmapObservable = SyncImageGenerator.GenerateAsynchronously(area);
bitmapObservable
.Subscribe(bitmap =>
{
GenerationDone(area, bitmap);
});
No need to manage threads at all, but you get maximum concurrency.
I have a function that takes a variable as a parameter and returns a calculated result. That function splits up into other functions each doing their own calculation. I need the function to run multi threaded.
My code:
for (int i = 0; i < pic.Width; i++)
{
for (int k = 0; k < pic.Height; k++)
{
var localK = k;
var localI = i;
Image bestPic;
new Thread(() =>
{
bestPic = new Bitmap(getBestPic(argb));//THIS IS WHERE THE WRONG VALUES ARE ASSIGNED BECAUSE OF CROSS THREADING
lock (thisLock)
{
g.DrawImage(bestPic, localI * bestPic.Width, localK * bestPic.Height, bestPic.Width, bestPic.Height);
}
}).Start();
}
}
All I need is the function getBestPic to run multi threaded. But how do I run the function getBestPic multi threaded and make the assigning of the returned result to the bestPic variable atomic?
My entire program if needed: This is a montage program.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;
namespace test
{
public partial class Form1 : Form
{
private static readonly Object thisLock = new Object();
private Graphics g;
private Bitmap returnImg;
private Bitmap pic;
private int done = 0;
private int pictureWidthAndLength = 200;
private string inputPicName = "test";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
DateTime dtStart = DateTime.Now;
pic = new Bitmap(inputPicName + ".jpg");
//MessageBox.Show(pic.GetPixel(1,1).ToArgb().ToString());
//MessageBox.Show(pic.Width.ToString() + " x " + pic.Height.ToString());
returnImg = new Bitmap(pic.Width * pictureWidthAndLength, pic.Height * pictureWidthAndLength);
using (g = Graphics.FromImage(returnImg))
{
Color clr;
int[] argb = new int[4];
for (int i = 0; i < pic.Width; i++)
{
for (int k = 0; k < pic.Height; k++)
{
clr = pic.GetPixel(i, k);
argb[0] = clr.A;
argb[1] = clr.R;
argb[2] = clr.G;
argb[3] = clr.B;
var localK = k;
var localI = i;
Image bestPic;
if (cbxthreading.Checked)
{
new Thread(() =>
{
bestPic = new Bitmap(getBestPic(argb));
lock (thisLock)
{
g.DrawImage(bestPic, localI * bestPic.Width, localK * bestPic.Height, bestPic.Width, bestPic.Height);
done++;
}
}).Start();
}
else
{
//Single threaded
bestPic = new Bitmap(getBestPic(argb));
g.DrawImage(bestPic, localI * pictureWidthAndLength, localK * pictureWidthAndLength, pictureWidthAndLength, pictureWidthAndLength);
}
//MessageBox.Show(getBestPic(argb));
}
}
if (cbxthreading.Checked)
{
int loopNum = pic.Width * pic.Height;
while (done < loopNum) { }
}
}
DateTime dtEnd = DateTime.Now;
MessageBox.Show((dtEnd - dtStart).ToString());
}
//Get picture that is best suited to replace pixel
private string getBestPic(int[] argb)
{
int numOfpics = 5;
int[] currentBest = new int[2];
currentBest[0] = 255;
currentBest[1] = 150;
for (int i = 0; i < numOfpics; i++)
{
int compare = compareARGB(getAverageRGB(new Bitmap((i + 1).ToString()+".jpg")), argb);
if (compare < currentBest[0])
{
currentBest[0] = compare;
currentBest[1] = i + 1;
}
}
return currentBest[1].ToString() + ".jpg";
}
// smaller the value, closer the camparison
private int compareARGB(int[] one, int[] two)
{
int [] tmp = new int[4];
tmp[0] = Convert.ToInt32(Math.Abs(one[0] - two[0]));
tmp[1] = Convert.ToInt32(Math.Abs(one[1] - two[1]));
tmp[2] = Convert.ToInt32(Math.Abs(one[2] - two[2]));
tmp[3] = Convert.ToInt32(Math.Abs(one[3] - two[3]));
return (tmp[0] + tmp[1] + tmp[2] + tmp[3]);
}
//return int arry with size 4 containing the argb values
private int[] getAverageRGB(Bitmap img)
{
Color clr;
int aplha = 0;
int red = 0;
int green = 0;
int blue = 0;
for (int i = 0; i < img.Width; i++)
{
for (int k = 0; k < img.Height; k++)
{
clr = img.GetPixel(i, k);
aplha += clr.A;
red += clr.R;
green += clr.G;
blue += clr.B;
}
}
aplha = aplha / (img.Width * img.Height);
red = red / (img.Width * img.Height);
green = green / (img.Width * img.Height);
blue = blue / (img.Width * img.Height);
int[] re = new int[] {aplha,red,green,blue};
return re;
}
private void button2_Click(object sender, EventArgs e)
{
returnImg.Save(inputPicName+".bmp");
MessageBox.Show("Done!");
}
}
}
The single thread functionality works, but takes long. The multi threaded functionality also finishes in a third of the time of the single threaded, but the result is not correct.
getBestPic() method runs multi-thread as I understand. But the problem is the argb parameter. You initialize it ones and then overwrite its values in for loops.argb is reference type, so only the reference is passed to getBestPic(), so it's referenced values get changed while processed in getBestPic().
I would try to pass it by Value or move int[] argb = new int[4];line to the inside of the second for loop, so you every time initialize new variable. More on passing reference type params here.
Just create a copy of the Value of your argb in the getBestPic() method and use it instead of using the original one
private string getBestPic(int[] argb)
{
int[] argbCopy = argb.ToArray();
int numOfpics = 5;
int[] currentBest = new int[2];
currentBest[0] = 255;
currentBest[1] = 150;
for (int i = 0; i < numOfpics; i++)
{
int compare = compareARGB(getAverageRGB(new Bitmap((i + 1).ToString()+".jpg")), argbCopy);
if (compare < currentBest[0])
{
currentBest[0] = compare;
currentBest[1] = i + 1;
}
}
return currentBest[1].ToString() + ".jpg";
}