实现重绘

This commit is contained in:
zqm
2026-03-25 11:35:00 +08:00
parent 5eb4bd3621
commit 9f0ae9afa9
7 changed files with 455 additions and 19 deletions

View File

@@ -63,7 +63,7 @@ namespace Camera
this.picBoxCamera.Location = new System.Drawing.Point(0, 0); this.picBoxCamera.Location = new System.Drawing.Point(0, 0);
this.picBoxCamera.Name = "picBoxCamera"; this.picBoxCamera.Name = "picBoxCamera";
this.picBoxCamera.Size = new System.Drawing.Size(700, 515); this.picBoxCamera.Size = new System.Drawing.Size(700, 515);
this.picBoxCamera.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.picBoxCamera.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Normal;
this.picBoxCamera.TabIndex = 0; this.picBoxCamera.TabIndex = 0;
this.picBoxCamera.TabStop = false; this.picBoxCamera.TabStop = false;
this.picBoxCamera.Paint += new System.Windows.Forms.PaintEventHandler(this.PicBoxCamera_Paint); this.picBoxCamera.Paint += new System.Windows.Forms.PaintEventHandler(this.PicBoxCamera_Paint);

View File

@@ -41,12 +41,21 @@ namespace Camera
public void SetImage(Image image) public void SetImage(Image image)
{ {
if (picBoxCamera.Image != null) try
{ {
picBoxCamera.Image.Dispose(); if (picBoxCamera.Image != null)
{
picBoxCamera.Image.Dispose();
}
Bitmap newImage = new Bitmap(image.Width, image.Height);
using (Graphics g = Graphics.FromImage(newImage))
{
g.DrawImage(image, 0, 0);
}
picBoxCamera.Image = newImage;
picBoxCamera.Invalidate();
} }
picBoxCamera.Image = (Image)image.Clone(); catch { }
picBoxCamera.Invalidate();
} }
public void SetConfigPath(string path) public void SetConfigPath(string path)
@@ -184,11 +193,20 @@ namespace Camera
private void UpdateImage(Image image) private void UpdateImage(Image image)
{ {
if (picBoxCamera.Image != null) try
{ {
picBoxCamera.Image.Dispose(); if (picBoxCamera.Image != null)
{
picBoxCamera.Image.Dispose();
}
Bitmap newImage = new Bitmap(image.Width, image.Height);
using (Graphics g = Graphics.FromImage(newImage))
{
g.DrawImage(image, 0, 0);
}
picBoxCamera.Image = newImage;
} }
picBoxCamera.Image = image; catch { }
} }
private void UpdateDataGridView() private void UpdateDataGridView()
@@ -216,8 +234,16 @@ namespace Camera
private void PicBoxCamera_Paint(object sender, PaintEventArgs e) private void PicBoxCamera_Paint(object sender, PaintEventArgs e)
{ {
Image currentImage = picBoxCamera.Image; Image currentImage = null;
if (currentImage == null) return; try
{
currentImage = picBoxCamera.Image;
if (currentImage == null) return;
}
catch
{
return;
}
int imageWidth = 0, imageHeight = 0; int imageWidth = 0, imageHeight = 0;
bool imageValid = false; bool imageValid = false;
@@ -232,12 +258,17 @@ namespace Camera
} }
catch catch
{ {
return;
} }
if (!imageValid) return; if (!imageValid) return;
Graphics g = e.Graphics; Graphics g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
Rectangle destRect = new Rectangle(0, 0, picBoxCamera.ClientSize.Width, picBoxCamera.ClientSize.Height);
g.DrawImage(currentImage, destRect, 0, 0, currentImage.Width, currentImage.Height, GraphicsUnit.Pixel);
float scaleX = (float)picBoxCamera.ClientSize.Width / imageWidth; float scaleX = (float)picBoxCamera.ClientSize.Width / imageWidth;
float scaleY = (float)picBoxCamera.ClientSize.Height / imageHeight; float scaleY = (float)picBoxCamera.ClientSize.Height / imageHeight;
@@ -498,7 +529,7 @@ namespace Camera
{ {
_camera.SetLedZone(newZone); _camera.SetLedZone(newZone);
} }
picBoxCamera.Invalidate(); picBoxCamera.Update();
} }
} }
else if (_isMoving) else if (_isMoving)
@@ -526,7 +557,7 @@ namespace Camera
{ {
_camera.SetLedZone(newZone); _camera.SetLedZone(newZone);
} }
picBoxCamera.Invalidate(); picBoxCamera.Update();
} }
} }
else else

View File

@@ -27,6 +27,7 @@ namespace Test
private void Camera_ImageCaptured(object sender, ImageEventArgs e) private void Camera_ImageCaptured(object sender, ImageEventArgs e)
{ {
System.Diagnostics.Debug.WriteLine("Form1: 收到图像事件");
if (InvokeRequired) if (InvokeRequired)
{ {
Invoke(new Action<Image>(UpdatePictureBox), e.Image); Invoke(new Action<Image>(UpdatePictureBox), e.Image);
@@ -39,11 +40,10 @@ namespace Test
private void UpdatePictureBox(Image image) private void UpdatePictureBox(Image image)
{ {
if (pictureBox1.Image != null) System.Diagnostics.Debug.WriteLine("Form1: 更新图像, Size=" + image.Width + "x" + image.Height);
{ Bitmap bitmap = new Bitmap(image);
pictureBox1.Image.Dispose(); pictureBox1.Image = bitmap;
} pictureBox1.Refresh();
pictureBox1.Image = image;
} }
private void button1_Click(object sender, EventArgs e) private void button1_Click(object sender, EventArgs e)
@@ -62,7 +62,14 @@ namespace Test
private void button2_Click(object sender, EventArgs e) private void button2_Click(object sender, EventArgs e)
{ {
_camera.SetArea(); _camera.SetConfigPath(@"D:\YeXian\EasyTest\EasyTest\bin\Debug\生产测试\TCM232V0.8_自动\Config");
Setting settingForm = new Setting();
settingForm.SetCamera(_camera);
if (_camera.GetCurrentImage() != null)
{
settingForm.SetImage(_camera.GetCurrentImage());
}
settingForm.ShowDialog();
} }
protected override void OnFormClosing(FormClosingEventArgs e) protected override void OnFormClosing(FormClosingEventArgs e)

View File

@@ -0,0 +1,74 @@
namespace Test
{
partial class Form2
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.button1 = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(30, 50);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(454, 376);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// button1
//
this.button1.Location = new System.Drawing.Point(569, 152);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(109, 49);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.button1);
this.Controls.Add(this.pictureBox1);
this.Name = "Form2";
this.Text = "Form2";
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Button button1;
}
}

View File

@@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Drawing.Imaging;
namespace Test
{
public partial class Form2 : Form
{
// 摄像头配置信息
private const string IP = "192.168.100.60";
private const string SNAPSHOT_URI = "/snapshot.jpg";
private const string USER = "admin";
private const string PASSWD = "Yexian.net.168";
// 保存路径
private const string SAVE_DIRECTORY = @"D:\Projects";
private string _authorization;
public Form2()
{
InitializeComponent();
// 确保保存目录存在
if (!Directory.Exists(SAVE_DIRECTORY))
{
Directory.CreateDirectory(SAVE_DIRECTORY);
}
}
private void button1_Click(object sender, EventArgs e)
{
Image image = GetCameraImage();
pictureBox1.Image = image;
// 自动保存图像到D:\Projects目录
SaveImageToProjects(image);
}
/// <summary>
/// 将图像保存到D:\Projects目录
/// </summary>
private void SaveImageToProjects(Image image)
{
try
{
if (image != null)
{
// 生成带时间戳的文件名
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff");
string fileName = $"CameraSnapshot_{timestamp}.jpg";
string filePath = Path.Combine(SAVE_DIRECTORY, fileName);
// 保存图像
image.Save(filePath, ImageFormat.Jpeg);
MessageBox.Show($"图像已保存到:{filePath}", "保存成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
System.Diagnostics.Debug.WriteLine($"图像保存成功:{filePath}");
}
}
catch (Exception ex)
{
MessageBox.Show($"保存图像失败:{ex.Message}", "保存失败",
MessageBoxButtons.OK, MessageBoxIcon.Error);
System.Diagnostics.Debug.WriteLine($"图像保存失败:{ex.Message}");
}
}
/// <summary>
/// 手动保存当前显示的图像
/// </summary>
public void SaveCurrentImage()
{
if (pictureBox1.Image != null)
{
SaveImageToProjects(pictureBox1.Image);
}
else
{
MessageBox.Show("没有可保存的图像", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
private Bitmap GetCameraImage()
{
string url = "http://" + IP + SNAPSHOT_URI;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Headers.Add("Cache-Control", "no-cache");
request.Headers.Add("Pragma", "no-cache");
if (!string.IsNullOrEmpty(_authorization))
{
request.Headers.Add("Authorization", _authorization);
}
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
}
catch (System.Net.WebException ex)
{
if (ex.Response != null && ((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.Unauthorized)
{
response = (HttpWebResponse)ex.Response;
string authHeader = response.Headers["WWW-Authenticate"];
response.Close();
string realm = ExtractAuthParam(authHeader, "realm");
string nonce = ExtractAuthParam(authHeader, "nonce");
_authorization = GenerateDigestAuthorization(USER, PASSWD, realm, nonce, "GET", SNAPSHOT_URI);
request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.Headers.Add("Authorization", _authorization);
response = (HttpWebResponse)request.GetResponse();
}
else
{
throw;
}
}
byte[] imageData;
using (Stream stream = response.GetResponseStream())
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
imageData = memoryStream.ToArray();
}
return new Bitmap(new MemoryStream(imageData));
}
/// <summary>
/// 从WWW-Authenticate头中提取参数
/// </summary>
private string ExtractAuthParam(string authHeader, string paramName)
{
Regex regex = new Regex(paramName + "=\"([^\"]+)\"");
Match match = regex.Match(authHeader);
if (match.Success)
{
return match.Groups[1].Value;
}
return string.Empty;
}
/// <summary>
/// 生成Digest认证的Authorization头
/// </summary>
private string GenerateDigestAuthorization(string username, string password, string realm, string nonce, string method, string uri)
{
string ha1 = CalculateMD5(username + ":" + realm + ":" + password);
string ha2 = CalculateMD5(method + ":" + uri);
string response = CalculateMD5(ha1 + ":" + nonce + ":" + ha2);
return string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"",
username, realm, nonce, uri, response);
}
/// <summary>
/// 计算MD5哈希值
/// </summary>
private string CalculateMD5(string input)
{
using (MD5 md5 = MD5.Create())
{
byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
byte[] hashBytes = md5.ComputeHash(inputBytes);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
sb.Append(hashBytes[i].ToString("x2"));
}
return sb.ToString();
}
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
@@ -56,11 +56,20 @@
<Compile Include="Form1.Designer.cs"> <Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon> <DependentUpon>Form1.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Form2.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form2.Designer.cs">
<DependentUpon>Form2.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Form1.resx"> <EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon> <DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Form2.resx">
<DependentUpon>Form2.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx"> <EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>