I'm having a little hard time after navigating around the site to find out solution. The problem I have is that I'm trying to capture an image using mediacapture taken from this url (download here).
I've found a couple threads in SO that deal with resolution already, but what they're doing is using default resolution as following
3024*4992.......4:3
1936*2592...162:121
1536*2048.......4:3
480*640..........4:3
3024*5376.....16:9
1728*3072.....16:9
1456*2592...162:91
(suggeted by this)
However what I want is to capture an image with 800x600 resolution, is this really possible somehow?
You can use 800x600 only if your camera supports it. My camera for example doesn't.
Find available resolutions:
uint[] x_res; // available horizontal resolutions
uint[] y_res; // available vertical resolutions
uint resolutionwidth; //used horizontal resolution
uint resolutionheight; //used vertical resolution
private void get_res_button_click(object sender, RoutedEventArgs e)
{
resolution_listbox.Items.Clear();
IEnumerable<VideoEncodingProperties> available_resolutions = captureManager.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.Photo).Select(x => x as VideoEncodingProperties);
int total_res = available_resolutions.Count();
x_res = new uint[total_res];
y_res = new uint[total_res]
int i = 0;
foreach (VideoEncodingProperties resolution in available_resolutions)
{
x_res[i] = resolution.Width;
y_res[i] = resolution.Height;
resolution_listbox.Items.Add(x_res[i].ToString() + " x " + y_res[i].ToString());
i++;
}
}
Select the one you want:
private async void resolution_listbox_selectionchanged(object sender, SelectionChangedEventArgs e)
{
if (resolution_listbox.SelectedItem != null)
{
int j = resolution_listbox.SelectedIndex;
resolutionwidth = x_res[j];
resolutionheight = y_res[j];
}
// And apply it:
IReadOnlyList<IMediaEncodingProperties> resolutions = captureManager.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.Photo);
for (int k = 0; k < resolutions.Count; k++)
{
if (resolutions[k] is VideoEncodingProperties)
{
VideoEncodingProperties vidprops = (VideoEncodingProperties)resolutions[k];
// check which VideoEncodingProperties contains the correct resolution
if (vidprops.Width == resolutionwidth && vidprops.Height == resolutionheight)
{
await captureManager.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.Photo, resolutions[k]);
}
}
}
}
Note:in the 1st method I used IEnumerable< VideoEncodingProperties > . This because I only want the numbers.
In the 2nd method I used IReadOnlyList< IMediaEncodingProperties >. This is because only the VideoEncodingProperties which contains the wanted resolution, needs to be applied. Not every IMediaEncodingProperties contains resolution information.
Related
I have a function which is pretty much a variable-renamed version of the code snippet from https://codingvision.net/c-get-frames-from-a-gif
I am using this to split up the images in a GIF so I am able to iterate over them and play them in a PictureBox to solve the issue highlighted here: Gif Animated Files in C# have Lower framerates than they should
My splitting function:
private Image[] __loading_gif_list;
private int __frame_count;
private int __frame_amount;
private Image[] get_frames(Image _image)
{
int frame_count = _image.GetFrameCount(FrameDimension.Time);
Image[] frames = new Image[frame_count];
for (int i = 0; i < frame_count; ++i)
{
_image.SelectActiveFrame(FrameDimension.Time, i);
frames[i] = (Image)_image.Clone();
}
return frames;
}
My Form Load function:
private void form_main_Load(object sender, EventArgs e)
{
__loading_gif_list = get_frames(Properties.Resources.loading);
__frame_amount = Properties.Resources.loading.GetFrameCount(FrameDimension.Time);
loading_picbox.Enabled = false;
}
and my iteration function (a timer can also be used, however I was simply experimenting with this implementation)
private async void loader_updater()
{
while (true)
{
if (__frame_count >= __frame_amount) { __frame_count = 1; }
else { ++__frame_count; }
loading_picbox.Image = __loading_gif_list[__frame_count - 1];
await Task.Delay(10);
}
}
The split function for a 113 frame GIF takes insanely long to complete and the entire program uses an excess of 600mb!! of memory for a 700kb GIF. I am unsure how to optimise the memory usage and speed.
Thank you very much!
I have a WinForms application where a number of lines are drawn in a TeeChart component. It is requested that it shall be possible to delete a line by right-clicking it.
Everything works fine, the clickseries event is captured and so on, but the user finds it difficult to hit the line on right click. The question is, is it possible to increase the region where the Line/FastLine object is sensible for clicking? That is, make the line wider without drawing the line any wider on the screen.
Tnx in advance
Yes, this is possible. The key to achieve that is PointInLineTolerance method. To achieve what you request you can combine it with NearestPoint's tool GetNearestPoint method as shown in this example:
public Form1()
{
InitializeComponent();
InitializeChart();
}
private void InitializeChart()
{
tChart1.Aspect.View3D = false;
tChart1.Series.Add(new Steema.TeeChart.Styles.Line()).FillSampleValues();
tChart1.MouseMove += TChart1_MouseMove;
}
private void TChart1_MouseMove(object sender, MouseEventArgs e)
{
var nearestPoint = new Steema.TeeChart.Tools.NearestPoint(tChart1[0]);
nearestPoint.Active = false;
var p = new Point(e.X, e.Y);
var index = nearestPoint.GetNearestPoint(p);
if (index != -1)
{
const int tolerance = 10;
var px = tChart1[0].CalcXPos(index);
var py = tChart1[0].CalcYPos(index);
var index2 = (index == tChart1[0].Count - 1) ? index - 1 : index + 1;
var qx = tChart1[0].CalcXPos(index2);
var qy = tChart1[0].CalcYPos(index2);
if (Steema.TeeChart.Drawing.Graphics3D.PointInLineTolerance(p, px, py, qx, qy, tolerance))
{
tChart1.Header.Text = "point " + index.ToString() + " clicked";
}
else
{
tChart1.Header.Text = "No point";
}
}
An alternative could be using an invisible fake series with same data as the original series.
I am trying to use Dynamic Data Display for my senior design project, but I'm having difficulty.
I can create the graph just fine, but I can't the line to update at all unless I am zooming in and out.
In most examples, all they had to do was create the graph, then when they manipulate the data, the graph updates itself.
So here are the main functions I am using with regards to the charts:
private List<WindDAQ.WindDataPoint> Chart1Data;
private EnumerableDataSource<WindDAQ.WindDataPoint> Chart1DataSource;
private void InitializeCharts()
{
Chart1Data = new List<WindDAQ.WindDataPoint>();
Chart1DataSource = new EnumerableDataSource<WindDAQ.WindDataPoint>(Chart1Data);
Chart1DataSource.SetXMapping(x => Chart1XAxis.ConvertToDouble(x.Time));
Chart1DataSource.SetYMapping(y => Chart1XAxis.ConvertToDouble(y.Lift));
Chart1.AddLineGraph(Chart1DataSource, Colors.Blue, 2, "Lift");
Chart1.Viewport.AutoFitToView = true;
}
private void UpdateChart()
{
for (int i = 0, count = itsDAQ.getStreamCount(); i < count; i++)
{
Chart1Data.Add(itsDAQ.getValue());
if(Chart1Data.Count >= 300)
{ Chart1Data.RemoveAt(0); }
}
}
InitializeCharts() is called once when creating the window.
UpdateChart() is called on a timer event.
WindDAQ.WindDataPoint contains Lift, Drag, Velocity, and Time data. Lift and Time are shown selected.
you should use the AppendAsync method of your observableCollection.
You'r updating only the list used to create the observable one, not the source of your graph.
private void UpdateChart()
{
for (int i = 0, count = itsDAQ.getStreamCount(); i < count; i++)
{
Chart1DataSource.AppendAsync(itsDAQ.getValue());
if (Chart1DataSource.Collection.Count >= 300) // this part should work in a thread-safe
{
Chart1DataSource.Collection.RemoveAt(0); // context and with some precaution
}
}
}
I have been working with a FLIR Thermovision camera the last couple days and have pulled together a very simple application that has a few aspects found in many different places (most of which here on stackoverflow).
Topics
hosting an ActiveX component in wpf application
Float[,] array to BitmapImage
Displaying a binded bitmap in a wpf image control by using MemoryStream and BitmapImage
1. Active X control
The Flir Thermovision SDK 2.6 comes with an ActiveX component dll. AxCAMCTRLLib.dll. In a WinForms application you can simply add the tool to the tool box and click and drag the component onto the form. This will automatically add the correct references to the project. To use it in a wpf application this won't work. In hind sight this seems pretty easy but it wasn't listed in their documentation.
First I had to navigate to the AxCAMCTRLLib.dll manually and add it to the references. Then add a new window to the project. This will be a hidden window used only to host the activeX component. This also requires WindowsFormsIntegration reference to hose ActiveX components.
using CAMCTRLLib;
using AxCAMCTRLLib;
namespace camView
{
public partial class CameraCtrl : Window
{
public AxCAMCTRLLib.AxLVCam camera;
private System.Windows.Forms.Integration.WindowsFormsHost host;
public CameraCtrl()
{
InitializeComponent();
host = new System.Windows.Forms.Integration.WindowsFormsHost();
camera = new AxCAMCTRLLib.AxLVCam();
host.Child = camera;
this.grid1.Children.Add(host);
}
}
}
Now in the MainWindow I can create, show then promptly hide a new window CameraCtrl and have access to the public ActiveX control.
public MainWindow()
{
InitializeComponent();
camCtrl = new CameraCtrl();
camCtrl.BeginInit();
camCtrl.Show();
camCtrl.Hide();
camCtrl.ShowInTaskbar = false;
camCtrl.ShowActivated = false;
}
OnClosing method in MainWindow has to be modified to close the hidden window as well.
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
camCtrl.Close();
base.OnClosing(e);
}
With that I can now access all of the control methods contained in the activex object.
2. Float[,] array to BitmapImage
The output images from the camera can be returned in a number of different formats, but for the particular camera I'm using it returns an object that contains a float[,]. Since it's thermal the output pixel values represent temperatures. That means they must be normalized and then converted first to Bitmap then stored in a MemoryStream then added to the source of a BitmapImage. The method I used is the following.
private BitmapImage setDisplayImage(float[,] image)
{
float[,] tempStoreImage = new float[240 , 320];
float max = -10000.0f, min = 10000.0f;
BitmapImage localBitmap;
for (int j = 0; j < 320; j++)
{
for (int i = 0; i < 240; i++)
{
tempStoreImage[i,j] = image[j,i];//have to transpose the image from cam
if (tempStoreImage[i,j] > max)
{
max = tempStoreImage[i,j];
}
if (tempStoreImage[i,j] < min)
{
min = tempStoreImage[i,j];
}
}
}
if(max != min)//can't divide by zero
{
System.Drawing.Bitmap newBitmap = new System.Drawing.Bitmap(320, 240);
for (int i = 0; i < 240; i++)
{
for (int j = 0; j < 320; j++)
{
tempStoreImage[i,j] = (float)Math.Round((double)(tempStoreImage[i,j] - min) * 255 / (max - min));//Normalize and set between 0 - 255
System.Drawing.Color newColor = System.Drawing.Color.FromArgb((int)tempStoreImage[i, j],0, 0, 0);//Gray scale color using alpha channel
newBitmap.SetPixel(j, i, newColor);
}
}
System.Drawing.Image img = (System.Drawing.Image)newBitmap;
MemoryStream stream = new MemoryStream();
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);//add Bitmap to memory stream
stream.Position = 0;
localBitmap = new BitmapImage();
localBitmap.BeginInit();
localBitmap.StreamSource = stream; //
localBitmap.EndInit();
}
else localBitmap = new BitmapImage();//dark image
return localBitmap;
}
3. Displaying the image
I created a simple helper class:
class BindData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
BitmapImage _image;
public BitmapImage Image
{
get { return _image; }
set
{
_image = value;
_image.Freeze();
OnPropertyChanged("Image");
}
}
}
And then created a static helper class object in the MainWindow (probably doesn't need to be static but I plan to use it in other classes.) BindData bind = new BindData() and set the image1.DataContext = bind. Then set the binding and window size to match my array:
<Image Height="240" HorizontalAlignment="Left" Margin="204,21,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="320" Source="{Binding Image}"/>
And finally captured images using a System.Timers.Timer:
private void cameraCap()
{
if (continueDisplay)
{
captureTimer.Stop();
lock (camCtrl)
{
object yo = camCtrl.camera.GetImage(3);
bind.Image = setDisplayImage(yo as float[,]);
}
captureTimer.Start();
}
else
captureTimer.Stop();
}
private void capture_Click(object sender, RoutedEventArgs e)
{
continueDisplay = true;
captureTimer.Start();
}
private void kill_Click(object sender, RoutedEventArgs e)
{
continueDisplay = false;
}
Couple things I ran into with using the timer. First the application timing and the camera timing are not the same so, I stop the timer at the beginning of a capture and start it back up after the end. Luckily the thread waits for the camera to return an image. This takes care of lag for the most part. Second the _image.Freeze() statement is essential. Without it you will get "Must create DependencySource on same Thread as the DependencyObject." error once initiated. The Freeze method makes the image available to other threads.
This does belong on codereview.stackexchange.com
I'm attempting to write a peak volume meter using NAudio. My code is very similar to http://channel9.msdn.com/coding4fun/articles/NET-Voice-Recorder, but both my code and the linked Voice Recorder project suffer from an issue.
When playing a sound of constant frequency and volume, the volume meter initially begins at a reasonable level, but then decays to a very small value. I'm not sure why this is the case, for the peak volume meter in the NAudioDemo does not do this. I attempted to replicate the code from NAudioDemo in my program, but I was unable to find the code file containing the peak volume meter code.
Can somebody guide me to an alternative solution for creating a peak volume meter or help me determine why my solution (and the one provided at the link) both don't work?
public MainWindow()
{
int waveInDevices = WaveIn.DeviceCount;
for (int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
{
WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
Console.WriteLine("Device {0}: {1}, {2} channels",
waveInDevice, deviceInfo.ProductName, deviceInfo.Channels);
WaveIn waveIn = new WaveIn();
waveIn.DeviceNumber = 0; //TODO: Let the user choose which device, this comes from the device numbers above
waveIn.DataAvailable += waveIn_DataAvailable;
int sampleRate = SAMPLE_RATE; // 8 kHz
int channels = 1; // mono
waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
waveIn.StartRecording();
}
}
void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
for (int index = 0; index < e.BytesRecorded; index += 2)
{
short sample = (short)((e.Buffer[index + 1] << 8) |
e.Buffer[index + 0]);
float sample32 = sample / 32768f;
ProcessSample(sample32);
}
}
void ProcessSample(float sample1)
{
samplenumber += 1;
if (sample1 > maxval)
{
maxval = sample1;
}
if (sample1 < minval)
{
minval = sample1;
}
//Run updateView every few loops
if (samplenumber > (double)SAMPLE_RATE / DISPLAY_UPDATE_RATE)
{
samplenumber = 0;
updateView(); //needs to be fast!
}
}
void updateView()
{
Console.WriteLine(maxval);
Console.WriteLine(minval);
progressBar1.Value = (maxval - minval)*50;
maxval = 0;
minval = 0;
}
All that is happening in that article is that it is finding the maximum audio peak over a small interval (e.g. 20ms) and then plotting that on a decibel scale. To find the peak, examine the value of each sample in the interval and select the max value (It's what the SampleAggregator class is doing). To convert to decibels, take the log base 10 of the maximum value, and multiply by 10. So 0dB is the loudest, and anything below say -96dB is effectively silence. (actually, looking back at the article, I don't think I even bothered to convert to a decibel scale, which I probably should have done)
This was my little solution for getting peak from output device. I'm using NAudio version 1.7.0.15
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
comboboxDevices.Items.AddRange(devices.ToArray());
}
private void timer1_Tick(object sender, EventArgs e)
{
if (comboboxDevices.SelectedItem != null)
{
var device = (MMDevice)comboboxDevices.SelectedItem;
progressBar1.Value = (int)(Math.Round(device.AudioMeterInformation.MasterPeakValue * 100));
}
}
}
Trying to get the levels with MasterPeakValue appears to be more complicated than just calling the method, which defeats its simplicity.
I accidentally realized that you have to open the device for recording, even if you don't use with the incoming data. Since you are starting a WaveIn, MasterPeakValue should return a non-0 value.
A simple alternative, just for testing, is to open the properties of system's recording devices (right-click on system volume icon and choose "Recording devices").
(Tested on 2 different computers.)