I'm trying to export several BitmapSource images from to png files. Here's my code:
Thread Call:
var exportImagesThread = new Thread(ExportRangeOfImages);
exportImagesThread.Start();
Function:
private void ExportRangeOfImages()
{
for (var currentFrame = exportFromFrame; currentFrame <= exportToFrame; currentFrame++)
{
var currentImage = Application.Current.Dispatcher.Invoke(() => (currentStream.Children[0] as Image).Source);
var pngBitmapEncoder = new PngBitmapEncoder();
var bitmapFrame = BitmapFrame.Create((BitmapSource) currentImage);
Application.Current.Dispatcher.Invoke(() => pngBitmapEncoder.Frames.Add(bitmapFrame));
var a = new FileStream(updatedDir + sequenceFile + "_" + frameNumber + ".png", FileMode.Create);
pngBitmapEncoder.Save(a);
}
}
When doing that I'm receiving Additional information: The calling thread cannot access this object because a different thread owns it. On line: var bitmapFrame = BitmapFrame.Create((BitmapSource) currentImage);
What am I missing?
Related
I'm currently working on the code to take pictures with a Canon camera and float them on the image.
The problem right now is that the picture is displayed normally in the image, but the memory is allocated, but the memory is not released and keeps stacking.
The size of the picture is 6000 X 4000 and the picture is saved as JPG. Also, the capacity of the picture is 6MB.
How can I turn off the memory?
Below is a picture of the memory graph.
case 1:
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate {
var firstphoto = new BitmapImage();
var stream1 = File.OpenRead(MainWindow.PhotoPath + #"\IMG_" + photonumber + #".JPG");
firstphoto.BeginInit();
firstphoto.CacheOption = BitmapCacheOption.OnLoad;
firstphoto.StreamSource = stream1;
firstphoto.EndInit();
firstphoto.Freeze();
FirstPic.Source = firstphoto;
stream1.Close();
stream1.Dispose();
}));
break;
case 2:
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate {
var secondphoto = new BitmapImage();
var stream2 = File.OpenRead(MainWindow.PhotoPath + #"\IMG_" + photonumber + #".JPG");
secondphoto.BeginInit();
secondphoto.CacheOption = BitmapCacheOption.OnLoad;
secondphoto.StreamSource = stream2;
secondphoto.EndInit();
secondphoto.Freeze();
SecondPic.Source = secondphoto;
stream2.Close();
stream2.Dispose();
}));
break;
case 3:
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate {
var thirdphoto = new BitmapImage();
var stream3 = File.OpenRead(MainWindow.PhotoPath + #"\IMG_" + photonumber + #".JPG");
thirdphoto.BeginInit();
thirdphoto.CacheOption = BitmapCacheOption.OnLoad;
thirdphoto.StreamSource = stream3;
thirdphoto.EndInit();
thirdphoto.Freeze();
ThirdPic.Source = thirdphoto;
stream3.Close();
stream3.Dispose();
}));
break;
case 0:
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate {
var fourthphoto = new BitmapImage();
var stream4 = File.OpenRead(MainWindow.PhotoPath + #"\IMG_" + photonumber + #".JPG");
fourthphoto.BeginInit();
fourthphoto.CacheOption = BitmapCacheOption.OnLoad;
fourthphoto.StreamSource = stream4;
fourthphoto.EndInit();
fourthphoto.Freeze();
FourthPic.Source = fourthphoto;
stream4.Close();
stream4.Dispose();
}));
I am new to learning about scanners and tried a bunch of packages but ended up on NTwain a NuGet library. I am struggling on how to start my scanner and save the images using the api. How can I understand it? Also here's what I have so far.
Edit
I found out how to enable the scan and save it but for some reason I can't get both sides of the paper? I don't know if my encoder is wrong trying to save it as a multi-tiff or its something you have to set using NTwain.
Edit 2
I figured it out. I didn't know scanner see double sided as "Duplex" -> myDS.Capabilities.CapDuplexEnabled.SetValue(BoolType.True);
public static void GetScanner()
{
// Create appId
var appId = TWIdentity.CreateFromAssembly(DataGroups.Image, Assembly.GetExecutingAssembly());
// Attach
var session = new TwainSession(appId);
List<Image> scannedImages = new List<Image>();
session.TransferReady += (s, e) =>
{
Debug.Print("TransferReady is a go.");
};
session.DataTransferred += (s, e) =>
{
if (e.NativeData != IntPtr.Zero)
{
// Handle image data
if (e.NativeData != IntPtr.Zero)
{
var stream = e.GetNativeImageStream();
if (stream != null)
{
//Save Image to list
scannedImages.Add(Image.FromStream(stream));
}
}
}
};
// Open it
session.Open();
// Open the first source found
DataSource myDS = session.FirstOrDefault();
myDS.Open();
myDS.Capabilities.CapDuplexEnabled.SetValue(BoolType.True);
// Start Scan
myDS.Enable(SourceEnableMode.NoUI, false, IntPtr.Zero);
//Close Session
myDS.Close();
// Save Images to specific folder as tiffs
int n = 0;
foreach(Image image in scannedImages)
{
//Get the codec for tiff files
ImageCodecInfo info = null;
foreach (ImageCodecInfo ice in ImageCodecInfo.GetImageEncoders())
if (ice.MimeType == "image/tiff")
info = ice;
//Save as Multi-Page Tiff
System.Drawing.Imaging.Encoder enc = System.Drawing.Imaging.Encoder.SaveFlag;
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
//Construct save path
var saveFolderPath = #"C:\Projects\SavingMethods\SavingMethods\ScannedImages\";
string fileName = "Testfile" + n + ".tiff";
var completedFilePath = Path.Combine(saveFolderPath, fileName);
//Save Image
image.Save(completedFilePath, info, ep);
n++;
}
}
I ended up figuring it out by myself but would like to thank ckuri for the comment as his link did help me immensely.
public static void GetScanner()
{
// Create appId
var appId = TWIdentity.CreateFromAssembly(DataGroups.Image,
Assembly.GetExecutingAssembly());
// Attach
var session = new TwainSession(appId);
List<Image> scannedImages = new List<Image>();
session.TransferReady += (s, e) =>
{
Debug.Print("TransferReady is a go.");
};
session.DataTransferred += (s, e) =>
{
if (e.NativeData != IntPtr.Zero)
{
// Handle image data
if (e.NativeData != IntPtr.Zero)
{
var stream = e.GetNativeImageStream();
if (stream != null)
{
//Save Image to list
scannedImages.Add(Image.FromStream(stream));
}
}
}
};
// Open it
session.Open();
// Open the first source found
DataSource myDS = session.FirstOrDefault();
myDS.Open();
myDS.Capabilities.CapDuplexEnabled.SetValue(BoolType.True);
// Start Scan
myDS.Enable(SourceEnableMode.NoUI, false, IntPtr.Zero);
//Close Session
myDS.Close();
// Save Images to specific folder as tiffs
int n = 0;
foreach(Image image in scannedImages)
{
//Get the codec for tiff files
ImageCodecInfo info = null;
foreach (ImageCodecInfo ice in ImageCodecInfo.GetImageEncoders())
if (ice.MimeType == "image/tiff")
info = ice;
//Save as Multi-Page Tiff
System.Drawing.Imaging.Encoder enc = System.Drawing.Imaging.Encoder.SaveFlag;
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
//Construct save path
var saveFolderPath = #"C:\Projects\SavingMethods\SavingMethods\ScannedImages\";
string fileName = "Testfile" + n + ".tiff";
var completedFilePath = Path.Combine(saveFolderPath, fileName);
//Save Image
image.Save(completedFilePath, info, ep);
n++;
}
}
I'm trying to loop through all the images I load, I'm able to process up to 40 images, then the I get the Out of memory error although I'm disposing the variable tempImage. The code breaks at the "Bitmap tempImage = new Bitmap(fileName);" line, plz help!
Is there a way to handle large number of input files? Like batch process and splitting the process into chunks? Because the operation would last more than a minute to finish, the program would crash by then.
foreach (string fileName in openFileDialog.FileNames)
{
circleDetection examDetect = new circleDetection();
Bitmap tempImage = new Bitmap(fileName);
directory.Text = fileName;
PictureBox picBox = new PictureBox();
picBox.Width = 200;
picBox.Height = 200;
picBox.Location = new System.Drawing.Point(picBoard.Controls.Count * (picBox.Width + 5) + 5, 5);
picBox.SizeMode = PictureBoxSizeMode.Zoom;
picBox.BorderStyle = BorderStyle.FixedSingle;
examDetect.ProcessImage(tempImage);
picBox.Image = examDetect.getImage();
Console.WriteLine(i++);
student.Add(compare(examDetect));
picBoard.Controls.Add(picBox);
tempImage.Dispose();
}
I prefer to use using() instead of .Dispose(). It's also disposing an object after it's cycle ended. So maybe you should try smth like next?
foreach (string fileName in openFileDialog.FileNames)
{
circleDetection examDetect = new circleDetection();
using (Bitmap tempImage = new Bitmap(fileName))
{
Exam.Add(tempImage);
directory.Text = fileName;
PictureBox picBox = new PictureBox();
picBox.Width = 200;
picBox.Height = 200;
picBox.Location = new System.Drawing.Point(picBoard.Controls.Count * (picBox.Width + 5) + 5, 5);
picBox.SizeMode = PictureBoxSizeMode.Zoom;
picBox.BorderStyle = BorderStyle.FixedSingle;
examDetect.ProcessImage(tempImage);
picBox.Image = examDetect.getImage();
Console.WriteLine(i++);
student.Add(compare(examDetect));
picBoard.Controls.Add(picBox);
}
}
See MSDN for more information about using statement.
UPDATE:
But, why don't you use stream for loading your files? It's recommended to use stream for such cases.
I am first creating Bitmap image file and saving it to some temp location. Later using that file to read in BitmapImage object to compare it with other file. Once comparison is done, I want to delete file but then it throws exception that file is being used by another process. How can I delete this file?
Here is my code:
private void btnLogin_Click(object sender, EventArgs e)
{
string strPath = AppDomain.CurrentDomain.BaseDirectory;
GC.Collect();
if (txtLoginImage.Text != "")
{
string strFileName = txtLoginImage.Text.Substring(txtLoginImage.Text.LastIndexOf('\\') + 1);
Bitmap MainImg = new System.Drawing.Bitmap(txtLoginImage.Text);
Bitmap NewImage = ConvertToGrayScale(MainImg);
NewImage.Save(AppDomain.CurrentDomain.BaseDirectory + "\\Images\\Temp\\" + strFileName, System.Drawing.Imaging.ImageFormat.Bmp);
NewImage.Dispose();
Uri SourceUri = new Uri(AppDomain.CurrentDomain.BaseDirectory + "\\Images\\Temp\\" + strFileName);
BitmapImage source = new BitmapImage();
source.UriSource = SourceUri;
IrisSystem.Class.BLL.User_BLL ubll = new IrisSystem.Class.BLL.User_BLL();
DataSet dsUserData= ubll.getlist();
bool isMatchFound = false;
if (dsUserData != null && dsUserData.Tables.Count > 0)
{
foreach (DataRow item in dsUserData.Tables[0].Rows)
{
Uri TargetUri = new Uri(AppDomain.CurrentDomain.BaseDirectory + "\\Images\\Grayscale\\" + item["GrayScaleImgName"]);
BitmapImage Target = new BitmapImage(TargetUri);
if (source.IsEqual(Target))
{
IrisSystem.frmHome frm= new IrisSystem.frmHome();
frm.strFullName = item["FullName"].ToString();
frm.ShowDialog();
Form.ActiveForm.Close();
isMatchFound = true;
break;
}
Target = null;
}
if (!isMatchFound)
MessageBox.Show("Invalid Credential..","Invalid Operation");
}
File.Delete(AppDomain.CurrentDomain.BaseDirectory + "\\Images\\Temp\\" + strFileName);
}
else
MessageBox.Show("Please select image", "Login Error");
}
You need to make sure that your Bitmap objects are disposed properly.
You are not disposing the MainImg object. you need to use using {} block to make sure that objects are disposed properly.
Replace This:
Bitmap MainImg = new System.Drawing.Bitmap(txtLoginImage.Text);
Bitmap NewImage = ConvertToGrayScale(MainImg);
NewImage.Save(AppDomain.CurrentDomain.BaseDirectory + "\\Images\\Temp\\"
+ strFileName, System.Drawing.Imaging.ImageFormat.Bmp);
NewImage.Dispose();
With This:
using(Bitmap MainImg = new System.Drawing.Bitmap(txtLoginImage.Text))
using(Bitmap NewImage = ConvertToGrayScale(MainImg))
{
NewImage.Save(AppDomain.CurrentDomain.BaseDirectory + "\\Images\\Temp\\" +
strFileName, System.Drawing.Imaging.ImageFormat.Bmp);
}
EDIT:
Replace This:
BitmapImage source = new BitmapImage();
source.UriSource = SourceUri;
With This:
BitmapImage source = new BitmapImage();
source.BeginInit();
source.UriSource = SourceUri;
source.EndInit();
I'm implementing "copy map to clip" as an image.
what I do is I create an image an save it to certain directory and copy it to clipboard.
but before I do that I delete every file that exist in the directory, but now I cant since, the image is being used by clipboard.
here's my code.
public override void OnClick()
{
//base.OnClick();
DeleteOldCopiedJPG();
System.Windows.Forms.Clipboard.Clear();
string fileName = System.Windows.Forms.Application.ExecutablePath.Substring(0, System.Windows.Forms.Application.ExecutablePath.LastIndexOf("\\") + 1) + Guid.NewGuid() + ".jpg";
//System.Windows.Forms.Cursor.Current = Cursors.Wait;
//if (System.IO.File.Exists(fileName))
// System.IO.File.Delete(fileName);
IExport objExport = (IExport)new ExportJPEG();
objExport.ExportFileName = fileName;
#if Debug || Release
ESRI.ArcGIS.Display.tagRECT objExportRECT = default( ESRI.ArcGIS.Display.tagRECT);
#else
tagRECT objExportRECT = default(tagRECT);
#endif
var _with1 = objExportRECT;
_with1.left = mapControl.ActiveView.ExportFrame.left;
_with1.top = mapControl.ActiveView.ExportFrame.top;
_with1.right = mapControl.ActiveView.ExportFrame.right;
_with1.bottom = mapControl.ActiveView.ExportFrame.bottom;
IEnvelope envelope = new EnvelopeClass();
envelope.PutCoords(mapControl.ActiveView.ExportFrame.left, mapControl.ActiveView.ExportFrame.top,
mapControl.ActiveView.ExportFrame.right, mapControl.ActiveView.ExportFrame.bottom);
objExport.PixelBounds = envelope;
System.Int32 intHDC = objExport.StartExporting();
mapControl.ActiveView.Output(intHDC, Convert.ToInt16(objExport.Resolution), objExportRECT, null, null);
objExport.FinishExporting();
objExport.Cleanup();
System.Drawing.Image objImage = System.Drawing.Bitmap.FromFile(fileName);
System.Windows.Forms.Clipboard.SetImage(objImage);
//RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(200, 200, 96, 96, PixelFormats.Pbgra32);
//renderTargetBitmap.Render((Visual)mapControl.ActiveView.ScreenDisplay);
//Clipboard.SetImage(renderTargetBitmap);
}
private void DeleteOldCopiedJPG(string Path)
{
string[] filePaths = Directory.GetFiles(System.Windows.Forms.Application.ExecutablePath.Substring(0, System.Windows.Forms.Application.ExecutablePath.LastIndexOf("\\") + 1));
foreach (string filepath in filePaths)
if (filepath.Substring(filepath.Length - 4) == ".jpg")
try{ File.Delete(filepath); } catch {}
}
You need to dispose the image after inserting it into the clipboard:
using (System.Drawing.Image objImage = System.Drawing.Bitmap.FromFile(fileName))
{
System.Windows.Forms.Clipboard.SetImage(objImage);
}
Otherwise it will remain open until the garbage collector calls the finalizer for objImage.