WPF编写类似ImagJ看图软件

 

 

 

整机程序结构

 

 

复制代码
<Window x:Class="WpfApp3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="60" Width="300" Closing="Window_Closing">
    <Grid>
        <Border>
            <Grid Drop="ImagePanel_Drop" AllowDrop="true" >
                <TextBlock x:Name="TbGray" Margin="10,0,0,0" />
                <TextBlock x:Name="TbTiShi" Text="请把图像拖到此处" Foreground="#92A3BB" VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </Grid>
        </Border>
    </Grid>
</Window>
MainWindow.xaml
复制代码
复制代码
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Window = System.Windows.Window;

namespace WpfApp3
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        List<ImageShow> imageShows = new List<ImageShow>();
        ImageShow ActiveImageShow;
        bool IsClosing = false;
        public MainWindow()
        {
            InitializeComponent();
            Task.Run(() =>
            {
                while (IsClosing == false)
                {
                    Thread.Sleep(1);
                    this.Dispatcher.Invoke(new Action(() =>
                    {
                        foreach (ImageShow imageShow in imageShows)
                        {
                            if (imageShow.IsActive && imageShow != ActiveImageShow)
                            {
                                ActiveImageShow = imageShow;
                            }
                        }
                        if(ActiveImageShow != null)
                            TbGray.Text = ActiveImageShow.XYGray;
                    }));
                }
            });
        }
        private BitmapSource MatToBitmapSource(Mat mat,ref byte[] gray)
        {
            PixelFormat pf = PixelFormats.Gray8;
            List<System.Windows.Media.Color> colors = new List<System.Windows.Media.Color>();
            colors.Add(System.Windows.Media.Colors.Red);
            colors.Add(System.Windows.Media.Colors.Blue);
            colors.Add(System.Windows.Media.Colors.Green);
            BitmapPalette myPalette = new BitmapPalette(colors);
            int rawStride = (mat.Width * pf.BitsPerPixel + 7) / 8;
            mat.GetArray(out gray);
            //force garbage collection
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
            return BitmapSource.Create(mat.Width, mat.Height, 96, 96, pf, myPalette, gray, rawStride);
        }
        public void HandleImageBuff(Mat mat, string name)
        {
            //在画布中添加图像
            var imageUser = new ImageUser();
            imageUser.Img = new Image();
            imageUser.Img.Source = MatToBitmapSource(mat, ref imageUser.ImgGray);
            imageUser.Img.Stretch = Stretch.Uniform;
            imageUser.Img.Width = mat.Width;
            imageUser.Img.Height = mat.Height;
            imageUser.Img.SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.NearestNeighbor);//取消抗锯齿
            imageUser.ImgName = name;
            var imageShow = new ImageShow(imageUser);
            imageShow.Show();
            imageShows.Add(imageShow);
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            IsClosing = true;
            foreach (ImageShow imageShow in imageShows)
                imageShow.Close();
        }

        private void ImagePanel_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                TbTiShi.Text = "";
                // Note that you can have more than one file.
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                var OriginalMat = Cv2.ImRead(files[0], ImreadModes.Grayscale);
                //显示图片
                HandleImageBuff(OriginalMat, "原图");

                Mat imageSobel = new Mat();
                Cv2.Sobel(OriginalMat, imageSobel, MatType.CV_8U, 1, 0);//Sobel算子计算梯度值
                HandleImageBuff(imageSobel, "SobelX");

                Cv2.Sobel(OriginalMat, imageSobel, MatType.CV_8U, 0, 1);
                HandleImageBuff(imageSobel, "SobelY");

                Cv2.Sobel(OriginalMat, imageSobel, MatType.CV_8U, 1, 1);
                HandleImageBuff(imageSobel, "SobelXY");
            }
        }
    }

    public class ImageUser
    {
        public Image Img;
        public string ImgName;
        public byte[] ImgGray;
    }

}
MainWindow.xaml.cs
复制代码
复制代码
<Window x:Class="WpfApp3.ImageShow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp3"
        mc:Ignorable="d"
        Title="ImageShow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <Border x:Name="outside" Background="Gray" 
                PreviewMouseDown="outsidewrapper_PreviewMouseDown" 
                PreviewMouseMove="outsidewrapper_PreviewMouseMove" 
                PreviewMouseUp="outside_PreviewMouseUp"
                PreviewMouseWheel="outside_PreviewMouseWheel"
                ClipToBounds="True" ScrollViewer.HorizontalScrollBarVisibility="Visible">

            <Canvas x:Name="inside"   
                    Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type Border}}}" 
                    Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType={x:Type Border}}}"
                    ScrollViewer.HorizontalScrollBarVisibility="Visible">
                <Canvas.RenderTransform>
                    <TransformGroup/>
                </Canvas.RenderTransform>
            </Canvas>
        </Border>
    </Grid>
</Window>
ImageShow.xaml
复制代码
复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WpfApp3
{
    /// <summary>
    /// ImageShow.xaml 的交互逻辑
    /// </summary>
    public partial class ImageShow : Window
    {
        public string XYGray;
        System.Windows.Point previousPoint;
        bool isTranslateStart = false;
        ImageUser ImageUser = new ImageUser();

        public ImageShow(ImageUser imageUser)
        {
            InitializeComponent();
            ImageUser = imageUser;

            this.Title = ImageUser.ImgName;
            this.Height = 500;
            this.Width = this.Height * ImageUser.Img.Width / ImageUser.Img.Height;
            inside.Children.Add(ImageUser.Img);
            Canvas.SetTop(ImageUser.Img, 0);
            Canvas.SetLeft(ImageUser.Img, 0);
            Task.Run(() =>
            {
                this.Dispatcher.Invoke(new Action(() =>
                {
                    FullScreen();
                }));
            });
        }

        private void FullScreen()
        {
            Double IamgeWidth = ImageUser.Img.Width;
            Double IamgeHeight = ImageUser.Img.Height;
            TransformGroup tg = inside.RenderTransform as TransformGroup;
            System.Windows.Media.Matrix value = new System.Windows.Media.Matrix();
            //全屏
            value.M11 = outside.ActualWidth / IamgeWidth / tg.Value.M11;//缩放倍数为Border的尺寸
            value.M22 = outside.ActualHeight / IamgeHeight / tg.Value.M22;

            ////图像比例不变全屏 固定宽度
            //value.M11 = outside.ActualWidth / IamgeWidth / tg.Value.M11;//缩放倍数为Border的尺寸
            //value.M22 = outside.ActualWidth / IamgeWidth * IamgeHeight / IamgeHeight / tg.Value.M22;

            ////图像比例不变全屏 固定高度
            //value.M11 = outside.ActualHeight / IamgeHeight * IamgeWidth / IamgeWidth / tg.Value.M11;//缩放倍数为Border的尺寸
            //value.M22 = outside.ActualHeight / IamgeHeight / tg.Value.M22;

            value.OffsetX = (0 - tg.Value.OffsetX) * value.M11;//调整偏移XY到(0,0)
            value.OffsetY = (0 - tg.Value.OffsetY) * value.M22;
            MatrixTransform matrixTransform = new MatrixTransform(value);//添加矩阵变换
            tg.Children.Add(matrixTransform);

        }

        private void outsidewrapper_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.MiddleButton == MouseButtonState.Pressed || e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed)
            {
                previousPoint = e.GetPosition(outside);
                isTranslateStart = true;
            }
            e.Handled = true;
        }

        private void outsidewrapper_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.MiddleButton == MouseButtonState.Pressed || e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed)
            {
                if (isTranslateStart)
                {
                    System.Windows.Point currentPoint = e.GetPosition(outside);  //不能用 inside,必须用outside
                    System.Windows.Vector v = currentPoint - previousPoint;
                    TransformGroup tg = inside.RenderTransform as TransformGroup;
                    tg.Children.Add(new TranslateTransform(v.X, v.Y));  //centerX和centerY用外部包装元素的坐标,不能用内部被变换的Canvas元素的坐标
                    previousPoint = currentPoint;
                }
            }
            else
            {
                //适用于静止图像
                System.Windows.Point point2 = Mouse.GetPosition(inside);//获取Canvas坐标
                UInt32 x, y;
                x = (UInt32)point2.X;
                y = (UInt32)point2.Y;
                XYGray = $"x:{x} y:{y} gray:- ";//显示坐标值

                if (x >= 0 && x < ImageUser.Img.Width && y >= 0 && y < ImageUser.Img.Height)
                    XYGray = $"x:{x} y:{y} gray:{ImageUser.ImgGray[y * (int)ImageUser.Img.Width + x]}";
            }
            e.Handled = true;
        }

        private void outside_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (e.MiddleButton == MouseButtonState.Pressed && e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released)
            {
                if (isTranslateStart)
                {
                    isTranslateStart = false;
                }
            }
            e.Handled = true;
        }
        private void outside_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            System.Windows.Point currentPoint = e.GetPosition(outside);  //不能用 inside,必须用outside
            TransformGroup tg = inside.RenderTransform as TransformGroup;
            double s = ((double)e.Delta) / 1000.0 + 1.0;
            if (ImageUser.Img.Height * tg.Value.M22 < 200 || ImageUser.Img.Width * tg.Value.M11 < 200)
            {
                if (s > 1.0)//当图像像素宽度和高度小于200的时候只能放大,不再缩小
                    tg.Children.Add(new ScaleTransform(s, s, currentPoint.X, currentPoint.Y));
            }
            else
                tg.Children.Add(new ScaleTransform(s, s, currentPoint.X, currentPoint.Y));
            e.Handled = true;
        }
    }
}
ImageShow.xaml.cs
复制代码

此程序打开图像用的库是OpenCvSharp4与OpenCvSharp4.runtime.win

posted @   阿坦  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示