从图片框中解锁图像

本文关键字:解锁 图像 | 更新日期: 2023-09-27 18:32:58

我目前正在开发一个应用程序来帮助扫描和显示我工作中的图像。

我的应用程序是用多个表单构建的,这里最重要的表单是我mainForm显示有关当前扫描的统计信息和具有不同功能的菜单条。我还有ImageViewerFormPictureBox显示在辅助显示器上以查看当前扫描的图像。

我正在使用Timer轮询图像扫描到的文件夹。扫描新图像并解锁图像后,我会将其抓取到FileStream中并在PictureBox中显示,请参见下文:

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;
        //currentImage = ImageFast.FromFile(filename);
        using (FileStream fsImage = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
            if (pb.InvokeRequired)
            {
                pb.Invoke(new MethodInvoker(
                delegate()
                {
                    pb.Image = currentImage;
                }));
            }
            else
            {
                pb.Image = currentImage;
            }
        }
    }
    catch (Exception imageEx)
    {
        throw new ExceptionHandler("Error when showing image", imageEx);
    }
}
public static Image ScaleImage(Image imgToResize, Size size)
{
    int sourceWidth = imgToResize.Width;
    int sourceHeight = imgToResize.Height;
    float nPercent = 0;
    float nPercentW = 0;
    float nPercentH = 0;
    nPercentW = ((float)size.Width / (float)sourceWidth);
    nPercentH = ((float)size.Height / (float)sourceHeight);
    if (nPercentH < nPercentW)
        nPercent = nPercentH;
    else
        nPercent = nPercentW;
    int destWidth = (int)(sourceWidth * nPercent);
    int destHeight = (int)(sourceHeight * nPercent);
    Bitmap b = new Bitmap(destWidth, destHeight);
    using (Graphics g = Graphics.FromImage(b))
    {
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
    }
    return b;
}

这样,PictureBox中显示的图像就不应该被锁定,但它确实被锁定了。问题是扫描的图像可能必须重新扫描,如果我这样做,我在尝试从扫描软件覆盖图像文件时会出现共享冲突错误。

有人知道我能做什么吗?

溶液

多亏了@SPFiredrake我有一个解决方案来创建临时文件以显示在 PictureBox 中,使原始图像保持解锁状态。

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;
        //currentImage = ImageFast.FromFile(filename);
        using (FileStream fsImage = new FileStream(CreateTempFile(filename), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
            if (pb.InvokeRequired)
            {
                pb.Invoke(new MethodInvoker(
                delegate()
                {
                    pb.Image = currentImage;
                }));
            }
            else
            {
                pb.Image = currentImage;
            }
        }
    }
    catch (Exception imageEx)
    {
        throw new ExceptionHandler("Error when showing image", imageEx);
    }
}
public static string CreateTempFile(string fileName)
{
    if (string.IsNullOrEmpty(fileName))
        throw new ArgumentNullException("fileName");
    if (!File.Exists(fileName))
        throw new ArgumentException("Specified file must exist!", "fileName");
    string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + Path.GetExtension(fileName));
    File.Copy(fileName, tempFile);
    Log.New("Temp file created: " + tempFile);
    return tempFile;
}

从图片框中解锁图像

这里的问题是图像是从 FileStream 加载的,该图像被 PictureBox 锁定,因为它包含对流的引用。您应该做的是首先将图片加载到本地内存中(通过 byte[] 数组(,然后从 MemoryStream 加载图像。在SetPicture方法中,您应该尝试以下更改,看看它是否有效:

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;
        byte[] imageBytes = File.ReadAllBytes(filename);
        using(MemoryStream msImage = new MemoryStream(imageBytes))
        {
            currentImage = ScaleImage(Image.FromStream(msImage), new Size(pb.Width, pb.Height));
        ....
}

编辑:在聊天中的对话之后,使用您最终使用的修复程序进行更新:

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;
        string tempFile = Path.Combine(Path.GetTempDirectory(), Guid.NewGuid().ToString() + Path.GetExtension(filename));
        File.Copy(filename, tempFile);
        //currentImage = ImageFast.FromFile(filename);
        using (FileStream fsImage = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            ...

这样,您就可以使用临时文件实际加载图片框,使原始文件保持不变(在初始副本之外(。

加载位图后,您不再保留文件流,因此一切正常。但是,如果您谈论的是加载发生并且扫描尝试覆盖该文件的时刻 - 始终扫描到"临时"或以垃圾命名的文件(使用 GUID 作为名称(。扫描完成后,将该文件重命名为 JPG - 然后您的显示表单将拾取并正确显示。

这样,重新扫描将只涉及尝试多次重命名临时文件,并带有"等待",以防止重叠的那一小块区域。

你的代码对我来说很好用。 我拿了一个精确的副本,并使用相同的图像文件反复调用它。

SetPicture(@"c:'temp'logo.png", pictureBox1);

其他内容正在锁定文件。 你能分享你的电话代码吗?

我想你现在已经完成了你的工作。
不过,我正在发帖以防其他人有同样的问题。
我有同样的问题:我在图片框控件中加载图像

picture.Image = new Bitmap(imagePath);  

以及尝试移动它时

File.Move(source, destination);  

mscorlib 抛出一个异常:
进程无法访问该文件,因为它正被另一个进程使用

我在这里找到了一个解决方案(尽管在 VB.Net 而不是 C# 中(,图片框"锁定"文件,无法移动/删除

帖子的作者克隆原始图像并将克隆的图像加载到 PictureBox 控件。
我稍微更改了代码并提出了这个:

private Bitmap CloneImage(string aImagePath) {  
    // create original image
    Image originalImage = new Bitmap(aImagePath);
    // create an empty clone of the same size of original
    Bitmap clone = new Bitmap(originalImage.Width, originalImage.Height);
    // get the object representing clone's currently empty drawing surface
    Graphics g = Graphics.FromImage(clone);
    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
    g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
    // copy the original image onto this surface
    g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height);
    // free graphics and original image
    g.Dispose();
    originalImage.Dispose();
    return clone;
    }

因此,我们的代码将是:

picture.Image = (Image)CloneImage(imagePath);  

这样做,我在移动文件时没有更多的例外。
我认为这是一个很好的替代方法,而且您不需要临时文件。

这是

杰克代码,但在Visual Basic .NET中,转换进入函数内部

 Private Function CloneImage(aImagePath As String) As Image
        ' create original image
        Dim originalImage As Image = New Bitmap(aImagePath)
        ' create an empty clone of the same size of original
        Dim clone As Bitmap = New Bitmap(originalImage.Width, originalImage.Height)
        ' get the object representing clone's currently empty drawing surface
        Dim g As Graphics = Graphics.FromImage(clone)
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed
        ' copy the original image onto this surface
        g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height)
        ' free graphics and original image
        g.Dispose()
        originalImage.Dispose()
        Return CType(clone, Image)
    End Function

所以打电话将是

picture.Image = CloneImage(imagePath)

谢谢杰克,

MS 对这个问题的回应...

对我来说是工作好的...

internal void UpdateLastImageDownloaded(string fullfilename)
{
    this.BeginInvoke((MethodInvoker)delegate()
    {
        try
        {
            //pictureBoxImage.Image = Image.FromFile(fullfilename);
            //Bitmap bmp = new Bitmap(fullfilename);
            //pictureBoxImage.Image = bmp;
            System.IO.FileStream fs;
            // Specify a valid picture file path on your computer.
            fs = new System.IO.FileStream(fullfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            pictureBoxImage.Image = System.Drawing.Image.FromStream(fs);
            fs.Close();
        }
        catch (Exception exc)
        {
            Logging.Log.WriteException(exc);
        }
    });
}

在尝试为我的 C# Windows 窗体找出解决方案时,我遇到了一篇有用的文章,其中提到了如何在图片框中加载图片而不"锁定"原始图片本身,而是它的实例。因此,如果您尝试删除,重命名或对原始文件执行任何操作,则不会收到错误消息的通知,该错误消息提到"该文件正被另一个进程使用"或其他任何内容!

这是对文章的引用。

总而言之,我相信此解决方案在处理少量图片时非常有用,因为大量应用此方法可能会导致内存不足。