I'm generating spectogram out of many pixels. Problem is that it takes 0.8 second to render it. I would like to achieve that with threading so It doesn't freeze my GUI.
I'm very clueless. I tried many approaches but I can't get it working.
I need only this method to work and it should be fine.
volatile DrawingVisual dv = new DrawingVisual();
volatile RenderTargetBitmap rtb;
volatile DrawingContext dc;
private async Task<Image> GetSpectogram(List<int[]> Lines)
{
using (dc = dv.RenderOpen())
{
int y = -1;
int x = -1;
foreach (int[] list in Lines)
{
x += 1;
for (int i = 0; i < list.Length; i++)
{
y += 1;
int value = list[i];
dc.DrawRectangle(GetRGBFromValue(value), null, new Rect(x, y, 1, 1));
}
y = -1;
}
dc.Close();
}
await Task.Factory.StartNew(() => {
rtb = new RenderTargetBitmap(TotalLines, TotalPixelsInLine, 0, 0, PixelFormats.Default);
rtb.Render(dv);
Image img = new Image();
img.Source = rtb;
img.Height = rtb.Height * 3;
img.VerticalAlignment = VerticalAlignment.Stretch;
img.Stretch = Stretch.Uniform;
return img;
});
return null;
}
Can you please tell me what is the problem here, I would like to understand it?
It is throwing threading error at rtb.Render(dv);
Thank you very much :)
Related
I am trying to draw giftu in DrawingVisual.
Here are some codes that I have tried,But the performance is very poor.
Used up to 22% of cpu (AMD Ryzen 7 5800H) and 50% of gpu(rtx 3060) and 4GB of RAM when drawing a 1924*934, 434 fps gif.
And it produces a very strange trailing shadow:
Screenshots
public void InitImage(string path)
{
isAnima = false;
uriBitmap = BitmapDecoder.Create(
new Uri(path, UriKind.Relative),
BitmapCreateOptions.None,
BitmapCacheOption.Default);
if (uriBitmap.Frames.Count > 1)
{
isAnima = true;
//Get the information of each frame of the gif here
frameInfos = new List<FrameInfo>();
for (int i = 0; i < uriBitmap.Frames.Count; i++)
{
frameInfos.Add(GetFrameInfo(uriBitmap.Frames[i]));
}
frameIndex = 0;
try
{
if (animationThread == null)
{
animationThread = new Thread(ChangeGifFrame);
animationThread.Start();
}
}
catch (Exception)
{
Debug.WriteLine("线程关闭");
}
}
}
private void DrawImage(Point location, Size size)
{
var drawing = this.dvc.drawingVisual.RenderOpen();
drawing.PushTransform(rotate);
if (!isAnima)
{
drawing.DrawImage(baseSource, new Rect(location, size));
}
else
{
//Since the FrameDisposalMethod is Combine, I need to draw all the frames before this frame
for (int i = 0; i < frameIndex; i++)
{
var frame = uriBitmap.Frames[i];
var info = frameInfos[i];
drawing.DrawImage(frame, new Rect(new Point(location.X + info.Left * zoom, location.Y + info.Top * zoom), new Size(info.Width * zoom, info.Height * zoom)));
}
}
drawing.Close();
}
public void ChangeGifFrame()
{
while (true)
{
if (isAnima)
{
this.Dispatcher.Invoke(new Action(() =>
{
frameIndex++;
if (frameIndex >= uriBitmap.Frames.Count)
{
frameIndex = 0;
}
DrawImage(drawPoint, drawSize);
}));
}
Thread.Sleep(30);
}
}
I also tried mixing BitmapSource using the following code, but the performance is worse than drawing directly
public BitmapSource MixBitmapSource(BitmapSource bs1, BitmapSource bs2)
{
DrawingVisual dv = new DrawingVisual();
RenderTargetBitmap render = new RenderTargetBitmap(bs1.PixelWidth, bs2.PixelHeight, bs1.DpiX, bs2.DpiY, PixelFormats.Default);
DrawingContext dc = dv.RenderOpen();
dc.DrawImage(bs1, new Rect(0, 0, bs1.PixelWidth, bs1.PixelHeight));
dc.DrawImage(bs2, new Rect(0, 0, bs1.PixelWidth, bs1.PixelHeight));
dc.Close();
render.Render(dv);
return render;
}
Is there a better way to draw gifs in DrawingVisual?
Thanks for all your help!
I have C# project already done but im having issue with printing it's charts when comes out with more pages of data points, i got the scroll bar work when start getting more pages so the user can review all data on all pages but i could not find how to make the print preview shows them or print them,
when click on print, it shows only the first page on the print preview and same thing when print it out.
her is the print code:
PrintPreviewDialog ppd = new PrintPreviewDialog();
ppd.Document = this.chart1.Printing.PrintDocument;
((Form)ppd).WindowState = FormWindowState.Maximized;
chart1.Printing.PrintDocument.DefaultPageSettings.Landscape = true;
chart1.Printing.PrintDocument.DefaultPageSettings.Margins.Left = 0;
chart1.Printing.PrintDocument.DefaultPageSettings.Margins.Right = 0;
chart1.Printing.PrintDocument.DefaultPageSettings.Margins.Top = 0;
chart1.Printing.PrintDocument.DefaultPageSettings.Margins.Bottom = 0;
ppd.ShowDialog();
and here is the chart load code:
public void loadChart(string sqlvalue)//load chart method
{
chart1.ChartAreas[0].AxisY.Maximum = 55;
chart1.ChartAreas[0].AxisY.Minimum = 35;
chart1.ChartAreas[0].AxisY.Interval = 5;//control how many lines/Interval
chart1.ChartAreas[0].AxisY.ScrollBar.Enabled = true;
chart1.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
chart1.ChartAreas[0].AxisX.Minimum = 0;
// chart1.ChartAreas[0].AxisX.Maximum = 10;
chart1.ChartAreas[0].AxisX.Interval = 1;
//X AXES label angle
chart1.ChartAreas[0].AxisX.LabelStyle.Angle = 60;
da = new SqlDataAdapter(sqlvalue, cn.connect());
da.Fill(dt);
this.chart1.DataSource = dt;
this.chart1.Series["left"].XValueMember = dt.Columns[3].ToString();//date data
this.chart1.Series["left"].YValueMembers = dt.Columns[1].ToString();//spindle 1 data
this.chart1.Series["Right"].YValueMembers = dt.Columns[2].ToString();//spindle 2 data
//label the series lines, Backcolor and forcolor
chart1.Series[0].LabelBackColor = Color.Red;
chart1.Series[0].LabelForeColor = Color.White;
//datapoint marker's color, bordercolor,style and size
chart1.Series[0].MarkerColor = Color.White;
chart1.Series[0].MarkerBorderColor = Color.Black;
chart1.Series[0].MarkerStyle = MarkerStyle.Circle;
chart1.Series[0].MarkerSize = 8;
//datapoint marker's color, style and size
chart1.Series[1].MarkerColor = Color.White;
chart1.Series[1].MarkerBorderColor = Color.Black;
chart1.Series[1].MarkerStyle = MarkerStyle.Circle;
chart1.Series[1].MarkerSize = 8;
chart1.Series[1].LabelBackColor = Color.Blue;
chart1.Series[1].LabelForeColor = Color.White;
//Chart background lines color
chart1.ChartAreas[0].AxisX.MajorGrid.LineColor = Color.Silver;
chart1.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.Silver;
this.chart1.DataBind();
cn.disconnect();
// enable autoscroll
chart1.ChartAreas[0].CursorX.AutoScroll = true;//------------
chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(0, 15);
chart1.ChartAreas[0].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
// set scrollbar small change to the target size
chart1.ChartAreas[0].AxisX.ScaleView.SmallScrollSize = 15;
Legend left = new Legend();
Legend LC = CustomCloneLegend(chart1, left);
chart1.Legends.Add(LC);
chart1.Padding = Padding.Empty;
ChartArea CA = chart1.ChartAreas[0];
CA.Position = new ElementPosition(4,6, 100, 90);
}
Thank you for your time and help, here is the chart code update:
private static Image MergeImages(List<Image> imageList)
{
var finalSize = new Size();
foreach (var image in imageList)
{
if (image.Width > finalSize.Width)
{
finalSize.Width = image.Width;
}
finalSize.Height += image.Height;
}
var outputImage = new Bitmap(finalSize.Width, finalSize.Height);
using (var gfx = Graphics.FromImage(outputImage))
{
var y = 0;
foreach (var image in imageList)
{
gfx.DrawImage(image, 0, y);
y += image.Height;
}
}
return outputImage;
}
The second method:
List<Image> ChartsToImages(List<Chart> charts)
{
var imageList = new List<Image>();
foreach (var c in charts)
{
using (var ms = new MemoryStream())
{
c.SaveImage(ms, ChartImageFormat.Png);
var bmp = System.Drawing.Bitmap.FromStream(ms);
imageList.Add(bmp);
}
}
return imageList;
}
and this code
var chartList = new List<Chart> { chart1 };
var imageList = ChartsToImages(chartList);
var finalImage = MergeImages(imageList);
finalImage.Save("D:\\Junk.png", ImageFormat.Png);
Im not sure is that what you mean by your first comment, but i found this code here under Converting chart to image questions. this code convert and saves the chart in the same amount of pages but i need to show them in the printpreviewcontrol and print them.
Below code refers to the as per page count starting point and ending point based printing. And Grid view value chars are row loop based counting the page.
private int numberOfItemsPerPage = 0;
private int numberOfItemsPrintedSoFar = 0;
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
int height = 480; //page height stating point
for (int l = numberOfItemsPrintedSoFar; l < dataGridView2.Rows.Count; l++)
{
numberOfItemsPerPage = numberOfItemsPerPage + 1;
if (numberOfItemsPerPage <= 25) // 25 is Page Line Item
{
numberOfItemsPrintedSoFar++;
if (numberOfItemsPrintedSoFar <= dataGridView2.Rows.Count)
{
height += dataGridView2.Rows[0].Height;
e.Graphics.DrawString(dataGridView2.Rows[l].Cells[0].FormattedValue.ToString(), dataGridView2.Font = new Font("Book Antiqua", 8), Brushes.Black, new RectangleF(5, height, dataGridView2.Columns[0].Width, dataGridView2.Rows[0].Height));
e.Graphics.DrawString(dataGridView2.Rows[l].Cells[1].FormattedValue.ToString(), dataGridView2.Font = new Font("Book Antiqua", 8), Brushes.Black, new RectangleF(170, height, dataGridView2.Columns[0].Width, dataGridView2.Rows[0].Height));
e.Graphics.DrawString(dataGridView2.Rows[l].Cells[2].FormattedValue.ToString(), dataGridView2.Font = new Font("Book Antiqua", 8), Brushes.Black, new RectangleF(290, height, dataGridView2.Columns[0].Width, dataGridView2.Rows[0].Height));
e.Graphics.DrawString(dataGridView2.Rows[l].Cells[3].FormattedValue.ToString(), dataGridView2.Font = new Font("Book Antiqua", 8), Brushes.Black, new RectangleF(345, height, dataGridView2.Columns[0].Width, dataGridView2.Rows[0].Height));
}
else
{
e.HasMorePages = false;
}
}
else
{
numberOfItemsPerPage = 0;
e.HasMorePages = true;
return;
}
}
numberOfItemsPerPage = 0;
numberOfItemsPrintedSoFar = 0;
}
I have a cropped version of an image that should appear on my screen.
Image 6Island = Image.FromFile("C:\\Users\\6Island.png");
Now the next goal is to Take an image of the screen.
Bitmap CaptureScreen()
{
var image = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
var gfx = Graphics.FromImage(image);
gfx.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
return image;
}
Image 6Island = Image.FromFile("C:\\Users\\6Island.png");
Image currentView = CaptureScreen();
I then want to see if I can I can find the image 6Island inside the new image. And the colors may vary a tiny bit. Is there anyway to do that?
This is just sample quick and dirty and very slow, but it works. This code make a "crop" of your big bitmap and compare it with your small bitmap. If equal then percentage must be 100, if unequal then percentage lower than that. I would say, if bigger than 98%, then you found it.
private static void CompareBigAndSmallBitmaps(string fileName1, string fileName2)
{
var bmpBig = (Bitmap) Image.FromFile(fileName1);
var bmpSmall = (Bitmap) Image.FromFile(fileName2);
for (var offX = 0; offX < bmpBig.Width - bmpSmall.Width; offX++)
{
for (var offY = 0; offY < bmpBig.Height - bmpSmall.Height; offY++)
{
var percentage = CompareSmallBitmaps(bmpBig, bmpSmall, offX, offY);
if (percentage > 98.0) // define percentage of equality
{
// Aha... found something here....and exit here if you want
}
}
}
}
private static double CompareSmallBitmaps(Bitmap bmpBig, Bitmap bmpSmall, int offX, int offY)
{
var equals = 0;
for (var x = 0; x < bmpSmall.Width; x++)
{
for (var y = 0; y < bmpSmall.Height; y++)
{
var color1 = bmpBig.GetPixel(x + offX, y + offY).ToArgb();
var color2 = bmpSmall.GetPixel(x, y).ToArgb();
if (color1 == color2)
{
equals++;
}
}
}
return (Convert.ToDouble(equals)/Convert.ToDouble(bmpSmall.Width*bmpSmall.Height))*100.0;
}
Hi I need to convert an unmanaged image to a managed bitmap image which I need to display on a picturebox but I it seems to throw an exception saying "object reference not set to an instance of an object". Does anyone have an idea about it? I have commented the line which throws the exception.
for (int i = 0; i < characters.Length; i++)
{
area = characters[i].Area;
UnmanagedImage numer = characters[i].Image;
System.Drawing.Image plateImage = numer.ToManagedImage();//Exception
numberplate = new Bitmap(new Bitmap(plateImage));
pictureBox2.Image = numberplate;
pictureBox2.Refresh();
}
I am using Aforge.net framework with C#
UPDATE
for (int i = 0; i < characters.Length; i++)
{
area = characters[i].Area;
Bitmap numer = characters[i].Image.ToManagedImage();
//System.Drawing.Image plateImage = numer.ToManagedImage();
//numberplate = new Bitmap(new Bitmap(plateImage));
pictureBox2.Image = numberplate;
pictureBox2.Refresh();
}
I found this code on the Aforge.Net forums and it seemed to work.
BlobCounterBase bc = new BlobCounter();
bc.FilterBlobs = true;
bc.MinHeight = 5;
bc.MinWidth = 5;
bc.ProcessImage(numberplate);
Blob[] blobs = bc.GetObjectsInformation();
MessageBox.Show(bc.ObjectsCount.ToString());
for (int i = 0, n = blobs.Length; i < n; i++)
{
if (blobs.Length > 0)
{
bc.ExtractBlobsImage(numberplate, blobs[i], true);
Bitmap copy = blobs[i].Image.ToManagedImage();
pictureBox2.Image = numberplate;
pictureBox2.Refresh();
}
}
Bitmap managedImage = numer.ToManagedImage( );
I'm looking at doing a project in C# that looks at an image file not sure of extension yet, and notes the RGB value and if its too dark moves it to another folder for me to look at later
So here it is in block form
Load multiple images from directory > Check RGB value of every file > if too dark > move to different folder. if not ignore (leave in original folder)
I know the basics like get files from dir but checking RGB value of whole picture and then moving it or ignoring it I'm stumped.
I have this code:
private void button1_Click(object sender, EventArgs e)
{
CompareImages(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "checked"), 127.0, new string[] {"*.jpg", "*.png"});
}
private void CompareImages(string sourceFolder, string disposedImgFolder, double threshold, string[] extensions)
{
if (Directory.Exists(sourceFolder))
{
DirectoryInfo dir = new DirectoryInfo(sourceFolder);
List<FileInfo> pictures = new List<FileInfo>();
foreach (string ext in extensions)
{
FileInfo[] fi = dir.GetFiles(ext);
pictures.AddRange(fi);
}
Directory.CreateDirectory(disposedImgFolder);
int j = 0;
if (pictures.Count > 0)
{
for (int i = 0; i < pictures.Count; i++)
{
Image img = null;
Bitmap bmp = null;
try
{
img = Image.FromFile(pictures[i].FullName);
bmp = new Bitmap(img);
img.Dispose();
double avg = GetAveragePixelValue(bmp);
bmp.Dispose();
if (avg < threshold)
{
string dest = Path.Combine(disposedImgFolder, pictures[i].Name);
if (File.Exists(dest) == false)
{
pictures[i].MoveTo(dest);
j++;
}
else
{
}
}
else
{
}
}
catch
{
if (img != null)
img.Dispose();
if (bmp != null)
bmp.Dispose();
}
}
MessageBox.Show("Done, " + j.ToString() + " files moved.");
}
}
}
private unsafe double GetAveragePixelValue(Bitmap bmp)
{
BitmapData bmData = null;
try
{
bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int stride = bmData.Stride;
IntPtr scan0 = bmData.Scan0;
int w = bmData.Width;
int h = bmData.Height;
double sum = 0;
long pixels = bmp.Width * bmp.Height;
byte* p = (byte*)scan0.ToPointer();
for (int y = 0; y < h; y++)
{
p = (byte*)scan0.ToPointer();
p += y * stride;
for (int x = 0; x < w; x++)
{
double i = ((double)p[0] + p[1] + p[2]) / 3.0;
sum += i;
p += 4;
}
}
bmp.UnlockBits(bmData);
double result = sum / (double)pixels;
return result;
}
catch
{
try
{
bmp.UnlockBits(bmData);
}
catch
{
}
}
return -1;
}
How do I define the threashold?
If you want to read every pixel of an image, it must be converted to a bitmap. Then you can use the GetPixel method. But, this process is very slow and it takes a lot of CPU. If you do so, you really should run some test before.
using (var m = new MemoryStream())
{
using (var img = Image.FromFile(args[0]))
{
img.Save(m, ImageFormat.Bmp);
}
m.Position = 0;
using (var bitmap = (Bitmap)Bitmap.FromStream(m))
{
for (var x = 0; x < bitmap.Width; x++)
{
for (var y = 0; y < bitmap.Height; y++)
{
var color = bitmap.GetPixel(x, y);
// TODO: Do what ever you want
}
}
}
}
I think you need to read up a bit on RGB. Every pixel will have an Red, Green and Blue value associated with it and i guess you are looking for a way to get some meassure of how bright the "average" pixel is? If so you need to loop over all pixels. While doing so calculate brightness of each pixel. "Brightness" of each pixels can be calculated in several ways, you could simply do (R + G + B)/3, or you could try to mimic that the human eye isn't equaly sensitive to R, G and B.
Then you will have to decide how to use your "brightness" of pixel. Mean, Median, something else? It depends on what you want to do..
Update: After reading more of your comments I'm still not really sure what you mean by "bright" or "dark". It also seems that you have your terminology a bit confused, you keep talking about a RGB value for an entire image, but RGB values in an image refer to individual pixel values.
I believe that this page could help you doing what you want:
http://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx
Also, some complimentary reading to understand RGB:
http://en.wikipedia.org/wiki/Luma_(video)
http://en.wikipedia.org/wiki/RGB_color_space