科学做视频网站,在线做图片,建网站所需材料,简述网站建设的1. 为什么要在麒麟系统上折腾C#图片处理#xff1f; 如果你和我一样#xff0c;是个喜欢在国产操作系统上折腾开发的“手艺人”#xff0c;那你肯定遇到过这个头疼的问题#xff1a;想在麒麟系统上用C#写个图片处理的小工具#xff0c;比如批量压缩照片、给图片加水印 } try { // 使用 SKCodec 可以获取更多图片信息且流式解码更高效 using var codec SKCodec.Create(new MemoryStream(imageData)); if (codec null) { throw new InvalidOperationException(无法解码图片数据可能是不支持的格式或数据已损坏。); } // 获取图片信息宽、高、色彩空间等 var info codec.Info; Console.WriteLine($检测到图片格式: {codec.EncodedFormat}, 尺寸: {info.Width}x{info.Height}); // 创建 Bitmap 并解码 var bitmap new SKBitmap(info.Width, info.Height, SKColorType.Rgba8888, SKAlphaType.Premul); var result codec.GetPixels(bitmap.Info, bitmap.GetPixels()); if (result ! SKCodecResult.Success) { bitmap.Dispose(); throw new InvalidOperationException($图片解码失败: {result}); } return bitmap; } catch (Exception ex) { // 这里可以加入更详细的日志记录 throw new InvalidOperationException($从字节数组加载图片失败: {ex.Message}, ex); } }为什么要这么写异常处理对空数据、损坏数据进行了检查避免程序崩溃。使用SKCodec它比直接Decode更强大可以先读取图片元信息尺寸、格式再决定如何分配内存和解码对于处理未知来源或大型图片更安全、更高效。资源管理SKBitmap和SKCodec都实现了IDisposable。在using语句中使用SKCodec确保其被及时释放。SKBitmap需要调用者负责在最后Dispose。3.2 图片处理与保存格式、质量与路径解码成功后我们可能需要对图片进行处理然后保存。原始代码直接保存为JPEG我们来看看更多选项。public static void ProcessAndSaveImage(SKBitmap bitmap, string outputPath, int quality 90) { if (bitmap null) throw new ArgumentNullException(nameof(bitmap)); if (string.IsNullOrEmpty(outputPath)) throw new ArgumentException(输出路径无效。); // 1. 图片处理示例创建一个缩略图 int targetWidth 200; int targetHeight (int)((float)bitmap.Height / bitmap.Width * targetWidth); using var resizedBitmap new SKBitmap(targetWidth, targetHeight); using var canvas new SKCanvas(resizedBitmap); canvas.Clear(SKColors.White); // 设置背景色对于有透明通道的PNG转JPG很有用 canvas.DrawBitmap(bitmap, new SKRect(0, 0, targetWidth, targetHeight)); // 2. 根据文件扩展名决定保存格式 var fileExtension Path.GetExtension(outputPath).ToLowerInvariant(); SKEncodedImageFormat format; switch (fileExtension) { case .jpg: case .jpeg: format SKEncodedImageFormat.Jpeg; break; case .png: format SKEncodedImageFormat.Png; break; case .webp: format SKEncodedImageFormat.Webp; break; case .bmp: format SKEncodedImageFormat.Bmp; break; default: format SKEncodedImageFormat.Jpeg; // 默认格式 Console.WriteLine($不支持的扩展名 {fileExtension}将保存为JPEG格式。); break; } // 3. 确保输出目录存在 var directory Path.GetDirectoryName(outputPath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 4. 保存图片 using (var outputStream File.OpenWrite(outputPath)) { // 对于PNGquality参数无效对于Jpeg/Webp控制压缩质量(0-100) bool encodeSuccess resizedBitmap.Encode(outputStream, format, quality); if (!encodeSuccess) { throw new InvalidOperationException(图片编码保存失败。); } } Console.WriteLine($图片已成功保存至: {outputPath}); }这段代码的实用点自动格式推断根据文件路径的后缀名自动选择保存格式更智能。目录创建自动创建不存在的输出目录避免DirectoryNotFoundException。质量参数对于有损格式Jpeg、Webpquality参数非常关键。我通常设置85-95在文件大小和视觉质量间取得很好平衡。100反而可能因为文件过大而不必要。背景处理在绘制缩略图时先Clear一个背景色。这是处理带透明通道的PNG图片转换为JPG时的常见技巧否则透明区域会变成黑色。3.3 整合与调用一个完整的示例现在我们把读取、处理、保存串起来模拟一个类似原始文章但从硬件如读卡器获取数据的场景。class Program { static void Main(string[] args) { // 模拟从硬件如读卡器获取图片字节数据 byte[] hardwareImageData SimulateDataFromHardware(); if (hardwareImageData ! null) { try { // 1. 解码字节数组为SKBitmap using var originalBitmap LoadImageFromBytes(hardwareImageData); Console.WriteLine($原始图片加载成功尺寸: {originalBitmap.Width}x{originalBitmap.Height}); // 2. 定义输出路径可以基于时间、GUID等生成唯一文件名 string timestamp DateTime.Now.ToString(yyyyMMdd_HHmmss); string outputJpegPath Path.Combine(/home/yourusername/ProcessedImages, $photo_{timestamp}.jpg); string outputThumbnailPath Path.Combine(/home/yourusername/ProcessedImages/Thumbnails, $thumb_{timestamp}.png); // 3. 保存原始图片的JPEG版本 using (var outputStream File.OpenWrite(outputJpegPath)) { originalBitmap.Encode(outputStream, SKEncodedImageFormat.Jpeg, 92); } // 4. 生成并保存缩略图PNG格式保留透明 ProcessAndSaveImage(originalBitmap, outputThumbnailPath, quality: 100); // PNG quality无效 Console.WriteLine(图片处理流程完成); } catch (Exception ex) { Console.Error.WriteLine($处理过程中发生错误: {ex.Message}); // 实际项目中这里应该记录日志并可能进行错误恢复或通知 } } else { Console.WriteLine(未获取到有效的图片数据。); } } static byte[] SimulateDataFromHardware() { // 这里模拟硬件数据。实际项目中这里是你调用硬件SDK如CV100Gen.GetBMPData的地方。 // 例如从文件读取一张测试图片来模拟 string testImagePath test_input.jpg; if (File.Exists(testImagePath)) { return File.ReadAllBytes(testImagePath); } return null; } }这个示例提供了一个更完整的流程框架包含了错误处理、日志输出和资源管理。你可以把SimulateDataFromHardware方法替换成真实的硬件API调用。4. 进阶技巧与常见坑点排查掌握了基础流程后我们来看看一些能让你代码更上一层楼的进阶技巧以及如何避开那些让人抓狂的“坑”。4.1 性能优化处理大图与批量操作当你需要处理手机拍的高分辨率照片或者进行批量处理时性能就变得至关重要。使用SKImage替代SKBitmap进行编码如果你只需要读取图片信息或进行编码保存而不需要修改像素SKImage是更轻量、更合适的选择。它可以从流或字节数组直接创建并且可以直接编码到输出流避免了SKBitmap完整解码到内存的开销。using var data SKData.CreateCopy(imageBytes); using var image SKImage.FromEncodedData(data); using var output File.OpenWrite(output.jpg); image.Encode(SKEncodedImageFormat.Jpeg, 85).SaveTo(output);流式处理对于非常大的图片考虑使用SKCodec进行增量解码或者将处理分块进行避免一次性占用过多内存。释放资源释放资源释放资源SKBitmap、SKImage、SKSurface、SKCanvas等对象都持有非托管资源。务必在使用完毕后调用Dispose()方法或者使用using语句包裹。内存泄漏在长时间运行的服务中会是致命的。4.2 常见错误与解决方案“DllNotFoundException: Unable to load DLL libSkiaSharp”原因这是麒麟/Linux系统上最常见的问题。根本原因是SkiaSharp.NativeAssets.Linux.NoDependencies原生库没有被正确部署或加载。解决首先100%确认你的.csproj文件中引用了SkiaSharp.NativeAssets.Linux.NoDependencies包且版本与SkiaSharp主包完全一致。尝试清理并重新生成项目dotnet clean然后dotnet build。检查项目的输出目录bin/Debug/net8.0/或bin/Release/net8.0/下是否存在runtimes文件夹里面应该有linux-x64/native/libSkiaSharp.so等文件。如果没有可能是包没有正确恢复。终极方案如果以上都不行可以尝试在项目文件中显式指定运行时标识符(RID)强制包含对应平台的本地资产。PropertyGroup RuntimeIdentifierlinux-x64/RuntimeIdentifier /PropertyGroup然后重新运行dotnet publish -c Release进行发布。图片保存后颜色不对特别是红色蓝色互换原因Skia默认使用SKColorType.Rgba8888而某些旧的图片数据或硬件数据可能是Bgra顺序。System.Drawing的Bitmap在某些系统上底层也是BGR。解决在创建SKBitmap或解码时指定正确的颜色类型。// 尝试使用 Bgra8888 var bitmap new SKBitmap(width, height, SKColorType.Bgra8888, SKAlphaType.Premul); // 或者如果你有一个现有的SKBitmap可以转换它 using var newBitmap new SKBitmap(bitmap.Info.WithColorType(SKColorType.Bgra8888)); bitmap.CopyTo(newBitmap);处理带透明通道的PNG保存为JPG后背景变黑原因JPG格式不支持透明通道。当透明像素Alpha0被转换为JPG时其RGB值通常是0,0,0会显示出来也就是黑色。解决在绘制到画布或编码前先填充一个白色或其他颜色的背景。using var surface SKSurface.Create(bitmap.Info); using var canvas surface.Canvas; canvas.Clear(SKColors.White); // 先画一个白色背景 canvas.DrawBitmap(bitmap, 0, 0); // 再把原图画上去 using var image surface.Snapshot(); // 现在image编码成JPG就没有黑背景了4.3 调试技巧在VS Code中高效排错在麒麟上用VS Code调试C#和Windows上体验几乎一致非常顺畅。设置断点与监视在代码行号左侧点击即可设置断点。在调试侧边栏的“监视”窗口中可以添加你想监控的变量表达式。对于SKBitmap对象你可以监视它的Width、Height、Bytes等属性。条件断点右键点击断点可以设置条件比如只在图片尺寸大于2000像素时才中断这在处理批量数据时非常有用。查看图像数据进阶虽然不能直接可视化SKBitmap但你可以通过快速监视ShiftF9查看其像素数据的字节数组长度或者编写一小段调试代码将内存中的位图临时保存到文件然后去查看这个文件以确认处理结果是否正确。关注“问题”面板和终端输出编译错误、NuGet包还原失败等信息都会在这里显示。运行时异常也会在调试控制台输出详细的调用栈信息这是定位问题的第一手资料。我在实际项目里经常需要处理从各种扫描仪、摄像头过来的图像。这套环境组合让我能在熟悉的C#语言和VS Code编辑器里高效地完成开发、调试和部署。一开始在麒麟上配置确实会多花点时间特别是处理本地库依赖的时候但一旦跑通其稳定性和性能表现都让我非常满意。记住关键就是那对版本号必须一致的NuGet包这是通往成功的大门钥匙。