diff --git a/Samples/Audio/Fourier (FFT)/MainForm.Designer.cs b/Samples/Audio/Fourier (FFT)/MainForm.Designer.cs index 3ed6559da..8ddb9abb5 100644 --- a/Samples/Audio/Fourier (FFT)/MainForm.Designer.cs +++ b/Samples/Audio/Fourier (FFT)/MainForm.Designer.cs @@ -206,8 +206,6 @@ private void InitializeComponent() this.chart1.BackColor = System.Drawing.Color.White; this.chart1.Location = new System.Drawing.Point(12, 27); this.chart1.Name = "chart1"; - this.chart1.RangeX = ((AForge.DoubleRange)(resources.GetObject("chart1.RangeX"))); - this.chart1.RangeY = ((AForge.DoubleRange)(resources.GetObject("chart1.RangeY"))); this.chart1.SimpleMode = false; this.chart1.Size = new System.Drawing.Size(684, 367); this.chart1.TabIndex = 6; diff --git a/Samples/Audio/Fourier (FFT)/MainForm.cs b/Samples/Audio/Fourier (FFT)/MainForm.cs index ea77cbbdd..a11de531e 100644 --- a/Samples/Audio/Fourier (FFT)/MainForm.cs +++ b/Samples/Audio/Fourier (FFT)/MainForm.cs @@ -80,7 +80,6 @@ void btnStart_Click(object sender, EventArgs e) source.NewFrame += source_NewFrame; source.AudioSourceError += source_AudioSourceError; - // Start it! source.Start(); } diff --git a/Samples/Imaging/Hough Transform/MainForm.cs b/Samples/Imaging/Hough Transform/MainForm.cs index e555a3ec6..13893370b 100644 --- a/Samples/Imaging/Hough Transform/MainForm.cs +++ b/Samples/Imaging/Hough Transform/MainForm.cs @@ -60,9 +60,6 @@ private void openToolStripMenuItem_Click(object sender, EventArgs e) Bitmap image = AForge.Imaging.Image.Clone(tempImage, PixelFormat.Format24bppRgb); tempImage.Dispose(); - // format image - AForge.Imaging.Image.FormatImage(ref image); - // lock the source image BitmapData sourceData = image.LockBits( new Rectangle(0, 0, image.Width, image.Height), diff --git a/Samples/Video/Two Cameras/MainForm.cs b/Samples/Video/Two Cameras/MainForm.cs index 879a466d4..cc97f85eb 100644 --- a/Samples/Video/Two Cameras/MainForm.cs +++ b/Samples/Video/Two Cameras/MainForm.cs @@ -115,8 +115,8 @@ private void stopButton_Click(object sender, EventArgs e) private void StartCameras() { // create first video source - VideoCaptureDevice videoSource1 = new VideoCaptureDevice(videoDevices[camera1Combo.SelectedIndex].MonikerString); - videoSource1.DesiredFrameRate = 10; + var videoSource1 = new VideoCaptureDevice(videoDevices[camera1Combo.SelectedIndex].MonikerString); + // videoSource1.DesiredFrameRate = 10; videoSourcePlayer1.VideoSource = videoSource1; videoSourcePlayer1.Start(); @@ -126,8 +126,8 @@ private void StartCameras() { System.Threading.Thread.Sleep(500); - VideoCaptureDevice videoSource2 = new VideoCaptureDevice(videoDevices[camera2Combo.SelectedIndex].MonikerString); - videoSource2.DesiredFrameRate = 10; + var videoSource2 = new VideoCaptureDevice(videoDevices[camera2Combo.SelectedIndex].MonikerString); + // videoSource2.DesiredFrameRate = 10; videoSourcePlayer2.VideoSource = videoSource2; videoSourcePlayer2.Start(); diff --git a/Sources/Accord.Audio.DirectSound/AudioCaptureDevice.cs b/Sources/Accord.Audio.DirectSound/AudioCaptureDevice.cs index f28e1ff2d..f7ca7b2d2 100644 --- a/Sources/Accord.Audio.DirectSound/AudioCaptureDevice.cs +++ b/Sources/Accord.Audio.DirectSound/AudioCaptureDevice.cs @@ -1,26 +1,26 @@ -// Accord Audio Library -// The Accord.NET Framework -// http://accord-framework.net -// -// Copyright © César Souza, 2009-2015 -// cesarsouza at gmail.com -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -// - -namespace Accord.DirectSound +// Accord Audio Library +// The Accord.NET Framework +// http://accord-framework.net +// +// Copyright © César Souza, 2009-2015 +// cesarsouza at gmail.com +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +namespace Accord.DirectSound { using Accord.Audio; using SharpDX.DirectSound; @@ -28,554 +28,550 @@ namespace Accord.DirectSound using System; using System.Threading; - /// - /// Audio source for local audio capture device (i.e. a microphone). - /// - /// - /// - /// This audio source captures audio data - /// obtained from a local audio capture device such as the microphone. The audio - /// is captured using DirectSound through SlimDX. - /// - /// For instructions on how to list capture devices, please see - /// the documentation page. - /// - /// - /// - /// Sample usage: - /// - /// - /// // Create default capture device - /// AudioCaptureDevice source = new AudioCaptureDevice(); - /// - /// // Specify capturing options - /// source.DesiredFrameSize = 4096; - /// source.SampleRate = 22050; - /// - /// // Specify the callback function which will be - /// // called once a sample is completely available - /// source.NewFrame += source_NewFrame; - /// - /// // Start capturing - /// source.Start(); - /// - /// // ... - /// - /// // The callback function should determine what - /// // should be done with the samples being caught - /// private void source_NewFrame(object sender, NewFrameEventArgs eventArgs) - /// { - /// // Read current frame... - /// Signal s = eventArgs.Signal; - /// - /// // Process/play/record it - /// // ... - /// } - /// - /// - /// For more details regarding usage, please check one of - /// the Audio sample applications accompanying the framework. - /// - /// - /// - /// - /// - public class AudioCaptureDevice : IAudioSource, IDisposable - { - - // moniker string of audio capture device - private Guid device = Guid.Empty; - private string sourceName; - - // user data associated with the audio source - private object userData = null; - - // received frames count - private int framesReceived; - - // received byte count - private int bytesReceived; - - // specifies desired capture frame size - private int desiredCaptureSize = 4096; - - // specifies the sample rate used in the source - private int sampleRate = 44100; - - private Thread thread = null; - private ManualResetEvent stopEvent = null; - - private SampleFormat sampleFormat = SampleFormat.Format32BitIeeeFloat; - - /// - /// New frame event. - /// - /// - /// Notifies clients about new available frame from audio source. - /// - /// Since audio source may have multiple clients, each client is responsible for - /// making a copy (cloning) of the passed audio frame, because the audio source disposes its - /// own original copy after notifying of clients. - /// - /// - public event EventHandler NewFrame; - - - /// - /// Audio source error event. - /// - /// - /// This event is used to notify clients about any type of errors occurred in - /// audio source object, for example internal exceptions. - /// - public event EventHandler AudioSourceError; - - /// - /// Audio source. - /// - /// - /// Audio source is represented by Guid of audio capture device. - /// - public virtual string Source - { - get { return sourceName; } - set { sourceName = value; } - } - - /// - /// Gets or sets the sample format used by the device. - /// - /// - public SampleFormat Format - { - get { return sampleFormat; } - set { sampleFormat = value; } - } - - /// - /// Gets or sets the desired frame size. - /// - public int DesiredFrameSize - { - get { return desiredCaptureSize; } - set { desiredCaptureSize = value; } - } - - /// - /// Gets the number of audio channels captured by - /// the device. Currently, only a single channel - /// is supported. - /// - /// - public int Channels - { - get { return 1; } - set { } - } - - /// - /// Received frames count. - /// - /// - /// Number of frames the audio source provided from the moment of the last - /// access to the property. - /// - /// - public int FramesReceived - { - get - { - int frames = framesReceived; - framesReceived = 0; - return frames; - } - } - - /// - /// Received bytes count. - /// - /// - /// Number of bytes the audio source provided from the moment of the last - /// access to the property. - /// - /// - public int BytesReceived - { - get - { - int bytes = bytesReceived; - bytesReceived = 0; - return bytes; - } - } - - /// - /// User data. - /// - /// - /// The property allows to associate user data with audio source object. - /// - public object UserData - { - get { return userData; } - set { userData = value; } - } - - /// - /// State of the audio source. - /// - /// - /// Current state of audio source object - running or not. - /// - public bool IsRunning - { - get - { - if (thread != null) - { - // check thread status - if (thread.Join(0) == false) - return true; - - // the thread is not running, free resources - Free(); - } - return false; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// - public AudioCaptureDevice() - { - this.device = Guid.Empty; - this.sourceName = "Default capture device"; - } - - /// - /// Initializes a new instance of the class. - /// - /// - public AudioCaptureDevice(AudioDeviceInfo device) - { - this.device = device.Guid; - this.sourceName = device.Description; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Global identifier of the audio capture device. - /// - public AudioCaptureDevice(Guid device) - { - this.device = device; - this.sourceName = device.ToString(); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Global identifier of the audio capture device. - /// The device name or description string. - /// - public AudioCaptureDevice(Guid device, string name) - { - this.device = device; - this.sourceName = name; - } - - /// - /// Start audio source. - /// - /// - /// Starts audio source and return execution to caller. audio source - /// object creates background thread and notifies about new frames with the - /// help of event. - /// - public void Start() - { - if (thread == null) - { - // check source - if (device == Guid.Empty) - throw new ArgumentException("Audio source is not specified"); - - framesReceived = 0; - bytesReceived = 0; - - // create events - stopEvent = new ManualResetEvent(false); - - // create and start new thread - thread = new Thread(WorkerThread); - thread.Name = device.ToString(); - thread.Start(); - } - } - - /// - /// Signals audio source to stop its work. - /// - /// - /// Signals audio source to stop its background thread, stop to - /// provide new frames and free resources. - /// - public void SignalToStop() - { - // stop thread - if (thread != null) - { - // signal to stop - stopEvent.Set(); - } - } - - /// - /// Wait for audio source has stopped. - /// - /// - /// Waits for source stopping after it was signaled to stop using - /// method. - /// - public void WaitForStop() - { - if (thread != null) - { - // wait for thread stop - thread.Join(); - - Free(); - } - } - - /// - /// Stop audio source. - /// - /// - /// Stops audio source aborting its thread. - /// - /// Since the method aborts background thread, its usage is highly not preferred - /// and should be done only if there are no other options. The correct way of stopping camera - /// is signaling it stop and then - /// waiting for background thread's completion. - /// - /// - public void Stop() - { - if (this.IsRunning) - { - thread.Abort(); - WaitForStop(); - } - } - - /// - /// Free resource. - /// - /// - private void Free() - { - thread = null; - - // release events - stopEvent.Close(); - stopEvent = null; - } - - - - /// - /// Worker thread. - /// - /// - private void WorkerThread() - { - // Get the selected capture device - DirectSoundCapture captureDevice = new DirectSoundCapture(device); - - - // Set the capture format - var bitsPerSample = Signal.GetSampleSize(sampleFormat); - WaveFormat format = WaveFormat.CreateCustomFormat(sampleFormat.ToWaveFormat(), sampleRate, 1, - sampleRate * bitsPerSample / 8, bitsPerSample / 8, bitsPerSample); - - // Setup the capture buffer - CaptureBufferDescription captureBufferDescription = new CaptureBufferDescription(); - captureBufferDescription.Format = format; - captureBufferDescription.BufferBytes = 2 * desiredCaptureSize * format.BlockAlign; - captureBufferDescription.Flags |= CaptureBufferCapabilitiesFlags.WaveMapped; - captureBufferDescription.Flags &= ~CaptureBufferCapabilitiesFlags.ControlEffects; - - CaptureBuffer captureBuffer = null; - NotificationPosition[] notifications = new NotificationPosition[2]; - - try - { - captureBuffer = new CaptureBuffer(captureDevice, captureBufferDescription); - - // Setup the notification positions - int bufferPortionSize = captureBuffer.Capabilities.BufferBytes / 2; - notifications[0] = new NotificationPosition(); - notifications[0].Offset = bufferPortionSize - 1; - notifications[0].WaitHandle = new AutoResetEvent(false); - notifications[1] = new NotificationPosition(); - notifications[1].Offset = bufferPortionSize - 1 + bufferPortionSize; - notifications[1].WaitHandle = new AutoResetEvent(false); - captureBuffer.SetNotificationPositions(notifications); - - // Make a copy of the wait handles - WaitHandle[] waitHandles = new WaitHandle[notifications.Length]; - for (int i = 0; i < notifications.Length; i++) - waitHandles[i] = notifications[i].WaitHandle; - - - - // Start capturing - captureBuffer.Start(true); - - - if (sampleFormat == SampleFormat.Format32BitIeeeFloat) - { - float[] currentSample = new float[desiredCaptureSize]; - - while (!stopEvent.WaitOne(0, true)) - { - int bufferPortionIndex = WaitHandle.WaitAny(waitHandles); - captureBuffer.Read(currentSample, 0, currentSample.Length, bufferPortionSize * bufferPortionIndex, LockFlags.None); - OnNewFrame(currentSample); - } - } - else if (sampleFormat == SampleFormat.Format16Bit) - { - short[] currentSample = new short[desiredCaptureSize]; - - while (!stopEvent.WaitOne(0, true)) - { - int bufferPortionIndex = WaitHandle.WaitAny(waitHandles); - captureBuffer.Read(currentSample, 0, currentSample.Length, bufferPortionSize * bufferPortionIndex, LockFlags.None); - OnNewFrame(currentSample); - } - } - } - catch (Exception ex) - { - if (AudioSourceError != null) - AudioSourceError(this, new AudioSourceErrorEventArgs(ex.Message)); - else throw; - } - finally - { - if (captureBuffer != null) - { - captureBuffer.Stop(); - captureBuffer.Dispose(); - } - - if (captureDevice != null) - captureDevice.Dispose(); - - for (int i = 0; i < notifications.Length; i++) - if (notifications[i].WaitHandle != null) - notifications[i].WaitHandle.Close(); - } - } - - - - /// - /// Notifies client about new block of frames. - /// - /// - /// New frame's audio. - /// - protected void OnNewFrame(Array frame) - { - framesReceived++; - - if ((!stopEvent.WaitOne(0, true)) && (NewFrame != null)) - { - NewFrame(this, new NewFrameEventArgs(Signal.FromArray(frame, sampleRate, sampleFormat))); - } - } - - - /// - /// Gets whether this audio source supports seeking. - /// - /// - public bool CanSeek - { - get { return false; } - } - - /// - /// This source does not support seeking. - /// - /// - public void Seek(int frameIndex) - { - throw new NotSupportedException(); - } - - /// - /// Gets or sets the desired sample rate for this capturing device. - /// - /// - public int SampleRate - { - get { return this.sampleRate; } - set { this.sampleRate = value; } - } - - - #region IDisposable members - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - /// - ~AudioCaptureDevice() - { - Dispose(false); - } - - /// - /// Performs application-defined tasks associated with - /// freeing, releasing, or resetting unmanaged resources. - /// - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// - /// - /// true to release both managed and unmanaged resources; - /// false to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - // free managed resources - if (stopEvent != null) - { - stopEvent.Close(); - stopEvent = null; - } - } - } - #endregion - - } + /// + /// Audio source for local audio capture device (i.e. a microphone). + /// + /// + /// + /// This audio source captures audio data + /// obtained from a local audio capture device such as the microphone. The audio + /// is captured using DirectSound through SlimDX. + /// + /// For instructions on how to list capture devices, please see + /// the documentation page. + /// + /// + /// + /// Sample usage: + /// + /// + /// // Create default capture device + /// AudioCaptureDevice source = new AudioCaptureDevice(); + /// + /// // Specify capturing options + /// source.DesiredFrameSize = 4096; + /// source.SampleRate = 22050; + /// + /// // Specify the callback function which will be + /// // called once a sample is completely available + /// source.NewFrame += source_NewFrame; + /// + /// // Start capturing + /// source.Start(); + /// + /// // ... + /// + /// // The callback function should determine what + /// // should be done with the samples being caught + /// private void source_NewFrame(object sender, NewFrameEventArgs eventArgs) + /// { + /// // Read current frame... + /// Signal s = eventArgs.Signal; + /// + /// // Process/play/record it + /// // ... + /// } + /// + /// + /// For more details regarding usage, please check one of + /// the Audio sample applications accompanying the framework. + /// + /// + /// + /// + /// + public class AudioCaptureDevice : IAudioSource, IDisposable + { + + // moniker string of audio capture device + private Guid device = Guid.Empty; + private string sourceName; + + // user data associated with the audio source + private object userData = null; + + // received frames count + private int framesReceived; + + // received byte count + private int bytesReceived; + + // specifies desired capture frame size + private int desiredCaptureSize = 4096; + + // specifies the sample rate used in the source + private int sampleRate = 44100; + + private Thread thread = null; + private ManualResetEvent stopEvent = null; + + private SampleFormat sampleFormat = SampleFormat.Format32BitIeeeFloat; + + /// + /// New frame event. + /// + /// + /// Notifies clients about new available frame from audio source. + /// + /// Since audio source may have multiple clients, each client is responsible for + /// making a copy (cloning) of the passed audio frame, because the audio source disposes its + /// own original copy after notifying of clients. + /// + /// + public event EventHandler NewFrame; + + + /// + /// Audio source error event. + /// + /// + /// This event is used to notify clients about any type of errors occurred in + /// audio source object, for example internal exceptions. + /// + public event EventHandler AudioSourceError; + + /// + /// Audio source. + /// + /// + /// Audio source is represented by Guid of audio capture device. + /// + public virtual string Source + { + get { return sourceName; } + set { sourceName = value; } + } + + /// + /// Gets or sets the sample format used by the device. + /// + /// + public SampleFormat Format + { + get { return sampleFormat; } + set { sampleFormat = value; } + } + + /// + /// Gets or sets the desired frame size. + /// + public int DesiredFrameSize + { + get { return desiredCaptureSize; } + set { desiredCaptureSize = value; } + } + + /// + /// Gets the number of audio channels captured by + /// the device. Currently, only a single channel + /// is supported. + /// + /// + public int Channels + { + get { return 1; } + set { } + } + + /// + /// Received frames count. + /// + /// + /// Number of frames the audio source provided from the moment of the last + /// access to the property. + /// + /// + public int FramesReceived + { + get + { + int frames = framesReceived; + framesReceived = 0; + return frames; + } + } + + /// + /// Received bytes count. + /// + /// + /// Number of bytes the audio source provided from the moment of the last + /// access to the property. + /// + /// + public int BytesReceived + { + get + { + int bytes = bytesReceived; + bytesReceived = 0; + return bytes; + } + } + + /// + /// User data. + /// + /// + /// The property allows to associate user data with audio source object. + /// + public object UserData + { + get { return userData; } + set { userData = value; } + } + + /// + /// State of the audio source. + /// + /// + /// Current state of audio source object - running or not. + /// + public bool IsRunning + { + get + { + if (thread != null) + { + // check thread status + if (thread.Join(0) == false) + return true; + + // the thread is not running, free resources + Free(); + } + return false; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + public AudioCaptureDevice() + { + this.device = Guid.Empty; + this.sourceName = "Default capture device"; + } + + /// + /// Initializes a new instance of the class. + /// + /// + public AudioCaptureDevice(AudioDeviceInfo device) + { + this.device = device.Guid; + this.sourceName = device.Description; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Global identifier of the audio capture device. + /// + public AudioCaptureDevice(Guid device) + { + this.device = device; + this.sourceName = device.ToString(); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Global identifier of the audio capture device. + /// The device name or description string. + /// + public AudioCaptureDevice(Guid device, string name) + { + this.device = device; + this.sourceName = name; + } + + /// + /// Start audio source. + /// + /// + /// Starts audio source and return execution to caller. audio source + /// object creates background thread and notifies about new frames with the + /// help of event. + /// + public void Start() + { + if (thread == null) + { + framesReceived = 0; + bytesReceived = 0; + + // create events + stopEvent = new ManualResetEvent(false); + + // create and start new thread + thread = new Thread(WorkerThread); + thread.Name = device.ToString(); + thread.Start(); + } + } + + /// + /// Signals audio source to stop its work. + /// + /// + /// Signals audio source to stop its background thread, stop to + /// provide new frames and free resources. + /// + public void SignalToStop() + { + // stop thread + if (thread != null) + { + // signal to stop + stopEvent.Set(); + } + } + + /// + /// Wait for audio source has stopped. + /// + /// + /// Waits for source stopping after it was signaled to stop using + /// method. + /// + public void WaitForStop() + { + if (thread != null) + { + // wait for thread stop + thread.Join(); + + Free(); + } + } + + /// + /// Stop audio source. + /// + /// + /// Stops audio source aborting its thread. + /// + /// Since the method aborts background thread, its usage is highly not preferred + /// and should be done only if there are no other options. The correct way of stopping camera + /// is signaling it stop and then + /// waiting for background thread's completion. + /// + /// + public void Stop() + { + if (this.IsRunning) + { + thread.Abort(); + WaitForStop(); + } + } + + /// + /// Free resource. + /// + /// + private void Free() + { + thread = null; + + // release events + stopEvent.Close(); + stopEvent = null; + } + + + + /// + /// Worker thread. + /// + /// + private void WorkerThread() + { + // Get the selected capture device + DirectSoundCapture captureDevice = new DirectSoundCapture(device); + + + // Set the capture format + var bitsPerSample = Signal.GetSampleSize(sampleFormat); + WaveFormat format = WaveFormat.CreateCustomFormat(sampleFormat.ToWaveFormat(), sampleRate, 1, + sampleRate * bitsPerSample / 8, bitsPerSample / 8, bitsPerSample); + + // Setup the capture buffer + CaptureBufferDescription captureBufferDescription = new CaptureBufferDescription(); + captureBufferDescription.Format = format; + captureBufferDescription.BufferBytes = 2 * desiredCaptureSize * format.BlockAlign; + captureBufferDescription.Flags |= CaptureBufferCapabilitiesFlags.WaveMapped; + captureBufferDescription.Flags &= ~CaptureBufferCapabilitiesFlags.ControlEffects; + + CaptureBuffer captureBuffer = null; + NotificationPosition[] notifications = new NotificationPosition[2]; + + try + { + captureBuffer = new CaptureBuffer(captureDevice, captureBufferDescription); + + // Setup the notification positions + int bufferPortionSize = captureBuffer.Capabilities.BufferBytes / 2; + notifications[0] = new NotificationPosition(); + notifications[0].Offset = bufferPortionSize - 1; + notifications[0].WaitHandle = new AutoResetEvent(false); + notifications[1] = new NotificationPosition(); + notifications[1].Offset = bufferPortionSize - 1 + bufferPortionSize; + notifications[1].WaitHandle = new AutoResetEvent(false); + captureBuffer.SetNotificationPositions(notifications); + + // Make a copy of the wait handles + WaitHandle[] waitHandles = new WaitHandle[notifications.Length]; + for (int i = 0; i < notifications.Length; i++) + waitHandles[i] = notifications[i].WaitHandle; + + + + // Start capturing + captureBuffer.Start(true); + + + if (sampleFormat == SampleFormat.Format32BitIeeeFloat) + { + float[] currentSample = new float[desiredCaptureSize]; + + while (!stopEvent.WaitOne(0, true)) + { + int bufferPortionIndex = WaitHandle.WaitAny(waitHandles); + captureBuffer.Read(currentSample, 0, currentSample.Length, bufferPortionSize * bufferPortionIndex, LockFlags.None); + OnNewFrame(currentSample); + } + } + else if (sampleFormat == SampleFormat.Format16Bit) + { + short[] currentSample = new short[desiredCaptureSize]; + + while (!stopEvent.WaitOne(0, true)) + { + int bufferPortionIndex = WaitHandle.WaitAny(waitHandles); + captureBuffer.Read(currentSample, 0, currentSample.Length, bufferPortionSize * bufferPortionIndex, LockFlags.None); + OnNewFrame(currentSample); + } + } + } + catch (Exception ex) + { + if (AudioSourceError != null) + AudioSourceError(this, new AudioSourceErrorEventArgs(ex.Message)); + else throw; + } + finally + { + if (captureBuffer != null) + { + captureBuffer.Stop(); + captureBuffer.Dispose(); + } + + if (captureDevice != null) + captureDevice.Dispose(); + + for (int i = 0; i < notifications.Length; i++) + if (notifications[i].WaitHandle != null) + notifications[i].WaitHandle.Close(); + } + } + + + + /// + /// Notifies client about new block of frames. + /// + /// + /// New frame's audio. + /// + protected void OnNewFrame(Array frame) + { + framesReceived++; + + if ((!stopEvent.WaitOne(0, true)) && (NewFrame != null)) + { + NewFrame(this, new NewFrameEventArgs(Signal.FromArray(frame, sampleRate, sampleFormat))); + } + } + + + /// + /// Gets whether this audio source supports seeking. + /// + /// + public bool CanSeek + { + get { return false; } + } + + /// + /// This source does not support seeking. + /// + /// + public void Seek(int frameIndex) + { + throw new NotSupportedException(); + } + + /// + /// Gets or sets the desired sample rate for this capturing device. + /// + /// + public int SampleRate + { + get { return this.sampleRate; } + set { this.sampleRate = value; } + } + + + #region IDisposable members + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + /// + ~AudioCaptureDevice() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// + /// + /// true to release both managed and unmanaged resources; + /// false to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // free managed resources + if (stopEvent != null) + { + stopEvent.Close(); + stopEvent = null; + } + } + } + #endregion + + } } \ No newline at end of file diff --git a/Sources/Accord.Audio.DirectSound/AudioOutputDevice.cs b/Sources/Accord.Audio.DirectSound/AudioOutputDevice.cs index fd757784b..6587b9ca0 100644 --- a/Sources/Accord.Audio.DirectSound/AudioOutputDevice.cs +++ b/Sources/Accord.Audio.DirectSound/AudioOutputDevice.cs @@ -299,10 +299,6 @@ public void Play() { if (thread == null) { - // check source - if (device == Guid.Empty) - throw new ArgumentException("Audio output is not specified"); - isPlaying = true; // create events diff --git a/Sources/Accord.Core/ExtensionMethods.cs b/Sources/Accord.Core/ExtensionMethods.cs index 82592a8cd..1a6bcbfec 100644 --- a/Sources/Accord.Core/ExtensionMethods.cs +++ b/Sources/Accord.Core/ExtensionMethods.cs @@ -23,6 +23,7 @@ namespace Accord { using System; + using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Globalization; @@ -38,6 +39,24 @@ namespace Accord public static class ExtensionMethods { + /// + /// Copies a collection by calling the ICloneable.Clone method for each element inside it. + /// + /// + /// + /// The collection to be cloned. + /// + /// A copy of the collection where each element has also been copied. + /// + public static T DeepClone(this T list) + where T : IList, ICloneable + { + T clone = (T)list.Clone(); + for (int i = 0; i < clone.Count; i++) + clone[i] = (ICloneable)list[i].Clone(); + return clone; + } + /// /// Creates and adds multiple /// objects with the given names at once. diff --git a/Sources/Accord.Docs/Accord.Documentation/Accord.Documentation.shfbproj b/Sources/Accord.Docs/Accord.Documentation/Accord.Documentation.shfbproj index 8344d5c64..49a98c83c 100644 --- a/Sources/Accord.Docs/Accord.Documentation/Accord.Documentation.shfbproj +++ b/Sources/Accord.Docs/Accord.Documentation/Accord.Documentation.shfbproj @@ -284,7 +284,7 @@ VS2013 C#, Visual Basic MemberName - 2.15.0.0 + 3.0.0.0 .NET Framework 4.5 Attributes, ExplicitInterfaceImplementations, InheritedMembers, InheritedFrameworkMembers, Protected, SealedProtected ms.vsipcc+, ms.vsexpresscc+ diff --git a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.IO.png b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.IO.png index 276771d2a..62229f946 100644 Binary files a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.IO.png and b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.IO.png differ diff --git a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Comparers.png b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Comparers.png index 49d445e9d..3f08f426d 100644 Binary files a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Comparers.png and b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Comparers.png differ diff --git a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Optimization.png b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Optimization.png index d1bdcec78..6229df73d 100644 Binary files a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Optimization.png and b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.Optimization.png differ diff --git a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.png b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.png index 594df8e41..9f85753a2 100644 Binary files a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.png and b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Math.png differ diff --git a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Distributions.Univariate.png b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Distributions.Univariate.png index 76bd4a667..f9b64cc81 100644 Binary files a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Distributions.Univariate.png and b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Distributions.Univariate.png differ diff --git a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Kernels.png b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Kernels.png index 0fcf10e1c..faf70efd8 100644 Binary files a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Kernels.png and b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Kernels.png differ diff --git a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Models.png b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Models.png index fcdefca27..bc07f6574 100644 Binary files a/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Models.png and b/Sources/Accord.Docs/Accord.Documentation/Diagrams/Classes/Accord.Statistics.Models.png differ diff --git a/Sources/Accord.Imaging/AForge/IntegralImage.cs b/Sources/Accord.Imaging/AForge/IntegralImage.cs index 871ebda38..a30ded860 100644 --- a/Sources/Accord.Imaging/AForge/IntegralImage.cs +++ b/Sources/Accord.Imaging/AForge/IntegralImage.cs @@ -1,455 +1,455 @@ -// AForge Image Processing Library -// AForge.NET framework -// http://www.aforgenet.com/framework/ -// -// Copyright © AForge.NET, 2005-2010 -// contacts@aforgenet.com -// - -namespace AForge.Imaging -{ - using System; - using System.Drawing; - using System.Drawing.Imaging; - - /// - /// Integral image. - /// - /// - /// The class implements integral image concept, which is described by - /// Viola and Jones in: P. Viola and M. J. Jones, "Robust real-time face detection", - /// Int. Journal of Computer Vision 57(2), pp. 137–154, 2004. - /// - /// "An integral image I of an input image G is defined as the image in which the - /// intensity at a pixel position is equal to the sum of the intensities of all the pixels - /// above and to the left of that position in the original image." - /// - /// The intensity at position (x, y) can be written as: - /// - /// x y - /// I(x,y) = SUM( SUM( G(i,j) ) ) - /// i=0 j=0 - /// - /// - /// The class uses 32-bit integers to represent integral image. - /// - /// The class processes only grayscale (8 bpp indexed) images. - /// - /// This class contains two versions of each method: safe and unsafe. Safe methods do - /// checks of provided coordinates and ensure that these coordinates belong to the image, what makes - /// these methods slower. Unsafe methods do not do coordinates' checks and rely that these - /// coordinates belong to the image, what makes these methods faster. - /// - /// Sample usage: - /// - /// // create integral image - /// IntegralImage im = IntegralImage.FromBitmap( image ); - /// // get pixels' mean value in the specified rectangle - /// float mean = im.GetRectangleMean( 10, 10, 20, 30 ) - /// - /// - /// - public class IntegralImage - { - /// - /// Intergral image's array. - /// - /// - /// See remarks to property. - /// - protected uint[,] integralImage = null; - - // image's width and height - private int width; - private int height; - - /// - /// Width of the source image the integral image was constructed for. - /// - public int Width - { - get { return width; } - } - - /// - /// Height of the source image the integral image was constructed for. - /// - public int Height - { - get { return height; } - } - - /// - /// Provides access to internal array keeping integral image data. - /// - /// - /// - /// The array should be accessed by [y, x] indexing. - /// - /// The array's size is [+1, +1]. The first - /// row and column are filled with zeros, what is done for more efficient calculation of - /// rectangles' sums. - /// - /// - public uint[,] InternalData - { - get { return integralImage; } - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Image width. - /// Image height. - /// - /// The constractor is protected, what makes it imposible to instantiate this - /// class directly. To create an instance of this class or - /// method should be used. - /// - protected IntegralImage( int width, int height ) - { - this.width = width; - this.height = height; - integralImage = new uint[height + 1, width + 1]; - } - - /// - /// Construct integral image from source grayscale image. - /// - /// - /// Source grayscale image. - /// - /// Returns integral image. - /// - /// The source image has incorrect pixel format. - /// - public static IntegralImage FromBitmap( Bitmap image ) - { - // check image format - if ( image.PixelFormat != PixelFormat.Format8bppIndexed ) - { - throw new UnsupportedImageFormatException( "Source image can be graysclae (8 bpp indexed) image only." ); - } - - // lock source image - BitmapData imageData = image.LockBits( - new Rectangle( 0, 0, image.Width, image.Height ), - ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed ); - - // process the image - IntegralImage im = FromBitmap( imageData ); - - // unlock image - image.UnlockBits( imageData ); - - return im; - } - - /// - /// Construct integral image from source grayscale image. - /// - /// - /// Source image data. - /// - /// Returns integral image. - /// - /// The source image has incorrect pixel format. - /// - public static IntegralImage FromBitmap( BitmapData imageData ) - { - return FromBitmap( new UnmanagedImage( imageData ) ); - } - - /// - /// Construct integral image from source grayscale image. - /// - /// - /// Source unmanaged image. - /// - /// Returns integral image. - /// - /// The source image has incorrect pixel format. - /// - public static IntegralImage FromBitmap( UnmanagedImage image ) - { - // check image format - if ( image.PixelFormat != PixelFormat.Format8bppIndexed ) - { - throw new ArgumentException( "Source image can be graysclae (8 bpp indexed) image only." ); - } - - // get source image size - int width = image.Width; - int height = image.Height; - int offset = image.Stride - width; - - // create integral image - IntegralImage im = new IntegralImage( width, height ); - uint[,] integralImage = im.integralImage; - - // do the job - unsafe - { - byte* src = (byte*) image.ImageData.ToPointer( ); - - // for each line - for ( int y = 1; y <= height; y++ ) - { - uint rowSum = 0; - - // for each pixel - for ( int x = 1; x <= width; x++, src++ ) - { - rowSum += *src; - - integralImage[y, x] = rowSum + integralImage[y - 1, x]; - } - src += offset; - } - } - - return im; - } - - /// - /// Calculate sum of pixels in the specified rectangle. - /// - /// - /// X coordinate of left-top rectangle's corner. - /// Y coordinate of left-top rectangle's corner. - /// X coordinate of right-bottom rectangle's corner. - /// Y coordinate of right-bottom rectangle's corner. - /// - /// Returns sum of pixels in the specified rectangle. - /// - /// Both specified points are included into the calculation rectangle. - /// - public uint GetRectangleSum( int x1, int y1, int x2, int y2 ) - { - // check if requested rectangle is out of the image - if ( ( x2 < 0 ) || ( y2 < 0 ) || ( x1 >= width ) || ( y1 >= height ) ) - return 0; - - if ( x1 < 0 ) x1 = 0; - if ( y1 < 0 ) y1 = 0; - - x2++; - y2++; - - if ( x2 > width ) x2 = width; - if ( y2 > height ) y2 = height; - - return integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]; - } - - /// - /// Calculate horizontal (X) haar wavelet at the specified point. - /// - /// - /// X coordinate of the point to calculate wavelet at. - /// Y coordinate of the point to calculate wavelet at. - /// Wavelet size to calculate. - /// - /// Returns value of the horizontal wavelet at the specified point. - /// - /// The method calculates horizontal wavelet, which is a difference - /// of two horizontally adjacent boxes' sums, i.e. A-B. A is the sum of rectangle with coordinates - /// (x, y-radius, x+radius-1, y+radius-1). B is the sum of rectangle with coordinates - /// (x-radius, y-radius, x-1, y+radiys-1). - /// - public int GetHaarXWavelet( int x, int y, int radius ) - { - int y1 = y - radius; - int y2 = y + radius - 1; - - long a = GetRectangleSum( x, y1, x + radius - 1, y2 ); - long b = GetRectangleSum( x - radius, y1, x - 1, y2 ); - - return (int) ( a - b ); - } - - /// - /// Calculate vertical (Y) haar wavelet at the specified point. - /// - /// - /// X coordinate of the point to calculate wavelet at. - /// Y coordinate of the point to calculate wavelet at. - /// Wavelet size to calculate. - /// - /// Returns value of the vertical wavelet at the specified point. - /// - /// The method calculates vertical wavelet, which is a difference - /// of two vertical adjacent boxes' sums, i.e. A-B. A is the sum of rectangle with coordinates - /// (x-radius, y, x+radius-1, y+radius-1). B is the sum of rectangle with coordinates - /// (x-radius, y-radius, x+radius-1, y-1). - /// - public int GetHaarYWavelet( int x, int y, int radius ) - { - int x1 = x - radius; - int x2 = x + radius - 1; - - float a = GetRectangleSum( x1, y, x2, y + radius - 1 ); - float b = GetRectangleSum( x1, y - radius, x2, y - 1 ); - - return (int) ( a - b ); - } - - /// - /// Calculate sum of pixels in the specified rectangle without checking it's coordinates. - /// - /// - /// X coordinate of left-top rectangle's corner. - /// Y coordinate of left-top rectangle's corner. - /// X coordinate of right-bottom rectangle's corner. - /// Y coordinate of right-bottom rectangle's corner. - /// - /// Returns sum of pixels in the specified rectangle. - /// - /// Both specified points are included into the calculation rectangle. - /// - public uint GetRectangleSumUnsafe( int x1, int y1, int x2, int y2 ) - { - x2++; - y2++; - - return integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]; - } - - /// - /// Calculate sum of pixels in the specified rectangle. - /// - /// - /// X coordinate of central point of the rectangle. - /// Y coordinate of central point of the rectangle. - /// Radius of the rectangle. - /// - /// Returns sum of pixels in the specified rectangle. - /// - /// The method calculates sum of pixels in square rectangle with - /// odd width and height. In the case if it is required to calculate sum of - /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. - /// - /// - public uint GetRectangleSum( int x, int y, int radius ) - { - return GetRectangleSum( x - radius, y - radius, x + radius, y + radius ); - } - - /// - /// Calculate sum of pixels in the specified rectangle without checking it's coordinates. - /// - /// - /// X coordinate of central point of the rectangle. - /// Y coordinate of central point of the rectangle. - /// Radius of the rectangle. - /// - /// Returns sum of pixels in the specified rectangle. - /// - /// The method calculates sum of pixels in square rectangle with - /// odd width and height. In the case if it is required to calculate sum of - /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. - /// - /// - public uint GetRectangleSumUnsafe( int x, int y, int radius ) - { - return GetRectangleSumUnsafe( x - radius, y - radius, x + radius, y + radius ); - } - - /// - /// Calculate mean value of pixels in the specified rectangle. - /// - /// - /// X coordinate of left-top rectangle's corner. - /// Y coordinate of left-top rectangle's corner. - /// X coordinate of right-bottom rectangle's corner. - /// Y coordinate of right-bottom rectangle's corner. - /// - /// Returns mean value of pixels in the specified rectangle. - /// - /// Both specified points are included into the calculation rectangle. - /// - public float GetRectangleMean( int x1, int y1, int x2, int y2 ) - { - // check if requested rectangle is out of the image - if ( ( x2 < 0 ) || ( y2 < 0 ) || ( x1 >= width ) || ( y1 >= height ) ) - return 0; - - if ( x1 < 0 ) x1 = 0; - if ( y1 < 0 ) y1 = 0; - - x2++; - y2++; - - if ( x2 > width ) x2 = width; - if ( y2 > height ) y2 = height; - - // return sum divided by actual rectangles size - return (float) ( (double) ( integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2] ) / - (double) ( ( x2 - x1 ) * ( y2 - y1 ) ) ); - } - - /// - /// Calculate mean value of pixels in the specified rectangle without checking it's coordinates. - /// - /// - /// X coordinate of left-top rectangle's corner. - /// Y coordinate of left-top rectangle's corner. - /// X coordinate of right-bottom rectangle's corner. - /// Y coordinate of right-bottom rectangle's corner. - /// - /// Returns mean value of pixels in the specified rectangle. - /// - /// Both specified points are included into the calculation rectangle. - /// - public float GetRectangleMeanUnsafe( int x1, int y1, int x2, int y2 ) - { - x2++; - y2++; - - // return sum divided by actual rectangles size - return (float) ( (double) ( integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2] ) / - (double) ( ( x2 - x1 ) * ( y2 - y1 ) ) ); - } - - /// - /// Calculate mean value of pixels in the specified rectangle. - /// - /// - /// X coordinate of central point of the rectangle. - /// Y coordinate of central point of the rectangle. - /// Radius of the rectangle. - /// - /// Returns mean value of pixels in the specified rectangle. - /// - /// The method calculates mean value of pixels in square rectangle with - /// odd width and height. In the case if it is required to calculate mean value of - /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. - /// - /// - public float GetRectangleMean( int x, int y, int radius ) - { - return GetRectangleMean( x - radius, y - radius, x + radius, y + radius ); - } - - /// - /// Calculate mean value of pixels in the specified rectangle without checking it's coordinates. - /// - /// - /// X coordinate of central point of the rectangle. - /// Y coordinate of central point of the rectangle. - /// Radius of the rectangle. - /// - /// Returns mean value of pixels in the specified rectangle. - /// - /// The method calculates mean value of pixels in square rectangle with - /// odd width and height. In the case if it is required to calculate mean value of - /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. - /// - /// - public float GetRectangleMeanUnsafe( int x, int y, int radius ) - { - return GetRectangleMeanUnsafe( x - radius, y - radius, x + radius, y + radius ); - } - } -} +// AForge Image Processing Library +// AForge.NET framework +// http://www.aforgenet.com/framework/ +// +// Copyright © AForge.NET, 2005-2010 +// contacts@aforgenet.com +// + +namespace AForge.Imaging +{ + using System; + using System.Drawing; + using System.Drawing.Imaging; + + /// + /// Integral image. + /// + /// + /// The class implements integral image concept, which is described by + /// Viola and Jones in: P. Viola and M. J. Jones, "Robust real-time face detection", + /// Int. Journal of Computer Vision 57(2), pp. 137–154, 2004. + /// + /// "An integral image I of an input image G is defined as the image in which the + /// intensity at a pixel position is equal to the sum of the intensities of all the pixels + /// above and to the left of that position in the original image." + /// + /// The intensity at position (x, y) can be written as: + /// + /// x y + /// I(x,y) = SUM( SUM( G(i,j) ) ) + /// i=0 j=0 + /// + /// + /// The class uses 32-bit integers to represent integral image. + /// + /// The class processes only grayscale (8 bpp indexed) images. + /// + /// This class contains two versions of each method: safe and unsafe. Safe methods do + /// checks of provided coordinates and ensure that these coordinates belong to the image, what makes + /// these methods slower. Unsafe methods do not do coordinates' checks and rely that these + /// coordinates belong to the image, what makes these methods faster. + /// + /// Sample usage: + /// + /// // create integral image + /// IntegralImage im = IntegralImage.FromBitmap( image ); + /// // get pixels' mean value in the specified rectangle + /// float mean = im.GetRectangleMean( 10, 10, 20, 30 ) + /// + /// + /// + public class IntegralImage + { + /// + /// Intergral image's array. + /// + /// + /// See remarks to property. + /// + protected uint[,] integralImage = null; + + // image's width and height + private int width; + private int height; + + /// + /// Width of the source image the integral image was constructed for. + /// + public int Width + { + get { return width; } + } + + /// + /// Height of the source image the integral image was constructed for. + /// + public int Height + { + get { return height; } + } + + /// + /// Provides access to internal array keeping integral image data. + /// + /// + /// + /// The array should be accessed by [y, x] indexing. + /// + /// The array's size is [+1, +1]. The first + /// row and column are filled with zeros, what is done for more efficient calculation of + /// rectangles' sums. + /// + /// + public uint[,] InternalData + { + get { return integralImage; } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Image width. + /// Image height. + /// + /// The constractor is protected, what makes it imposible to instantiate this + /// class directly. To create an instance of this class or + /// method should be used. + /// + protected IntegralImage(int width, int height) + { + this.width = width; + this.height = height; + integralImage = new uint[height + 1, width + 1]; + } + + /// + /// Construct integral image from source grayscale image. + /// + /// + /// Source grayscale image. + /// + /// Returns integral image. + /// + /// The source image has incorrect pixel format. + /// + public static IntegralImage FromBitmap(Bitmap image) + { + // check image format + if (image.PixelFormat != PixelFormat.Format8bppIndexed) + { + throw new UnsupportedImageFormatException("Source image can be graysclae (8 bpp indexed) image only."); + } + + // lock source image + BitmapData imageData = image.LockBits( + new Rectangle(0, 0, image.Width, image.Height), + ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed); + + // process the image + IntegralImage im = FromBitmap(imageData); + + // unlock image + image.UnlockBits(imageData); + + return im; + } + + /// + /// Construct integral image from source grayscale image. + /// + /// + /// Source image data. + /// + /// Returns integral image. + /// + /// The source image has incorrect pixel format. + /// + public static IntegralImage FromBitmap(BitmapData imageData) + { + return FromBitmap(new UnmanagedImage(imageData)); + } + + /// + /// Construct integral image from source grayscale image. + /// + /// + /// Source unmanaged image. + /// + /// Returns integral image. + /// + /// The source image has incorrect pixel format. + /// + public static IntegralImage FromBitmap(UnmanagedImage image) + { + // check image format + if (image.PixelFormat != PixelFormat.Format8bppIndexed) + { + throw new ArgumentException("Source image can be graysclae (8 bpp indexed) image only."); + } + + // get source image size + int width = image.Width; + int height = image.Height; + int offset = image.Stride - width; + + // create integral image + var im = new IntegralImage(width, height); + uint[,] integralImage = im.integralImage; + + // do the job + unsafe + { + byte* src = (byte*)image.ImageData.ToPointer(); + + // for each line + for (int y = 1; y <= height; y++) + { + uint rowSum = 0; + + // for each pixel + for (int x = 1; x <= width; x++, src++) + { + rowSum += *src; + + integralImage[y, x] = rowSum + integralImage[y - 1, x]; + } + src += offset; + } + } + + return im; + } + + /// + /// Calculate sum of pixels in the specified rectangle. + /// + /// + /// X coordinate of left-top rectangle's corner. + /// Y coordinate of left-top rectangle's corner. + /// X coordinate of right-bottom rectangle's corner. + /// Y coordinate of right-bottom rectangle's corner. + /// + /// Returns sum of pixels in the specified rectangle. + /// + /// Both specified points are included into the calculation rectangle. + /// + public uint GetRectangleSum(int x1, int y1, int x2, int y2) + { + // check if requested rectangle is out of the image + if ((x2 < 0) || (y2 < 0) || (x1 >= width) || (y1 >= height)) + return 0; + + if (x1 < 0) x1 = 0; + if (y1 < 0) y1 = 0; + + x2++; + y2++; + + if (x2 > width) x2 = width; + if (y2 > height) y2 = height; + + return integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]; + } + + /// + /// Calculate horizontal (X) haar wavelet at the specified point. + /// + /// + /// X coordinate of the point to calculate wavelet at. + /// Y coordinate of the point to calculate wavelet at. + /// Wavelet size to calculate. + /// + /// Returns value of the horizontal wavelet at the specified point. + /// + /// The method calculates horizontal wavelet, which is a difference + /// of two horizontally adjacent boxes' sums, i.e. A-B. A is the sum of rectangle with coordinates + /// (x, y-radius, x+radius-1, y+radius-1). B is the sum of rectangle with coordinates + /// (x-radius, y-radius, x-1, y+radiys-1). + /// + public int GetHaarXWavelet(int x, int y, int radius) + { + int y1 = y - radius; + int y2 = y + radius - 1; + + long a = GetRectangleSum(x, y1, x + radius - 1, y2); + long b = GetRectangleSum(x - radius, y1, x - 1, y2); + + return (int)(a - b); + } + + /// + /// Calculate vertical (Y) haar wavelet at the specified point. + /// + /// + /// X coordinate of the point to calculate wavelet at. + /// Y coordinate of the point to calculate wavelet at. + /// Wavelet size to calculate. + /// + /// Returns value of the vertical wavelet at the specified point. + /// + /// The method calculates vertical wavelet, which is a difference + /// of two vertical adjacent boxes' sums, i.e. A-B. A is the sum of rectangle with coordinates + /// (x-radius, y, x+radius-1, y+radius-1). B is the sum of rectangle with coordinates + /// (x-radius, y-radius, x+radius-1, y-1). + /// + public int GetHaarYWavelet(int x, int y, int radius) + { + int x1 = x - radius; + int x2 = x + radius - 1; + + float a = GetRectangleSum(x1, y, x2, y + radius - 1); + float b = GetRectangleSum(x1, y - radius, x2, y - 1); + + return (int)(a - b); + } + + /// + /// Calculate sum of pixels in the specified rectangle without checking it's coordinates. + /// + /// + /// X coordinate of left-top rectangle's corner. + /// Y coordinate of left-top rectangle's corner. + /// X coordinate of right-bottom rectangle's corner. + /// Y coordinate of right-bottom rectangle's corner. + /// + /// Returns sum of pixels in the specified rectangle. + /// + /// Both specified points are included into the calculation rectangle. + /// + public uint GetRectangleSumUnsafe(int x1, int y1, int x2, int y2) + { + x2++; + y2++; + + return integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]; + } + + /// + /// Calculate sum of pixels in the specified rectangle. + /// + /// + /// X coordinate of central point of the rectangle. + /// Y coordinate of central point of the rectangle. + /// Radius of the rectangle. + /// + /// Returns sum of pixels in the specified rectangle. + /// + /// The method calculates sum of pixels in square rectangle with + /// odd width and height. In the case if it is required to calculate sum of + /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. + /// + /// + public uint GetRectangleSum(int x, int y, int radius) + { + return GetRectangleSum(x - radius, y - radius, x + radius, y + radius); + } + + /// + /// Calculate sum of pixels in the specified rectangle without checking it's coordinates. + /// + /// + /// X coordinate of central point of the rectangle. + /// Y coordinate of central point of the rectangle. + /// Radius of the rectangle. + /// + /// Returns sum of pixels in the specified rectangle. + /// + /// The method calculates sum of pixels in square rectangle with + /// odd width and height. In the case if it is required to calculate sum of + /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. + /// + /// + public uint GetRectangleSumUnsafe(int x, int y, int radius) + { + return GetRectangleSumUnsafe(x - radius, y - radius, x + radius, y + radius); + } + + /// + /// Calculate mean value of pixels in the specified rectangle. + /// + /// + /// X coordinate of left-top rectangle's corner. + /// Y coordinate of left-top rectangle's corner. + /// X coordinate of right-bottom rectangle's corner. + /// Y coordinate of right-bottom rectangle's corner. + /// + /// Returns mean value of pixels in the specified rectangle. + /// + /// Both specified points are included into the calculation rectangle. + /// + public float GetRectangleMean(int x1, int y1, int x2, int y2) + { + // check if requested rectangle is out of the image + if ((x2 < 0) || (y2 < 0) || (x1 >= width) || (y1 >= height)) + return 0; + + if (x1 < 0) x1 = 0; + if (y1 < 0) y1 = 0; + + x2++; + y2++; + + if (x2 > width) x2 = width; + if (y2 > height) y2 = height; + + // return sum divided by actual rectangles size + return (float)((double)(integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]) / + (double)((x2 - x1) * (y2 - y1))); + } + + /// + /// Calculate mean value of pixels in the specified rectangle without checking it's coordinates. + /// + /// + /// X coordinate of left-top rectangle's corner. + /// Y coordinate of left-top rectangle's corner. + /// X coordinate of right-bottom rectangle's corner. + /// Y coordinate of right-bottom rectangle's corner. + /// + /// Returns mean value of pixels in the specified rectangle. + /// + /// Both specified points are included into the calculation rectangle. + /// + public float GetRectangleMeanUnsafe(int x1, int y1, int x2, int y2) + { + x2++; + y2++; + + // return sum divided by actual rectangles size + return (float)((double)(integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]) / + (double)((x2 - x1) * (y2 - y1))); + } + + /// + /// Calculate mean value of pixels in the specified rectangle. + /// + /// + /// X coordinate of central point of the rectangle. + /// Y coordinate of central point of the rectangle. + /// Radius of the rectangle. + /// + /// Returns mean value of pixels in the specified rectangle. + /// + /// The method calculates mean value of pixels in square rectangle with + /// odd width and height. In the case if it is required to calculate mean value of + /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. + /// + /// + public float GetRectangleMean(int x, int y, int radius) + { + return GetRectangleMean(x - radius, y - radius, x + radius, y + radius); + } + + /// + /// Calculate mean value of pixels in the specified rectangle without checking it's coordinates. + /// + /// + /// X coordinate of central point of the rectangle. + /// Y coordinate of central point of the rectangle. + /// Radius of the rectangle. + /// + /// Returns mean value of pixels in the specified rectangle. + /// + /// The method calculates mean value of pixels in square rectangle with + /// odd width and height. In the case if it is required to calculate mean value of + /// 3x3 rectangle, then it is required to specify its center and radius equal to 1. + /// + /// + public float GetRectangleMeanUnsafe(int x, int y, int radius) + { + return GetRectangleMeanUnsafe(x - radius, y - radius, x + radius, y + radius); + } + } +} diff --git a/Sources/Accord.Imaging/ObjectiveFidelity.cs b/Sources/Accord.Imaging/ObjectiveFidelity.cs index ddbc81c44..a78f5c5fd 100644 --- a/Sources/Accord.Imaging/ObjectiveFidelity.cs +++ b/Sources/Accord.Imaging/ObjectiveFidelity.cs @@ -51,7 +51,7 @@ namespace Accord.Imaging /// Bitmap ori = ... // Original picture /// Bitmap recon = ... // Reconstructed picture /// - /// // Create a new Wolf-Joulion threshold: + /// // Create a new Objective fidelity comparer: /// var of = new ObjectiveFidelity(ori, recon); /// /// // Get the results diff --git a/Sources/Accord.MachineLearning/VectorMachines/MulticlassSupportVectorMachine.cs b/Sources/Accord.MachineLearning/VectorMachines/MulticlassSupportVectorMachine.cs index 4228c8c80..6e5d61554 100644 --- a/Sources/Accord.MachineLearning/VectorMachines/MulticlassSupportVectorMachine.cs +++ b/Sources/Accord.MachineLearning/VectorMachines/MulticlassSupportVectorMachine.cs @@ -1,26 +1,26 @@ -// Accord Machine Learning Library -// The Accord.NET Framework -// http://accord-framework.net -// -// Copyright © César Souza, 2009-2015 -// cesarsouza at gmail.com -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -// - -namespace Accord.MachineLearning.VectorMachines +// Accord Machine Learning Library +// The Accord.NET Framework +// http://accord-framework.net +// +// Copyright © César Souza, 2009-2015 +// cesarsouza at gmail.com +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +namespace Accord.MachineLearning.VectorMachines { using Accord.Math; using Accord.Statistics.Kernels; @@ -30,103 +30,103 @@ namespace Accord.MachineLearning.VectorMachines using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Threading; - using System.Threading.Tasks; - - /// - /// Decision strategies for - /// Multi-class Support Vector Machines. - /// - /// - public enum MulticlassComputeMethod - { - /// - /// Max-voting method (also known as 1vs1 decision). - /// - /// - Voting, - - /// - /// Elimination method (also known as DAG decision). - /// - /// - Elimination, - } - - /// - /// One-against-one Multi-class Kernel Support Vector Machine Classifier. - /// - /// - /// - /// - /// The Support Vector Machine is by nature a binary classifier. One of the ways - /// to extend the original SVM algorithm to multiple classes is to build a one- - /// against-one scheme where multiple SVMs specialize to recognize each of the - /// available classes. By using a competition scheme, the original multi-class - /// classification problem is then reduced to n*(n/2) smaller binary problems. - /// - /// Currently this class supports only Kernel machines as the underlying classifiers. - /// If a Linear Support Vector Machine is needed, specify a Linear kernel in the - /// constructor at the moment of creation. - /// - /// - /// References: - /// - /// - /// - /// http://courses.media.mit.edu/2006fall/mas622j/Projects/aisen-project/index.html - /// - /// - /// http://nlp.stanford.edu/IR-book/html/htmledition/multiclass-svms-1.html - /// - /// - /// - /// - /// - /// - /// // Sample data - /// // The following is simple auto association function - /// // where each input correspond to its own class. This - /// // problem should be easily solved by a Linear kernel. - /// - /// // Sample input data - /// double[][] inputs = - /// { - /// new double[] { 0 }, - /// new double[] { 3 }, - /// new double[] { 1 }, - /// new double[] { 2 }, - /// }; - /// - /// // Output for each of the inputs - /// int[] outputs = { 0, 3, 1, 2 }; - /// - /// - /// // Create a new Linear kernel - /// IKernel kernel = new Linear(); - /// - /// // Create a new Multi-class Support Vector Machine with one input, - /// // using the linear kernel and for four disjoint classes. - /// var machine = new MulticlassSupportVectorMachine(1, kernel, 4); - /// - /// // Create the Multi-class learning algorithm for the machine - /// var teacher = new MulticlassSupportVectorLearning(machine, inputs, outputs); - /// - /// // Configure the learning algorithm to use SMO to train the - /// // underlying SVMs in each of the binary class subproblems. - /// teacher.Algorithm = (svm, classInputs, classOutputs, i, j) => - /// new SequentialMinimalOptimization(svm, classInputs, classOutputs); - /// + using System.Threading.Tasks; + + /// + /// Decision strategies for + /// Multi-class Support Vector Machines. + /// + /// + public enum MulticlassComputeMethod + { + /// + /// Max-voting method (also known as 1vs1 decision). + /// + /// + Voting, + + /// + /// Elimination method (also known as DAG decision). + /// + /// + Elimination, + } + + /// + /// One-against-one Multi-class Kernel Support Vector Machine Classifier. + /// + /// + /// + /// + /// The Support Vector Machine is by nature a binary classifier. One of the ways + /// to extend the original SVM algorithm to multiple classes is to build a one- + /// against-one scheme where multiple SVMs specialize to recognize each of the + /// available classes. By using a competition scheme, the original multi-class + /// classification problem is then reduced to n*(n/2) smaller binary problems. + /// + /// Currently this class supports only Kernel machines as the underlying classifiers. + /// If a Linear Support Vector Machine is needed, specify a Linear kernel in the + /// constructor at the moment of creation. + /// + /// + /// References: + /// + /// + /// + /// http://courses.media.mit.edu/2006fall/mas622j/Projects/aisen-project/index.html + /// + /// + /// http://nlp.stanford.edu/IR-book/html/htmledition/multiclass-svms-1.html + /// + /// + /// + /// + /// + /// + /// // Sample data + /// // The following is simple auto association function + /// // where each input correspond to its own class. This + /// // problem should be easily solved by a Linear kernel. + /// + /// // Sample input data + /// double[][] inputs = + /// { + /// new double[] { 0 }, + /// new double[] { 3 }, + /// new double[] { 1 }, + /// new double[] { 2 }, + /// }; + /// + /// // Output for each of the inputs + /// int[] outputs = { 0, 3, 1, 2 }; + /// + /// + /// // Create a new Linear kernel + /// IKernel kernel = new Linear(); + /// + /// // Create a new Multi-class Support Vector Machine with one input, + /// // using the linear kernel and for four disjoint classes. + /// var machine = new MulticlassSupportVectorMachine(1, kernel, 4); + /// + /// // Create the Multi-class learning algorithm for the machine + /// var teacher = new MulticlassSupportVectorLearning(machine, inputs, outputs); + /// + /// // Configure the learning algorithm to use SMO to train the + /// // underlying SVMs in each of the binary class subproblems. + /// teacher.Algorithm = (svm, classInputs, classOutputs, i, j) => + /// new SequentialMinimalOptimization(svm, classInputs, classOutputs); + /// /// // Run the learning algorithm - /// double error = teacher.Run(); // output should be 0 + /// double error = teacher.Run(); // output should be 0 /// /// // Compute the decision output for one of the input vectors - /// int decision = machine.Compute(new double[] { 3 }); // result should be 3 - /// - /// - /// - /// The next example is a simple 3 classes classification problem. - /// It shows how to use a different kernel function, such as the - /// polynomial kernel of degree 2. + /// int decision = machine.Compute(new double[] { 3 }); // result should be 3 + /// + /// + /// + /// The next example is a simple 3 classes classification problem. + /// It shows how to use a different kernel function, such as the + /// polynomial kernel of degree 2. /// /// /// // Sample input data @@ -165,1133 +165,1133 @@ public enum MulticlassComputeMethod /// /// // Compute the decision output for one of the input vectors /// int decision = machine.Compute( new double[] { -1, 3, 2 }); - /// - /// - /// - /// - /// - /// - /// - /// - /// - [Serializable] - public class MulticlassSupportVectorMachine : ISupportVectorMachine, - IEnumerable, KernelSupportVectorMachine>>, IDisposable - { - - // List of underlying binary classifiers - private KernelSupportVectorMachine[][] machines; - - // Multi-class statistics - private int? totalVectorsCount; - private int? uniqueVectorsCount; - private int? sharedVectorsCount; - - - // Performance optimizations - [NonSerialized] - private Lazy sharedVectors; - - [NonSerialized] - private ThreadLocal vectorCache; - - - /// - /// Constructs a new Multi-class Kernel Support Vector Machine - /// - /// - /// The number of inputs for the machine. If sequences have - /// varying length, pass zero to this parameter and pass a suitable sequence - /// kernel to this constructor, such as . - /// The number of classes in the classification problem. - /// - /// - /// If the number of inputs is zero, this means the machine - /// accepts a indefinite number of inputs. This is often the - /// case for kernel vector machines using a sequence kernel. - /// - /// - public MulticlassSupportVectorMachine(int inputs, int classes) - : this(inputs, new Linear(), classes) - { - } - - /// - /// Constructs a new Multi-class Kernel Support Vector Machine - /// - /// - /// The chosen kernel for the machine. Default is to - /// use the kernel. - /// The number of inputs for the machine. If sequences have - /// varying length, pass zero to this parameter and pass a suitable sequence - /// kernel to this constructor, such as . - /// The number of classes in the classification problem. - /// - /// - /// If the number of inputs is zero, this means the machine - /// accepts a indefinite number of inputs. This is often the - /// case for kernel vector machines using a sequence kernel. - /// - /// - public MulticlassSupportVectorMachine(int inputs, IKernel kernel, int classes) - { - if (classes <= 1) - throw new ArgumentException("The machine must have at least two classes.", "classes"); - - // Create the kernel machines - machines = new KernelSupportVectorMachine[classes - 1][]; - for (int i = 0; i < machines.Length; i++) - { - machines[i] = new KernelSupportVectorMachine[i + 1]; - - for (int j = 0; j <= i; j++) - machines[i][j] = new KernelSupportVectorMachine(kernel, inputs); - } - - this.initialize(); - } - - /// - /// Constructs a new Multi-class Kernel Support Vector Machine - /// - /// - /// - /// The machines to be used in each of the pair-wise class subproblems. - /// - /// - public MulticlassSupportVectorMachine(KernelSupportVectorMachine[][] machines) - { - if (machines == null) - throw new ArgumentNullException("machines"); - - this.machines = machines; - this.initialize(); - } - - private void initialize() - { - this.vectorCache = new ThreadLocal(() => new Cache()); - this.sharedVectors = new Lazy(computeSharedVectors, true); - } - - - - - #region Properties - /// - /// Gets the classifier for against . - /// - /// - /// - /// If the index of is greater than , - /// the classifier for the against - /// will be returned instead. If both indices are equal, null will be - /// returned instead. - /// - /// - public KernelSupportVectorMachine this[int class1, int class2] - { - get - { - if (class1 == class2) - return null; - if (class1 > class2) - return machines[class1 - 1][class2]; - else - return machines[class2 - 1][class1]; - } - } - - /// - /// Gets the total number of machines - /// in this multi-class classifier. - /// - /// - public int MachinesCount - { - get { return ((machines.Length + 1) * machines.Length) / 2; } - } - - /// - /// Gets the total number of support vectors - /// in the entire multi-class machine. - /// - /// - public int SupportVectorCount - { - get - { - if (totalVectorsCount == null) - { - int count = 0; - for (int i = 0; i < machines.Length; i++) - for (int j = 0; j < machines[i].Length; j++) - if (machines[i][j].SupportVectors != null) - count += machines[i][j].SupportVectors.Length; - totalVectorsCount = count; - } - - return totalVectorsCount.Value; - } - } - - /// - /// Gets the number of unique support - /// vectors in the multi-class machine. - /// - /// - public int SupportVectorUniqueCount - { - get - { - if (uniqueVectorsCount == null) - { - HashSet unique = new HashSet(); - for (int i = 0; i < machines.Length; i++) - { - for (int j = 0; j < machines[i].Length; j++) - { - if (machines[i][j].SupportVectors != null) - { - for (int k = 0; k < machines[i][j].SupportVectors.Length; k++) - unique.Add(machines[i][j].SupportVectors[k]); - } - } - } - - uniqueVectorsCount = unique.Count; - } - - return uniqueVectorsCount.Value; - } - } - - /// - /// Gets the number of shared support - /// vectors in the multi-class machine. - /// - /// - public int SupportVectorSharedCount - { - get - { - if (sharedVectorsCount == null) - { - var v = sharedVectors.Value; - } - return sharedVectorsCount.Value; - } - } - - /// - /// Gets the number of classes. - /// - /// - public int Classes - { - get { return machines.Length + 1; } - } - - /// - /// Gets the number of inputs of the machines. - /// - /// - public int Inputs - { - get { return machines[0][0].Inputs; } - } - - /// - /// Gets a value indicating whether this machine produces probabilistic outputs. - /// - /// - /// - /// true if this machine produces probabilistic outputs; otherwise, false. - /// - /// - public bool IsProbabilistic - { - get { return machines[0][0].IsProbabilistic; } - } - - /// - /// Gets the subproblems classifiers. - /// - /// - public KernelSupportVectorMachine[][] Machines - { - get { return machines; } - } - #endregion - - - #region Public Compute Overloads - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// - /// The decision label for the given input. - /// - public int Compute(params double[] inputs) - { - double output; // Compute using elimination method as default. - return Compute(inputs, MulticlassComputeMethod.Elimination, out output); - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// The output of the machine. If this is a - /// probabilistic machine, the - /// output is the probability of the positive class. If this is - /// a standard machine, the output is the distance to the decision - /// hyperplane in feature space. - /// - /// The decision label for the given input. - /// - /// - public int Compute(double[] inputs, out double output) - { - // Compute using elimination method as default. - return Compute(inputs, MulticlassComputeMethod.Elimination, out output); - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// The output of the machine. If this is a - /// probabilistic machine, the - /// output is the probability of the positive class. If this is - /// a standard machine, the output is the distance to the decision - /// hyperplane in feature space. - /// The decision path followed by the Decision - /// Directed Acyclic Graph used by the - /// elimination method. - /// - /// The decision label for the given input. - /// - /// - public int Compute(double[] inputs, out double output, out Tuple[] decisionPath) - { - double[] responses; - decisionPath = new Tuple[Classes - 1]; - return computeElimination(inputs, out responses, out output, decisionPath); - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// The model response for each class. - /// - /// The decision label for the given input. - /// - public int Compute(double[] inputs, out double[] responses) - { - double output; // Compute using elimination method as default. - return Compute(inputs, MulticlassComputeMethod.Elimination, out responses, out output); - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// The - /// multi-class classification method to use. - /// The model response for each class. - /// The output of the machine. If this is a - /// probabilistic machine, the - /// output is the probability of the positive class. If this is - /// a standard machine, the output is the distance to the decision - /// hyperplane in feature space. - /// - /// The decision label for the given input. - /// - public int Compute(double[] inputs, MulticlassComputeMethod method, out double[] responses, out double output) - { - if (method == MulticlassComputeMethod.Voting) - { - int[] votes; - int result = computeVoting(inputs, out votes, out output); - - responses = new double[votes.Length]; - for (int i = 0; i < responses.Length; i++) - responses[i] = votes[i] * (2.0 / (Classes * (Classes - 1))); - - return result; - } - else - { - return computeElimination(inputs, out responses, out output, null); - } - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// The - /// multi-class classification method to use. - /// The model response for each class. - /// - /// The class decision for the given input. - /// - public int Compute(double[] inputs, MulticlassComputeMethod method, out double[] responses) - { - double output; - return Compute(inputs, method, out responses, out output); - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// The - /// multi-class classification method to use. - /// The output of the machine. If this is a - /// probabilistic machine, the - /// output is the probability of the positive class. If this is - /// a standard machine, the output is the distance to the decision - /// hyperplane in feature space. - /// - /// The class decision for the given input. - /// - public int Compute(double[] inputs, MulticlassComputeMethod method, out double output) - { - if (method == MulticlassComputeMethod.Voting) - { - int[] votes; - return computeVoting(inputs, out votes, out output); - } - else - { - double[] responses; - return computeElimination(inputs, out responses, out output, null); - } - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// The - /// multi-class classification method to use. - /// - /// The class decision for the given input. - /// - public int Compute(double[] inputs, MulticlassComputeMethod method) - { - double output; - return Compute(inputs, method, out output); - } - #endregion - - - #region Private Multi-class Decision - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// An input vector. - /// A vector containing the number of votes for each class. - /// The output of the machine. If this is a - /// probabilistic machine, the - /// output is the probability of the positive class. If this is - /// a standard machine, the output is the distance to the decision - /// hyperplane in feature space. - /// - /// The decision label for the given input. - /// - private int computeVoting(double[] inputs, out int[] votes, out double output) - { - // Compute decision by Voting - - // Get a list of the shared vectors (lazy) - int[][][] vectors = this.sharedVectors.Value; - - // Get the cache for this thread - Cache cache = createOrResetCache(); - - // out variables cannot be passed into delegates, - // so will be creating a copy for the vote array. - int[] voting = new int[Classes]; - - // For each class - Parallel.For(0, Classes, i => - { - // For each other class - for (int j = 0; j < i; j++) - { - double machineOutput; - - // Retrieve and compute the two-class problem for classes i x j - int answer = computeSequential(i, j, inputs, out machineOutput, cache); - - // Determine the winner class - int y = (answer == -1) ? i : j; - - // Increment votes for the winner - Interlocked.Increment(ref voting[y]); - } - }); - - // Voting finished. - votes = voting; - - // Select class which maximum number of votes - int imax; output = Matrix.Max(votes, out imax); - - // Determine output probability using no. of votes - output = output * (2.0 / (Classes * (Classes - 1))); - - return imax; // Return the winner as the output. - } - - /// - /// Computes the given input to produce the corresponding output. - /// - /// - /// - /// This method computes the decision for a one-against-one multiclass - /// support vector machine using the Directed Acyclic Graph method by - /// Platt, Cristianini and Shawe-Taylor. Details are given on the - /// original paper "Large Margin DAGs for Multiclass Classification", 2000. - /// - /// - /// An input vector. - /// The model response for each class. - /// The output of the machine. If this is a - /// probabilistic machine, the - /// output is the probability of the positive class. If this is - /// a standard machine, the output is the distance to the decision - /// hyperplane in feature space. - /// The decision path followed by the Decision - /// Directed Acyclic Graph used by the - /// elimination method. - /// - /// The decision label for the given input. - /// - private int computeElimination(double[] inputs, out double[] responses, - out double output, Tuple[] decisionPath) - { - // Compute decision by Directed Acyclic Graph - - // Get a list of the shared vectors - int[][][] vectors = this.sharedVectors.Value; - - // Get the cache for this thread - Cache cache = createOrResetCache(); - - output = 0; - - // Initialize metrics - responses = new double[Classes]; - bool probabilistic = IsProbabilistic; - - if (probabilistic) - { - for (int i = 0; i < responses.Length; i++) - responses[i] = 1.0; - } - - // Start with first and last classes - int classA = Classes - 1, classB = 0; - int progress = 0; - - // Navigate decision path - while (classA != classB) - { - - // Compute the two-class decision problem to decide for A x B - int answer = computeParallel(classA, classB, inputs, out output, cache); - - if (decisionPath != null) - decisionPath[progress++] = Tuple.Create(classA, classB); - - // Check who won and update - - if (answer == -1) - { - // The class A has won and class B has lost - - if (probabilistic) - { - // Decrease loser likelihood - responses[classB] *= output; - - // Increase for all other classes - for (int i = 0; i < responses.Length; i++) - if (i != classB) responses[i] *= 1.0 - output; - } - else - { - // Store the distance to the - // answer for the loser class - responses[classB] = -output; - } - - // Advance classB towards - // the middle of the list - classB++; - } - - else // answer == +1 - { - // The class A has lost and class B has won - - if (probabilistic) - { - // Decrease loser likelihood - responses[classA] *= 1.0 - output; - - // Increase for all other classes - for (int i = 0; i < responses.Length; i++) - if (i != classA) responses[i] *= output; - } - else - { - // Store the distance to the - // answer for the loser class - responses[classA] = output; - } - - // Advance classA towards - // the middle of the list - classA--; - } - } - - // At this point, classA = classB is the winner - if (!probabilistic) responses[classA] = output; - -#if DEBUG - else - { - int imax; responses.Max(out imax); - if (imax != classA) throw new Exception(); - } -#endif - - // Return output for winner class - output = responses[classA]; - - return classA; - } - - #endregion - - - #region Private Single machine Decision - /// - /// Compute SVM output with support vector sharing. - /// - /// - private int computeSequential(int classA, int classB, double[] input, out double output, Cache cache) - { - // Get the machine for this problem - KernelSupportVectorMachine machine = machines[classA - 1][classB]; - - // Get the vectors shared among all machines - int[] vectors = cache.Vectors[classA - 1][classB]; - double[] values = cache.Products; - - double sum = machine.Threshold; - - - if (machine.IsCompact) - { - // For linear machines, computation is simpler - for (int i = 0; i < machine.Weights.Length; i++) - sum += machine.Weights[i] * input[i]; - } - else - { - // For each support vector in the machine - for (int i = 0; i < vectors.Length; i++) - { - double value; - - // Check if it is a shared vector - int j = vectors[i]; - - if (j >= 0) - { - // This is a shared vector. Check - // if it has already been computed - - if (!Double.IsNaN(values[j])) - { - // Yes, it has. Retrieve the value from the cache - value = values[j]; - } - else - { - // No, it has not. Compute and store the computed value in the cache - value = values[j] = machine.Kernel.Function(machine.SupportVectors[i], input); - Interlocked.Increment(ref cache.Evaluations); - } - } - else - { - // This vector is not shared by any other machine. No need to cache - value = machine.Kernel.Function(machine.SupportVectors[i], input); - Interlocked.Increment(ref cache.Evaluations); - } - - sum += machine.Weights[i] * value; - } - } - - - // Produce probabilities if required - if (machine.IsProbabilistic) - { - output = machine.Link.Inverse(sum); - return output >= 0.5 ? +1 : -1; - } - else - { - output = sum; - return output >= 0 ? +1 : -1; - } - } - - /// - /// Compute SVM output with support vector sharing. - /// - /// - private int computeParallel(int classA, int classB, double[] input, out double output, Cache cache) - { - // Get the machine for this problem - KernelSupportVectorMachine machine = machines[classA - 1][classB]; - - // Get the vectors shared among all machines - int[] vectors = cache.Vectors[classA - 1][classB]; - - double[] values = cache.Products; -#if !NET35 - SpinLock[] locks = cache.SyncObjects; -#endif - double sum = machine.Threshold; - - - if (machine.IsCompact) + /// + /// + /// + /// + /// + /// + /// + /// + /// + [Serializable] + public class MulticlassSupportVectorMachine : ISupportVectorMachine, + IEnumerable, KernelSupportVectorMachine>>, IDisposable + { + + // List of underlying binary classifiers + private KernelSupportVectorMachine[][] machines; + + // Multi-class statistics + private int? totalVectorsCount; + private int? uniqueVectorsCount; + private int? sharedVectorsCount; + + + // Performance optimizations + [NonSerialized] + private Lazy sharedVectors; + + [NonSerialized] + private ThreadLocal vectorCache; + + + /// + /// Constructs a new Multi-class Kernel Support Vector Machine + /// + /// + /// The number of inputs for the machine. If sequences have + /// varying length, pass zero to this parameter and pass a suitable sequence + /// kernel to this constructor, such as . + /// The number of classes in the classification problem. + /// + /// + /// If the number of inputs is zero, this means the machine + /// accepts a indefinite number of inputs. This is often the + /// case for kernel vector machines using a sequence kernel. + /// + /// + public MulticlassSupportVectorMachine(int inputs, int classes) + : this(inputs, new Linear(), classes) + { + } + + /// + /// Constructs a new Multi-class Kernel Support Vector Machine + /// + /// + /// The chosen kernel for the machine. Default is to + /// use the kernel. + /// The number of inputs for the machine. If sequences have + /// varying length, pass zero to this parameter and pass a suitable sequence + /// kernel to this constructor, such as . + /// The number of classes in the classification problem. + /// + /// + /// If the number of inputs is zero, this means the machine + /// accepts a indefinite number of inputs. This is often the + /// case for kernel vector machines using a sequence kernel. + /// + /// + public MulticlassSupportVectorMachine(int inputs, IKernel kernel, int classes) + { + if (classes <= 1) + throw new ArgumentException("The machine must have at least two classes.", "classes"); + + // Create the kernel machines + machines = new KernelSupportVectorMachine[classes - 1][]; + for (int i = 0; i < machines.Length; i++) + { + machines[i] = new KernelSupportVectorMachine[i + 1]; + + for (int j = 0; j <= i; j++) + machines[i][j] = new KernelSupportVectorMachine(kernel, inputs); + } + + this.initialize(); + } + + /// + /// Constructs a new Multi-class Kernel Support Vector Machine + /// + /// + /// + /// The machines to be used in each of the pair-wise class subproblems. + /// + /// + public MulticlassSupportVectorMachine(KernelSupportVectorMachine[][] machines) + { + if (machines == null) + throw new ArgumentNullException("machines"); + + this.machines = machines; + this.initialize(); + } + + private void initialize() + { + this.vectorCache = new ThreadLocal(() => new Cache()); + this.sharedVectors = new Lazy(computeSharedVectors, true); + } + + + + + #region Properties + /// + /// Gets the classifier for against . + /// + /// + /// + /// If the index of is greater than , + /// the classifier for the against + /// will be returned instead. If both indices are equal, null will be + /// returned instead. + /// + /// + public KernelSupportVectorMachine this[int class1, int class2] + { + get + { + if (class1 == class2) + return null; + if (class1 > class2) + return machines[class1 - 1][class2]; + else + return machines[class2 - 1][class1]; + } + } + + /// + /// Gets the total number of machines + /// in this multi-class classifier. + /// + /// + public int MachinesCount + { + get { return ((machines.Length + 1) * machines.Length) / 2; } + } + + /// + /// Gets the total number of support vectors + /// in the entire multi-class machine. + /// + /// + public int SupportVectorCount + { + get + { + if (totalVectorsCount == null) + { + int count = 0; + for (int i = 0; i < machines.Length; i++) + for (int j = 0; j < machines[i].Length; j++) + if (machines[i][j].SupportVectors != null) + count += machines[i][j].SupportVectors.Length; + totalVectorsCount = count; + } + + return totalVectorsCount.Value; + } + } + + /// + /// Gets the number of unique support + /// vectors in the multi-class machine. + /// + /// + public int SupportVectorUniqueCount + { + get + { + if (uniqueVectorsCount == null) + { + HashSet unique = new HashSet(); + for (int i = 0; i < machines.Length; i++) + { + for (int j = 0; j < machines[i].Length; j++) + { + if (machines[i][j].SupportVectors != null) + { + for (int k = 0; k < machines[i][j].SupportVectors.Length; k++) + unique.Add(machines[i][j].SupportVectors[k]); + } + } + } + + uniqueVectorsCount = unique.Count; + } + + return uniqueVectorsCount.Value; + } + } + + /// + /// Gets the number of shared support + /// vectors in the multi-class machine. + /// + /// + public int SupportVectorSharedCount + { + get + { + if (sharedVectorsCount == null) + { + var v = sharedVectors.Value; + } + return sharedVectorsCount.Value; + } + } + + /// + /// Gets the number of classes. + /// + /// + public int Classes + { + get { return machines.Length + 1; } + } + + /// + /// Gets the number of inputs of the machines. + /// + /// + public int Inputs + { + get { return machines[0][0].Inputs; } + } + + /// + /// Gets a value indicating whether this machine produces probabilistic outputs. + /// + /// + /// + /// true if this machine produces probabilistic outputs; otherwise, false. + /// + /// + public bool IsProbabilistic + { + get { return machines[0][0].IsProbabilistic; } + } + + /// + /// Gets the subproblems classifiers. + /// + /// + public KernelSupportVectorMachine[][] Machines + { + get { return machines; } + } + #endregion + + + #region Public Compute Overloads + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// + /// The decision label for the given input. + /// + public int Compute(params double[] inputs) + { + double output; // Compute using elimination method as default. + return Compute(inputs, MulticlassComputeMethod.Elimination, out output); + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// The output of the machine. If this is a + /// probabilistic machine, the + /// output is the probability of the positive class. If this is + /// a standard machine, the output is the distance to the decision + /// hyperplane in feature space. + /// + /// The decision label for the given input. + /// + /// + public int Compute(double[] inputs, out double output) + { + // Compute using elimination method as default. + return Compute(inputs, MulticlassComputeMethod.Elimination, out output); + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// The output of the machine. If this is a + /// probabilistic machine, the + /// output is the probability of the positive class. If this is + /// a standard machine, the output is the distance to the decision + /// hyperplane in feature space. + /// The decision path followed by the Decision + /// Directed Acyclic Graph used by the + /// elimination method. + /// + /// The decision label for the given input. + /// + /// + public int Compute(double[] inputs, out double output, out Tuple[] decisionPath) + { + double[] responses; + decisionPath = new Tuple[Classes - 1]; + return computeElimination(inputs, out responses, out output, decisionPath); + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// The model response for each class. + /// + /// The decision label for the given input. + /// + public int Compute(double[] inputs, out double[] responses) + { + double output; // Compute using elimination method as default. + return Compute(inputs, MulticlassComputeMethod.Elimination, out responses, out output); + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// The + /// multi-class classification method to use. + /// The model response for each class. + /// The output of the machine. If this is a + /// probabilistic machine, the + /// output is the probability of the positive class. If this is + /// a standard machine, the output is the distance to the decision + /// hyperplane in feature space. + /// + /// The decision label for the given input. + /// + public int Compute(double[] inputs, MulticlassComputeMethod method, out double[] responses, out double output) + { + if (method == MulticlassComputeMethod.Voting) + { + int[] votes; + int result = computeVoting(inputs, out votes, out output); + + responses = new double[votes.Length]; + for (int i = 0; i < responses.Length; i++) + responses[i] = votes[i] * (2.0 / (Classes * (Classes - 1))); + + return result; + } + else + { + return computeElimination(inputs, out responses, out output, null); + } + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// The + /// multi-class classification method to use. + /// The model response for each class. + /// + /// The class decision for the given input. + /// + public int Compute(double[] inputs, MulticlassComputeMethod method, out double[] responses) + { + double output; + return Compute(inputs, method, out responses, out output); + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// The + /// multi-class classification method to use. + /// The output of the machine. If this is a + /// probabilistic machine, the + /// output is the probability of the positive class. If this is + /// a standard machine, the output is the distance to the decision + /// hyperplane in feature space. + /// + /// The class decision for the given input. + /// + public int Compute(double[] inputs, MulticlassComputeMethod method, out double output) + { + if (method == MulticlassComputeMethod.Voting) + { + int[] votes; + return computeVoting(inputs, out votes, out output); + } + else + { + double[] responses; + return computeElimination(inputs, out responses, out output, null); + } + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// The + /// multi-class classification method to use. + /// + /// The class decision for the given input. + /// + public int Compute(double[] inputs, MulticlassComputeMethod method) + { + double output; + return Compute(inputs, method, out output); + } + #endregion + + + #region Private Multi-class Decision + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// An input vector. + /// A vector containing the number of votes for each class. + /// The output of the machine. If this is a + /// probabilistic machine, the + /// output is the probability of the positive class. If this is + /// a standard machine, the output is the distance to the decision + /// hyperplane in feature space. + /// + /// The decision label for the given input. + /// + private int computeVoting(double[] inputs, out int[] votes, out double output) + { + // Compute decision by Voting + + // Get a list of the shared vectors (lazy) + int[][][] vectors = this.sharedVectors.Value; + + // Get the cache for this thread + Cache cache = createOrResetCache(); + + // out variables cannot be passed into delegates, + // so will be creating a copy for the vote array. + int[] voting = new int[Classes]; + + // For each class + Parallel.For(0, Classes, i => + { + // For each other class + for (int j = 0; j < i; j++) + { + double machineOutput; + + // Retrieve and compute the two-class problem for classes i x j + int answer = computeSequential(i, j, inputs, out machineOutput, cache); + + // Determine the winner class + int y = (answer == -1) ? i : j; + + // Increment votes for the winner + Interlocked.Increment(ref voting[y]); + } + }); + + // Voting finished. + votes = voting; + + // Select class which maximum number of votes + int imax; output = Matrix.Max(votes, out imax); + + // Determine output probability using no. of votes + output = output * (2.0 / (Classes * (Classes - 1))); + + return imax; // Return the winner as the output. + } + + /// + /// Computes the given input to produce the corresponding output. + /// + /// + /// + /// This method computes the decision for a one-against-one multiclass + /// support vector machine using the Directed Acyclic Graph method by + /// Platt, Cristianini and Shawe-Taylor. Details are given on the + /// original paper "Large Margin DAGs for Multiclass Classification", 2000. + /// + /// + /// An input vector. + /// The model response for each class. + /// The output of the machine. If this is a + /// probabilistic machine, the + /// output is the probability of the positive class. If this is + /// a standard machine, the output is the distance to the decision + /// hyperplane in feature space. + /// The decision path followed by the Decision + /// Directed Acyclic Graph used by the + /// elimination method. + /// + /// The decision label for the given input. + /// + private int computeElimination(double[] inputs, out double[] responses, + out double output, Tuple[] decisionPath) + { + // Compute decision by Directed Acyclic Graph + + // Get a list of the shared vectors + int[][][] vectors = this.sharedVectors.Value; + + // Get the cache for this thread + Cache cache = createOrResetCache(); + + output = 0; + + // Initialize metrics + responses = new double[Classes]; + bool probabilistic = IsProbabilistic; + + if (probabilistic) + { + for (int i = 0; i < responses.Length; i++) + responses[i] = 1.0; + } + + // Start with first and last classes + int classA = Classes - 1, classB = 0; + int progress = 0; + + // Navigate decision path + while (classA != classB) + { + + // Compute the two-class decision problem to decide for A x B + int answer = computeParallel(classA, classB, inputs, out output, cache); + + if (decisionPath != null) + decisionPath[progress++] = Tuple.Create(classA, classB); + + // Check who won and update + + if (answer == -1) + { + // The class A has won and class B has lost + + if (probabilistic) + { + // Decrease loser likelihood + responses[classB] *= output; + + // Increase for all other classes + for (int i = 0; i < responses.Length; i++) + if (i != classB) responses[i] *= 1.0 - output; + } + else + { + // Store the distance to the + // answer for the loser class + responses[classB] = -output; + } + + // Advance classB towards + // the middle of the list + classB++; + } + + else // answer == +1 + { + // The class A has lost and class B has won + + if (probabilistic) + { + // Decrease loser likelihood + responses[classA] *= 1.0 - output; + + // Increase for all other classes + for (int i = 0; i < responses.Length; i++) + if (i != classA) responses[i] *= output; + } + else + { + // Store the distance to the + // answer for the loser class + responses[classA] = output; + } + + // Advance classA towards + // the middle of the list + classA--; + } + } + + // At this point, classA = classB is the winner + if (!probabilistic) responses[classA] = output; + +#if DEBUG + else + { + int imax; responses.Max(out imax); + if (imax != classA) throw new Exception(); + } +#endif + + // Return output for winner class + output = responses[classA]; + + return classA; + } + + #endregion + + + #region Private Single machine Decision + /// + /// Compute SVM output with support vector sharing. + /// + /// + private int computeSequential(int classA, int classB, double[] input, out double output, Cache cache) + { + // Get the machine for this problem + KernelSupportVectorMachine machine = machines[classA - 1][classB]; + + // Get the vectors shared among all machines + int[] vectors = cache.Vectors[classA - 1][classB]; + double[] values = cache.Products; + + double sum = machine.Threshold; + + + if (machine.IsCompact) + { + // For linear machines, computation is simpler + for (int i = 0; i < machine.Weights.Length; i++) + sum += machine.Weights[i] * input[i]; + } + else + { + // For each support vector in the machine + for (int i = 0; i < vectors.Length; i++) + { + double value; + + // Check if it is a shared vector + int j = vectors[i]; + + if (j >= 0) + { + // This is a shared vector. Check + // if it has already been computed + + if (!Double.IsNaN(values[j])) + { + // Yes, it has. Retrieve the value from the cache + value = values[j]; + } + else + { + // No, it has not. Compute and store the computed value in the cache + value = values[j] = machine.Kernel.Function(machine.SupportVectors[i], input); + Interlocked.Increment(ref cache.Evaluations); + } + } + else + { + // This vector is not shared by any other machine. No need to cache + value = machine.Kernel.Function(machine.SupportVectors[i], input); + Interlocked.Increment(ref cache.Evaluations); + } + + sum += machine.Weights[i] * value; + } + } + + + // Produce probabilities if required + if (machine.IsProbabilistic) + { + output = machine.Link.Inverse(sum); + return output >= 0.5 ? +1 : -1; + } + else + { + output = sum; + return output >= 0 ? +1 : -1; + } + } + + /// + /// Compute SVM output with support vector sharing. + /// + /// + private int computeParallel(int classA, int classB, double[] input, out double output, Cache cache) + { + // Get the machine for this problem + KernelSupportVectorMachine machine = machines[classA - 1][classB]; + + // Get the vectors shared among all machines + int[] vectors = cache.Vectors[classA - 1][classB]; + + double[] values = cache.Products; +#if !NET35 + SpinLock[] locks = cache.SyncObjects; +#endif + double sum = machine.Threshold; + + + if (machine.IsCompact) { if (machine.Weights == null) - throw new Exception(); - - // For linear machines, computation is simpler - for (int i = 0; i < machine.Weights.Length; i++) - sum += machine.Weights[i] * input[i]; - } - else - { - -#if NET35 - #region Backward compatibility - for (int i = 0; i < vectors.Length; i++) - { - double value; - - // Check if it is a shared vector - int j = vectors[i]; - - if (j >= 0) - { - // This is a shared vector. Check - // if it has already been computed - - if (!Double.IsNaN(values[j])) - { - // Yes, it has. Retrieve the value from the cache - value = values[j]; - } - else - { - // No, it has not. Compute and store the computed value in the cache - value = values[j] = machine.Kernel.Function(machine.SupportVectors[i], input); - Interlocked.Increment(ref cache.Evaluations); - } - } - else - { - // This vector is not shared by any other machine. No need to cache - value = machine.Kernel.Function(machine.SupportVectors[i], input); - Interlocked.Increment(ref cache.Evaluations); - } - - sum += machine.Weights[i] * value; - } - #endregion -#else - // For each support vector in the machine - Parallel.For(0, vectors.Length, - - // Init - () => 0.0, - - // Map - (i, state, partialSum) => - { - double value; - - // Check if it is a shared vector - int j = vectors[i]; - - if (j >= 0) - { - // This is a shared vector. Check - // if it has already been computed - - bool taken = false; - locks[j].Enter(ref taken); - - if (!Double.IsNaN(values[j])) - { - // Yes, it has. Retrieve the value from the cache - value = values[j]; - } - else - { - // No, it has not. Compute and store the computed value in the cache - value = values[j] = machine.Kernel.Function(machine.SupportVectors[i], input); - Interlocked.Increment(ref cache.Evaluations); - } - - locks[j].Exit(); - } - else - { - // This vector is not shared by any other machine. No need to cache - value = machine.Kernel.Function(machine.SupportVectors[i], input); - Interlocked.Increment(ref cache.Evaluations); - } - - return partialSum + machine.Weights[i] * value; - }, - - // Reduce - (partialSum) => { lock (locks) sum += partialSum; } - ); -#endif - } - - // Produce probabilities if required - if (machine.IsProbabilistic) - { - output = machine.Link.Inverse(sum); - return output >= 0.5 ? +1 : -1; - } - else - { - output = sum; - return output >= 0 ? +1 : -1; - } - } - #endregion - - - - - - /// - /// Resets the cache and machine statistics - /// so they can be recomputed on next evaluation. - /// - /// - public void Reset() - { - if (this.vectorCache != null) - this.vectorCache.Dispose(); - - this.sharedVectors = null; - this.totalVectorsCount = null; - this.uniqueVectorsCount = null; - this.sharedVectorsCount = null; - - this.initialize(); - } - - /// - /// Gets the total kernel evaluations performed - /// in the last call to any of the - /// functions in the current thread. - /// - /// - /// The number of total kernel evaluations. - /// - public int GetLastKernelEvaluations() - { - return vectorCache.Value.Evaluations; - } - - - #region Loading & Saving - - /// - /// Saves the machine to a stream. - /// - /// - /// The stream to which the machine is to be serialized. - /// - public void Save(Stream stream) - { - BinaryFormatter b = new BinaryFormatter(); - b.Serialize(stream, this); - } - - /// - /// Saves the machine to a file. - /// - /// - /// The path to the file to which the machine is to be serialized. - /// - public void Save(string path) - { - using (FileStream fs = new FileStream(path, FileMode.Create)) - { - Save(fs); - } - } - - /// - /// Loads a machine from a stream. - /// - /// - /// The stream from which the machine is to be deserialized. - /// - /// The deserialized machine. - /// - public static MulticlassSupportVectorMachine Load(Stream stream) - { - BinaryFormatter b = new BinaryFormatter(); - return (MulticlassSupportVectorMachine)b.Deserialize(stream); - } - - /// - /// Loads a machine from a file. - /// - /// - /// The path to the file from which the machine is to be deserialized. - /// - /// The deserialized machine. - /// - public static MulticlassSupportVectorMachine Load(string path) - { - using (FileStream fs = new FileStream(path, FileMode.Open)) - { - return Load(fs); - } - } - - [OnDeserialized] - private void onDeserialized(StreamingContext context) - { - initialize(); - } - #endregion - - #region IEnumerable members - /// - /// Returns an enumerator that iterates through all machines - /// contained inside this multi-class support vector machine. - /// - /// - public IEnumerator, KernelSupportVectorMachine>> GetEnumerator() - { - for (int i = 0; i < machines.Length; i++) - { - for (int j = 0; j < machines[i].Length; j++) - { - yield return new KeyValuePair, KernelSupportVectorMachine>( - Tuple.Create(i + 1, j), machines[i][j]); - } - } - } - - /// - /// Returns an enumerator that iterates through all machines - /// contained inside this multi-class support vector machine. - /// - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion - - #region IDisposable members - /// - /// Performs application-defined tasks associated with - /// freeing, releasing, or resetting unmanaged resources. - /// - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// - /// - /// true to release both managed and unmanaged resources; - /// false to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - // free managed resources - if (vectorCache != null) - { - vectorCache.Dispose(); - vectorCache = null; - } - } - } - #endregion - - - - #region Cache - private Cache createOrResetCache() - { - Cache cache = vectorCache.Value; - - // First of all, check if the shared vectors in this machine - // already have been identified. If they don't, identify them. - - cache.Vectors = sharedVectors.Value; // use lazy instantiation - int vectorCount = SupportVectorSharedCount; - - // Now, check if a cache has already been created for this - // thread and has adequate size. If it has not, create it. - - if (cache.Products == null || cache.Products.Length < vectorCount) - { - // The cache has not been created - cache.Products = new double[vectorCount]; - -#if !NET35 // Create synchronization objects - cache.SyncObjects = new SpinLock[vectorCount]; - for (int i = 0; i < cache.SyncObjects.Length; i++) - cache.SyncObjects[i] = new SpinLock(); -#endif - } - - // Initialize (or reset) the cache. A value of Not-a-Number - // indicates that the value of corresponding vector has not - // been computed yet. - for (int i = 0; i < cache.Products.Length; i++) - cache.Products[i] = Double.NaN; - - - cache.Evaluations = 0; - - return cache; - } - - - private int[][][] computeSharedVectors() - { - // This method should only be called once after the machine has - // been learned. If the inner machines or they Support Vectors - // change, this method will need to be recomputed. - - // Detect all vectors which are being shared along the machines - var shared = new Dictionary>>(); - - // for all machines - for (int i = 0; i < machines.Length; i++) - { - for (int j = 0; j < machines[i].Length; j++) - { - // if the machine is not in compact form - if (machines[i][j].SupportVectors != null) - { - // register the support vector on the shared cache collection - for (int k = 0; k < machines[i][j].SupportVectors.Length; k++) - { - double[] sv = machines[i][j].SupportVectors[k]; - - List> count; - bool success = shared.TryGetValue(sv, out count); - - if (success) - { - // Value is already in the dictionary - count.Add(Tuple.Create(i, j, k)); - } - else - { - count = new List>(); - count.Add(Tuple.Create(i, j, k)); - shared[sv] = count; - } - } - } - } - } - - // Create a table of indices for shared vectors - int idx = 0; - - var indices = new Dictionary(); - foreach (double[] sv in shared.Keys) - indices[sv] = idx++; - - // Create a lookup table for the machines - int[][][] sharedVectors = new int[machines.Length][][]; - for (int i = 0; i < sharedVectors.Length; i++) - { - sharedVectors[i] = new int[machines[i].Length][]; - for (int j = 0; j < sharedVectors[i].Length; j++) - { - if (machines[i][j].SupportVectors != null) - { - sharedVectors[i][j] = new int[machines[i][j].SupportVectors.Length]; - - for (int k = 0; k < machines[i][j].SupportVectors.Length; k++) - { - double[] sv = machines[i][j].SupportVectors[k]; - if (shared.ContainsKey(sv)) - sharedVectors[i][j][k] = indices[sv]; - else - sharedVectors[i][j][k] = -1; - } - } - } - } - - sharedVectorsCount = shared.Count; - return sharedVectors; - } - - private class Cache - { - public int Evaluations; - public double[] Products; - public int[][][] Vectors; -#if !NET35 - public SpinLock[] SyncObjects; -#endif - } - - #endregion - - } -} + throw new Exception(); + + // For linear machines, computation is simpler + for (int i = 0; i < machine.Weights.Length; i++) + sum += machine.Weights[i] * input[i]; + } + else + { + +#if NET35 + #region Backward compatibility + for (int i = 0; i < vectors.Length; i++) + { + double value; + + // Check if it is a shared vector + int j = vectors[i]; + + if (j >= 0) + { + // This is a shared vector. Check + // if it has already been computed + + if (!Double.IsNaN(values[j])) + { + // Yes, it has. Retrieve the value from the cache + value = values[j]; + } + else + { + // No, it has not. Compute and store the computed value in the cache + value = values[j] = machine.Kernel.Function(machine.SupportVectors[i], input); + Interlocked.Increment(ref cache.Evaluations); + } + } + else + { + // This vector is not shared by any other machine. No need to cache + value = machine.Kernel.Function(machine.SupportVectors[i], input); + Interlocked.Increment(ref cache.Evaluations); + } + + sum += machine.Weights[i] * value; + } + #endregion +#else + // For each support vector in the machine + Parallel.For(0, vectors.Length, + + // Init + () => 0.0, + + // Map + (i, state, partialSum) => + { + double value; + + // Check if it is a shared vector + int j = vectors[i]; + + if (j >= 0) + { + // This is a shared vector. Check + // if it has already been computed + + bool taken = false; + locks[j].Enter(ref taken); + + if (!Double.IsNaN(values[j])) + { + // Yes, it has. Retrieve the value from the cache + value = values[j]; + } + else + { + // No, it has not. Compute and store the computed value in the cache + value = values[j] = machine.Kernel.Function(machine.SupportVectors[i], input); + Interlocked.Increment(ref cache.Evaluations); + } + + locks[j].Exit(); + } + else + { + // This vector is not shared by any other machine. No need to cache + value = machine.Kernel.Function(machine.SupportVectors[i], input); + Interlocked.Increment(ref cache.Evaluations); + } + + return partialSum + machine.Weights[i] * value; + }, + + // Reduce + (partialSum) => { lock (locks) sum += partialSum; } + ); +#endif + } + + // Produce probabilities if required + if (machine.IsProbabilistic) + { + output = machine.Link.Inverse(sum); + return output >= 0.5 ? +1 : -1; + } + else + { + output = sum; + return output >= 0 ? +1 : -1; + } + } + #endregion + + + + + + /// + /// Resets the cache and machine statistics + /// so they can be recomputed on next evaluation. + /// + /// + public void Reset() + { + if (this.vectorCache != null) + this.vectorCache.Dispose(); + + this.sharedVectors = null; + this.totalVectorsCount = null; + this.uniqueVectorsCount = null; + this.sharedVectorsCount = null; + + this.initialize(); + } + + /// + /// Gets the total kernel evaluations performed + /// in the last call to any of the + /// functions in the current thread. + /// + /// + /// The number of total kernel evaluations. + /// + public int GetLastKernelEvaluations() + { + return vectorCache.Value.Evaluations; + } + + + #region Loading & Saving + + /// + /// Saves the machine to a stream. + /// + /// + /// The stream to which the machine is to be serialized. + /// + public void Save(Stream stream) + { + BinaryFormatter b = new BinaryFormatter(); + b.Serialize(stream, this); + } + + /// + /// Saves the machine to a file. + /// + /// + /// The path to the file to which the machine is to be serialized. + /// + public void Save(string path) + { + using (FileStream fs = new FileStream(path, FileMode.Create)) + { + Save(fs); + } + } + + /// + /// Loads a machine from a stream. + /// + /// + /// The stream from which the machine is to be deserialized. + /// + /// The deserialized machine. + /// + public static MulticlassSupportVectorMachine Load(Stream stream) + { + BinaryFormatter b = new BinaryFormatter(); + return (MulticlassSupportVectorMachine)b.Deserialize(stream); + } + + /// + /// Loads a machine from a file. + /// + /// + /// The path to the file from which the machine is to be deserialized. + /// + /// The deserialized machine. + /// + public static MulticlassSupportVectorMachine Load(string path) + { + using (FileStream fs = new FileStream(path, FileMode.Open)) + { + return Load(fs); + } + } + + [OnDeserialized] + private void onDeserialized(StreamingContext context) + { + initialize(); + } + #endregion + + #region IEnumerable members + /// + /// Returns an enumerator that iterates through all machines + /// contained inside this multi-class support vector machine. + /// + /// + public IEnumerator, KernelSupportVectorMachine>> GetEnumerator() + { + for (int i = 0; i < machines.Length; i++) + { + for (int j = 0; j < machines[i].Length; j++) + { + yield return new KeyValuePair, KernelSupportVectorMachine>( + Tuple.Create(i + 1, j), machines[i][j]); + } + } + } + + /// + /// Returns an enumerator that iterates through all machines + /// contained inside this multi-class support vector machine. + /// + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion + + #region IDisposable members + /// + /// Performs application-defined tasks associated with + /// freeing, releasing, or resetting unmanaged resources. + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// + /// + /// true to release both managed and unmanaged resources; + /// false to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // free managed resources + if (vectorCache != null) + { + vectorCache.Dispose(); + vectorCache = null; + } + } + } + #endregion + + + + #region Cache + private Cache createOrResetCache() + { + Cache cache = vectorCache.Value; + + // First of all, check if the shared vectors in this machine + // already have been identified. If they don't, identify them. + + cache.Vectors = sharedVectors.Value; // use lazy instantiation + int vectorCount = SupportVectorSharedCount; + + // Now, check if a cache has already been created for this + // thread and has adequate size. If it has not, create it. + + if (cache.Products == null || cache.Products.Length < vectorCount) + { + // The cache has not been created + cache.Products = new double[vectorCount]; + +#if !NET35 // Create synchronization objects + cache.SyncObjects = new SpinLock[vectorCount]; + for (int i = 0; i < cache.SyncObjects.Length; i++) + cache.SyncObjects[i] = new SpinLock(); +#endif + } + + // Initialize (or reset) the cache. A value of Not-a-Number + // indicates that the value of corresponding vector has not + // been computed yet. + for (int i = 0; i < cache.Products.Length; i++) + cache.Products[i] = Double.NaN; + + + cache.Evaluations = 0; + + return cache; + } + + + private int[][][] computeSharedVectors() + { + // This method should only be called once after the machine has + // been learned. If the inner machines or they Support Vectors + // change, this method will need to be recomputed. + + // Detect all vectors which are being shared along the machines + var shared = new Dictionary>>(); + + // for all machines + for (int i = 0; i < machines.Length; i++) + { + for (int j = 0; j < machines[i].Length; j++) + { + // if the machine is not in compact form + if (machines[i][j].SupportVectors != null) + { + // register the support vector on the shared cache collection + for (int k = 0; k < machines[i][j].SupportVectors.Length; k++) + { + double[] sv = machines[i][j].SupportVectors[k]; + + List> count; + bool success = shared.TryGetValue(sv, out count); + + if (success) + { + // Value is already in the dictionary + count.Add(Tuple.Create(i, j, k)); + } + else + { + count = new List>(); + count.Add(Tuple.Create(i, j, k)); + shared[sv] = count; + } + } + } + } + } + + // Create a table of indices for shared vectors + int idx = 0; + + var indices = new Dictionary(); + foreach (double[] sv in shared.Keys) + indices[sv] = idx++; + + // Create a lookup table for the machines + int[][][] sharedVectors = new int[machines.Length][][]; + for (int i = 0; i < sharedVectors.Length; i++) + { + sharedVectors[i] = new int[machines[i].Length][]; + for (int j = 0; j < sharedVectors[i].Length; j++) + { + if (machines[i][j].SupportVectors != null) + { + sharedVectors[i][j] = new int[machines[i][j].SupportVectors.Length]; + + for (int k = 0; k < machines[i][j].SupportVectors.Length; k++) + { + double[] sv = machines[i][j].SupportVectors[k]; + if (shared.ContainsKey(sv)) + sharedVectors[i][j][k] = indices[sv]; + else + sharedVectors[i][j][k] = -1; + } + } + } + } + + sharedVectorsCount = shared.Count; + return sharedVectors; + } + + private class Cache + { + public int Evaluations; + public double[] Products; + public int[][][] Vectors; +#if !NET35 + public SpinLock[] SyncObjects; +#endif + } + + #endregion + + } +} diff --git a/Sources/Accord.Statistics/Models/Accord.Statistics.Models.cd b/Sources/Accord.Statistics/Models/Accord.Statistics.Models.cd index 46a6f4a32..5b6760a40 100644 --- a/Sources/Accord.Statistics/Models/Accord.Statistics.Models.cd +++ b/Sources/Accord.Statistics/Models/Accord.Statistics.Models.cd @@ -1,227 +1,228 @@ - - - - - - - - - - - BEAAAAAAAAAAgAAAIAAAQAAAEEAAEDAAAAAAAAAAAAA= - Models\Regression\Linear\MultivariateLinearRegression.cs - Models\Regression\Linear\MultipleLinearRegression.cs - - - - - - - - - AEAQAAAAAAAAgAAEAAAAAAAQAEAAEDAAAAAAAAAAAAA= - Models\Regression\Linear\PolynomialRegression.cs - - - - - - - - - - - - AEAQAAAAAAAAgAAEAAAAAAAAAAAAEDAAAAAAAgAAAEA= - Models\Regression\Linear\SimpleLinearRegression.cs - - - - - - - - - - - - - - - AEAAAACAAAAAgAAEAAAAQABAEEAAEDAAAAAQAAAAAAA= - Models\Regression\Linear\MultipleLinearRegression.cs - - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAIAAAAAAAAAA= - Models\Regression\Nonlinear\LogisticRegression.cs - - - - - - - - - - - BEAIAAAAAoAjCAYJBACIAAQAEAEAACAAAAQgABAAAAA= - Analysis\StepwiseLogisticRegressionAnalysis.cs - - - - - - - - - - AQAAgIAAAgAAAAAAAAAAAAAAUEAAAAAARAQABAAAEAA= - Analysis\StepwiseLogisticRegressionAnalysis.cs - - - - - - - - AEAAAAAAAAAAAAAQAAACQIAYEEAAEBAAYAAABEAAABA= - Models\Regression\Nonlinear\MultinomialLogisticRegression.cs - - - - - - - - - AACQABAAAAByQgAAVAIAAAoAkAAIAAAAAYAAAAEAgII= - Models\Survival\Fitting\PartialNewtonRaphson.cs - - - - - - - IEAAAAAAAAAABAAAAAACAIAIEEAAAQAAQAAAhAAAAFA= - Models\Survival\ProportionalHazards.cs - - - - - - - - QASQABAAAABAQgAAQAIAAAgAAAAIAAAAAYAAAAAAgAI= - Models\Regression\Nonlinear\Fitting\IterativeReweightedLeastSquares.cs - - - - - - - - - AECQAhEAAAFQQgAAQAMAAAEACAAAQABAAYAAAMJAgAI= - Models\Regression\Nonlinear\Fitting\LowerBoundNewtonRaphson.cs - - - - - - - - - AAQQAFAAAABAAgAEQAIABAAAAAAAAAAAAYAAAGAAAAI= - Models\Regression\Nonlinear\Fitting\LogisticGradientDescent.cs - - - - - - - AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - Models\Regression\Linear\ILinearRegression.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI= - Models\Survival\Fitting\ISurvivalFitting.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI= - Models\Regression\Nonlinear\Fitting\Base\IMultipleRegressionFitting.cs - - - - - - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI= - Models\Regression\Nonlinear\Fitting\Base\IRegressionFitting.cs - - - + + + + + + + + + + + BEAAAAAAAAAAgAAAIAAAQAAAEEAAEDAAAAAAAAAAAAA= + Models\Regression\Linear\MultivariateLinearRegression.cs + Models\Regression\Linear\MultipleLinearRegression.cs + + + + + + + + + AEAQAAAAAAAAgAAEAAAAAAAQAEAAEDAAAAAAAAAAAAA= + Models\Regression\Linear\PolynomialRegression.cs + + + + + + + + + + + + AEAQAAAAAAAAgAAEAAAAAAAAAAAAEDAAAAAAAgAAAEA= + Models\Regression\Linear\SimpleLinearRegression.cs + + + + + + + + + + + + + + + AEAAAACAAAAAgAAEAAAAQABAEEAAEDAAAAAQAAAAAAA= + Models\Regression\Linear\MultipleLinearRegression.cs + + + + + + + AAIAAAAAAAAAAAAAAAAAAAAAAEAAAAAAIAAAAAAAAAA= + Models\Regression\Nonlinear\LogisticRegression.cs + + + + + + + + + + + BEAIAAIAAoAjCAYJBgCIAAYAEAAAACAAAAQgABAAAAA= + Analysis\StepwiseLogisticRegressionAnalysis.cs + + + + + + + + + + AQAAgIAAAgAAAAAAAAAAAAAAUEAAAAAARAQABAAAEAA= + Analysis\StepwiseLogisticRegressionAnalysis.cs + + + + + + + + AEAAAAAAAAAAAAAQAAACQIAYEEAAEBAAYAAABEAAABA= + Models\Regression\Nonlinear\MultinomialLogisticRegression.cs + + + + + + + + + AAiQABAAAAByQgAAVAIAAAoAgIAIAAAAAYAAQAEAgII= + Models\Survival\Fitting\ProportionalHazardsNewtonRaphson.cs + + + + + + + IEQAAAAAAAAABAAAAAACAIAIEEAAAQAAQAAAhAAAAFA= + Models\Survival\ProportionalHazards.cs + + + + + + + + QASQABAIAABAQgAAQAIAAggAAAAIAAAAAYAAAAAAgAI= + Models\Regression\Nonlinear\Fitting\IterativeReweightedLeastSquares.cs + + + + + + + + + AECQAhEAAAFQQgAAQAMAAAEACAAAQABAAYAAAMJAgAI= + Models\Regression\Nonlinear\Fitting\LowerBoundNewtonRaphson.cs + + + + + + + + + AAQQAFAAAABAAgAEQAIABAAAAAAAAAAAAYAAAGAAAAI= + Models\Regression\Nonlinear\Fitting\LogisticGradientDescent.cs + + + + + + + AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Models\Regression\Linear\ILinearRegression.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI= + Models\Survival\Fitting\ISurvivalFitting.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI= + Models\Regression\Nonlinear\Fitting\Base\IMultipleRegressionFitting.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI= + Models\Regression\Nonlinear\Fitting\Base\IRegressionFitting.cs + + + \ No newline at end of file diff --git a/Sources/Accord.Statistics/Models/Markov/Learning/BaumWelchLearning`1.cs b/Sources/Accord.Statistics/Models/Markov/Learning/BaumWelchLearning`1.cs index 3c07c6764..5529ac1fc 100644 --- a/Sources/Accord.Statistics/Models/Markov/Learning/BaumWelchLearning`1.cs +++ b/Sources/Accord.Statistics/Models/Markov/Learning/BaumWelchLearning`1.cs @@ -205,7 +205,7 @@ namespace Accord.Statistics.Models.Markov.Learning /// new double[] { 9, 8 }, // observation 2 of sequence 2 /// new double[] { 1, 0 }, // observation 3 of sequence 2 /// }, - /// new double[][] // sequence 3 + /// new double[][] // sequence 3 /// { /// new double[] { 1, 3 }, // observation 1 of sequence 3 /// new double[] { 8, 9 }, // observation 2 of sequence 3 diff --git a/Unit Tests/Accord.Tests.Imaging/BagOfVisualWordsTest.cs b/Unit Tests/Accord.Tests.Imaging/BagOfVisualWordsTest.cs index e326f27ea..e201288fd 100644 --- a/Unit Tests/Accord.Tests.Imaging/BagOfVisualWordsTest.cs +++ b/Unit Tests/Accord.Tests.Imaging/BagOfVisualWordsTest.cs @@ -22,6 +22,7 @@ namespace Accord.Tests.Imaging { + using Accord; using Accord.Imaging; using Accord.MachineLearning; using Accord.Math; @@ -178,6 +179,8 @@ public void GetFeatureVectorTest() [Test] public void SerializeTest() { + var images = BagOfVisualWordsTest.images.DeepClone(); + Accord.Math.Tools.SetupGenerator(0); BagOfVisualWords bow = new BagOfVisualWords(10); @@ -205,6 +208,8 @@ public void SerializeTest() [Test] public void SerializeTest2() { + var images = BagOfVisualWordsTest.images.DeepClone(); + Accord.Math.Tools.SetupGenerator(0); FastCornersDetector fast = new FastCornersDetector(); diff --git a/Unit Tests/Accord.Tests.MachineLearning/DecisionTrees/C45LearningTest.cs b/Unit Tests/Accord.Tests.MachineLearning/DecisionTrees/C45LearningTest.cs index 43fbddba3..3b010db82 100644 --- a/Unit Tests/Accord.Tests.MachineLearning/DecisionTrees/C45LearningTest.cs +++ b/Unit Tests/Accord.Tests.MachineLearning/DecisionTrees/C45LearningTest.cs @@ -488,7 +488,7 @@ public void IrisDatasetTest() double[][] inputs = new double[text.Length][]; for (int i = 0; i < inputs.Length; i++) - inputs[i] = text[i].Submatrix(4).Convert(Double.Parse); + inputs[i] = text[i].Submatrix(4).Convert(s => Double.Parse(s, System.Globalization.CultureInfo.InvariantCulture)); string[] labels = text.GetColumn(4); @@ -535,7 +535,7 @@ public void AttributeReuseTest1() double[][] inputs = new double[text.Length][]; for (int i = 0; i < inputs.Length; i++) - inputs[i] = text[i].Submatrix(4).Convert(Double.Parse); + inputs[i] = text[i].Submatrix(4).Convert(s => Double.Parse(s, System.Globalization.CultureInfo.InvariantCulture)); string[] labels = text.GetColumn(4); diff --git a/Unit Tests/Accord.Tests.Neuro/LevenbergMarquardtLearningTest.cs b/Unit Tests/Accord.Tests.Neuro/LevenbergMarquardtLearningTest.cs index f97736eda..c9c29e61e 100644 --- a/Unit Tests/Accord.Tests.Neuro/LevenbergMarquardtLearningTest.cs +++ b/Unit Tests/Accord.Tests.Neuro/LevenbergMarquardtLearningTest.cs @@ -685,7 +685,7 @@ public void ZeroLambdaTest() using (TextReader stream = new StringReader(Properties.Resources.ZeroLambda)) using (CsvReader reader = new CsvReader(stream, false)) { - data = reader.ToTable().ToMatrix(); + data = reader.ToTable().ToMatrix(System.Globalization.CultureInfo.InvariantCulture); } // number of learning samples @@ -720,12 +720,12 @@ public void ZeroLambdaTest() Neuron.RandGenerator = new ThreadSafeRandom(0); // create multi-layer neural network - ActivationNetwork network = new ActivationNetwork( + var network = new ActivationNetwork( new BipolarSigmoidFunction(5), 1, 12, 1); // create teacher - LevenbergMarquardtLearning teacher = new LevenbergMarquardtLearning(network, true); + var teacher = new LevenbergMarquardtLearning(network, true); teacher.LearningRate = 1; diff --git a/Unit Tests/Accord.Tests.Statistics/Distributions/Multivariate/IndependentTest.cs b/Unit Tests/Accord.Tests.Statistics/Distributions/Multivariate/IndependentTest.cs index 4ba92e5b9..775eed9a0 100644 --- a/Unit Tests/Accord.Tests.Statistics/Distributions/Multivariate/IndependentTest.cs +++ b/Unit Tests/Accord.Tests.Statistics/Distributions/Multivariate/IndependentTest.cs @@ -56,7 +56,7 @@ public void ConstructorTest() Assert.AreEqual(0, target.Covariance[0, 1]); Assert.AreEqual(0, target.Covariance[1, 0]); - var text = target.ToString("N2"); + var text = target.ToString("N2", System.Globalization.CultureInfo.InvariantCulture); Assert.AreEqual("Independent(x0, x1; N(x0; μ = 4.20, σ² = 1.00) + N(x1; μ = 7.00, σ² = 4.00))", text); } diff --git a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/BetaPrimeDistributionTest.cs b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/BetaPrimeDistributionTest.cs index e46b10833..b96d61682 100644 --- a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/BetaPrimeDistributionTest.cs +++ b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/BetaPrimeDistributionTest.cs @@ -23,28 +23,12 @@ namespace Accord.Tests.Statistics.Distributions.Univariate.Continuous { using Accord.Statistics.Distributions.Univariate; - using Accord.Statistics.Distributions.Univariate.Continuous; using NUnit.Framework; using System.Globalization; [TestFixture] public class BetaPrimeDistributionTest { - private TestContext testContextInstance; - - public TestContext TestContext - { - get - { - return testContextInstance; - } - set - { - testContextInstance = value; - } - } - - [Test] public void Confirm_BetPrimeDistribution_Relative_to_F_Distribution() { diff --git a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/KumaraswamyDistributionTest.cs b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/KumaraswamyDistributionTest.cs index 2c61b9cf4..7d4eb4f1d 100644 --- a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/KumaraswamyDistributionTest.cs +++ b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/KumaraswamyDistributionTest.cs @@ -24,27 +24,12 @@ namespace Accord.Tests.Statistics.Distributions.Univariate.Continuous { using System; using System.Globalization; - using Accord.Statistics.Distributions.Univariate.Continuous; + using Accord.Statistics.Distributions.Univariate; using NUnit.Framework; [TestFixture] public class KumaraswamyDistributionTest { - private TestContext testContextInstance; - - public TestContext TestContext - { - get - { - return testContextInstance; - } - set - { - testContextInstance = value; - } - } - - [Test] public void Constructor_KumaraswamyDistribution_PDF_given_0d2_AND_1d2_Parameters_Moments_matches_R_output() { diff --git a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/TrapezoidalDistributionTest.cs b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/TrapezoidalDistributionTest.cs index fef4289bf..6c7fe40b6 100644 --- a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/TrapezoidalDistributionTest.cs +++ b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/TrapezoidalDistributionTest.cs @@ -23,28 +23,13 @@ namespace Accord.Tests.Statistics.Distributions.Univariate.Continuous { using System; - using Accord.Statistics.Distributions.Univariate.Continuous; + using Accord.Statistics.Distributions.Univariate; using NUnit.Framework; using System.Globalization; [TestFixture] public class TrapezoidalDistributionTest { - - private TestContext testContextInstance; - - public TestContext TestContext - { - get - { - return testContextInstance; - } - set - { - testContextInstance = value; - } - } - [Test] public void TrapezoidalDistributionConstructorTest() { diff --git a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/UQuadraticDistributionTest.cs b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/UQuadraticDistributionTest.cs index dba01ffe8..03f3eae0c 100644 --- a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/UQuadraticDistributionTest.cs +++ b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/Continuous/UQuadraticDistributionTest.cs @@ -23,7 +23,7 @@ namespace Accord.Tests.Statistics.Distributions.Univariate.Continuous { using System; - using Accord.Statistics.Distributions.Univariate.Continuous; + using Accord.Statistics.Distributions.Univariate; using NUnit.Framework; using System.Globalization; @@ -31,20 +31,6 @@ namespace Accord.Tests.Statistics.Distributions.Univariate.Continuous public class UQuadraticDistributionTest { - private TestContext testContextInstance; - - public TestContext TestContext - { - get - { - return testContextInstance; - } - set - { - testContextInstance = value; - } - } - [Test] public void Constructor_UQuadratic() { diff --git a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/MixtureDistributionTest.cs b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/MixtureDistributionTest.cs index 246281550..21d4ecb87 100644 --- a/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/MixtureDistributionTest.cs +++ b/Unit Tests/Accord.Tests.Statistics/Distributions/Univariate/MixtureDistributionTest.cs @@ -380,7 +380,7 @@ public void MixtureFitTest() Threshold = 0 }); - var result = mixture.ToString("N2"); + var result = mixture.ToString("N2", System.Globalization.CultureInfo.InvariantCulture); Assert.AreEqual("Mixture(x; 0.50*N(x; μ = -2.00, σ² = 0.25) + 0.50*N(x; μ = 4.00, σ² = 0.25))", result); }