介绍
最近一直在研究Flutter,但是在创建UI的时候经常想获取屏幕上的颜色。
在这种情况下,我想要一个可以轻松获取颜色信息的软件,所以我做了它。
如果需要,可以从本文底部下载。
下面是哪种应用程序的图像
嵌入 Qiita 的视频
— 鱿鱼 (@ika_kk)2022 年 8 月 10 日
我在 C# 中制作了一个具有零个不必要功能的颜色选择器皮c。山雀河。这个 m/jo M0D q9P背景
我尝试了各种方法来获取颜色,但我找不到适合我的方法...
- 截图 + Photoshop
我一开始也是这样做的,但是太麻烦了- 画 3d
我可以做我想做的,但毕竟有很多准备工作- 只是颜色选择器
优秀的软件。没有比这更好的了,不是吗?
然而,窗口尺寸有点大,这是球的一个缺陷,我有时认为这是一个障碍。似乎还有很多其他的东西,但我决定在学习的同时尝试自己制作。
这个应用程序的特点
这很简单。
①可指定提取尺寸
这是特色功能。
在提取精确像素颜色时,由于噪声的影响,它变得相当不均匀,因此我们可以指定提取范围。
换言之,当增加提取范围时,获取范围内的平均颜色信息。
这可以在一定程度上抵消噪音的影响。
但是,监视器区域的外部似乎是黑色的(#000000),当提取尺寸较大并提取显示器边缘附近的区域时,颜色可能会变暗一些。在这种情况下,请缩小提取大小并采取对策。② 可以得到色码/RGB/HSB/灰度值
除了常用的色码(#RRGGBB)和RGB值,还可以得到HSV值和灰度值转换为灰度。
关于 HSV,所需的值范围因使用的场景而异,因此可以指定。
H 为 0~360 或 0~255,S 和 V 为 0~255 或 0~100。
根据要使用的场景指定单选按钮。按照我常用的标准电视播放标准,灰度值为0.3R+0.59G+0.11B。
③其他特点
我觉得可以随时看到颜色信息会很方便,所以我将它设置为始终显示在屏幕的最前面。
另外,我尝试删除标题栏以使其看起来很酷。
可以通过拖动适当的位置来调整位置。
(WPF) 尝试实现一个时尚的半透明窗口 - Mame - Tantanmen技术故事
如何获取屏幕上的像素值
这是这个应用程序的关键部分。我实现了像素提取如下。
第 8 到 11 行 graphics.CopyFromScreen(...) 提取屏幕的任意位置和大小。
- 参数 1:提取范围的左上角坐标。指定从当前光标位置减去直径/2=半径的点。
- 参数 2:写入位图时的起始坐标。指定位图的左上角 = (0, 0)。
- 参数 3:提取范围的大小。指定直径。
捕获光标周围的实现部分// サーチ直径 int _diam; while (true) { // 現在のカーソル位置周辺の画像取得 var bitmap = new Bitmap(_diam, _diam); var currentPos = System.Windows.Forms.Cursor.Position; using (var graphic = Graphics.FromImage(bitmap)) { graphic.CopyFromScreen( new System.Drawing.Point(currentPos.X - _diam / 2, currentPos.Y - _diam / 2), new System.Drawing.Point(0, 0), new System.Drawing.Size(_diam, _diam)); } // 変数bitmapには、現在のカーソル周辺をキャプチャした画像が入ります。 // 以降その画像をよしなにして平均値を取得し、画面に表示しています。 }我相信你们中的一些人已经注意到了,所以我承认。
虽然宣传是取圆圈内的平均值,但实际上取的是正方形内的平均值。圈内判断很麻烦这是一个温柔的谎言,不是吗?
请原谅我,因为无论是圆形还是方形,结果可能都没有太大变化。关于提取大小和处理时间
我认为如果提取尺寸很大,则需要时间来获得平均值,所以我测量了它。
- 实验环境
- Windows10, Core i7-6700(x64), RAM16GB, GeForceGTX960, 60fps 显示器
- 实验条件
- 一边摇动萃取范围的水平一边测量 1,000 次。
- 让我们看一下平均值。
对于 $N$ 的搜索直径,我预计处理时间会增加 $O(N^2)$,但它会在 256px 的直径下保持 60fps。
超过512px,它是二次增长的,但我并没有真正使用这么大的半径,所以它应该不是问题。
(特别是这次实现的上限是32px)
我用调试监视器检查了 CPU 使用率和内存。
CPU 使用率没有明显变化,32px 的内存最大约为 100MB。完整代码
我把它上传到 GitHub,但现在。
- 语言 C# / .NET Framework 4.8
- 编辑器 Visual Studio Community 2017 版本 15.7.4
- 我不使用任何特殊库,基本上是原生编写的
后面的代码
主窗口.csusing 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.Navigation; using System.Windows.Shapes; using System.Drawing; using System.Drawing.Imaging; using System.Diagnostics; using System.Runtime.InteropServices; namespace ColorPickerPj { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { /// <summary>サーチ半径を保持する変数</summary> private int _diam = 1; /// <summary> /// コンストラクタ。 /// プログラム実行と同時に色取得処理を開始する。 /// </summary> public MainWindow() { InitializeComponent(); // ウインドウ半透明にしたときにドラッグできるようにする // 以下参考URL // https://p4j4.hatenablog.com/entry/2018/03/31/170251 MouseLeftButtonDown += (_,__) => { DragMove(); }; // メインループ var task = new Task(MainLoop); task.Start(); } /// <summary> /// プログラム実行中まわりつづけるループ。 /// 常に現在のカーソル周辺の画素値を分析しつづける。 /// タスクとして使用。 /// </summary> private void MainLoop() { while (true) { // 現在のカーソル位置周辺の画像取得 var bitmap = new Bitmap(_diam, _diam); var currentPos = System.Windows.Forms.Cursor.Position; using (var graphic = Graphics.FromImage(bitmap)) { graphic.CopyFromScreen( new System.Drawing.Point(currentPos.X - _diam / 2, currentPos.Y - _diam / 2), new System.Drawing.Point(0, 0), new System.Drawing.Size(_diam, _diam)); } // デバッグ用 //bitmap.Save("temp.bmp"); // 画素値(RGB)取得 byte r, g, b; GetMeanValueFromBitmap(bitmap, out r, out g, out b); // HSVへ変換 double h, s, v; RgbToHsv(r, g, b, out h, out s, out v); // グレースケール値 byte gray = (byte)(0.30 * r + 0.59 * g + 0.11 * b); // 表示更新 // 実際にはHSVも同時に計算して表示する var msg = new Action<byte, byte, byte, double, double, double, byte>(UpdateValue); Dispatcher.Invoke(msg, r, g, b, h, s, v, gray); } } /// <summary> /// 画像の各チャンネルの平均画素値を取得する。 /// 下記参考URL /// https://www.84kure.com/blog/2014/07/13/c-%E3%83%93%E3%83%83%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E3%81%AB%E3%83%94%E3%82%AF%E3%82%BB%E3%83%AB%E5%8D%98%E4%BD%8D%E3%81%A7%E9%AB%98%E9%80%9F%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9/ /// </summary> /// <param name="bitmap">入力画像。デフォルトは32bitARGBのはず。</param> /// <param name="r">Rチャンネルの平均画素値</param> /// <param name="g">Gチャンネルの平均画素値</param> /// <param name="b">Bチャンネルの平均画素値</param> private void GetMeanValueFromBitmap(Bitmap bitmap, out byte r, out byte g, out byte b) { BitmapData data = bitmap.LockBits( new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); // リストに画素値を格納していく。byte型だと平均が出せないのでdouble型でやる。 var rValues = new List<double>(); var gValues = new List<double>(); var bValues = new List<double>(); int bytes = bitmap.Width * bitmap.Height * 4; for (int i = 0; i < bytes; i += 4) { bValues.Add(Marshal.ReadByte(data.Scan0, i)); gValues.Add(Marshal.ReadByte(data.Scan0, i + 1)); rValues.Add(Marshal.ReadByte(data.Scan0, i + 2)); } bitmap.UnlockBits(data); // なんかの拍子に格納されてなかったら怖いので一応条件分岐 if (rValues.Count > 0 && gValues.Count > 0 && bValues.Count > 0) { r = (byte)rValues.Average(); g = (byte)gValues.Average(); b = (byte)bValues.Average(); } else { r = g = b = 0; } } /// <summary> /// RGBをHSVに変換する。 /// Hは0~360、sとvは0~255で取得する。 /// </summary> /// <param name="r">R値</param> /// <param name="g">G値</param> /// <param name="b">B値</param> /// <param name="h">H値。0~360</param> /// <param name="s">S値。0~255</param> /// <param name="v">V値。0~255</param> private void RgbToHsv(byte r, byte g, byte b, out double h, out double s, out double v) { double max = Math.Max(r, Math.Max(g, b)); double min = Math.Min(r, Math.Min(g, b)); // 色相 if (r == g && g == b) { h = 0; } else if (r >= g && r >= b) { h = 60.0 * ((double)(g - b) / (max - min)); } else if (g >= r && g >= b) { h = 60.0 * ((double)(b - r) / (max - min)) + 120; } else { h = 60.0 * ((double)(r - g) / (max - min)) + 240; } if (h < 0) { h += 360; } // 彩度 if (max == 0) { s = 0; } else { s = (max - min) / max; s *= 255; } // 明度 v = max; } /// <summary> /// RGB値とHSV値をもとに画面表示を更新する。 /// HSVに関しては画面の設定をもとに値域を変換する。 /// </summary> /// <param name="r">R値</param> /// <param name="g">G値</param> /// <param name="b">B値</param> /// <param name="h">H値。0~360</param> /// <param name="s">S値。0~255</param> /// <param name="v">V値。0~255</param> /// <param name="gray">グレースケール値</param> private void UpdateValue(byte r, byte g, byte b, double h, double s, double v, byte gray) { // サーチ直径の表示色を見えやすい色に設定 brushEllipse.Stroke = v < 128 ? new SolidColorBrush(System.Windows.Media.Color.FromArgb(200, 200, 200, 200)): // 背景が暗い場合は白線 new SolidColorBrush(System.Windows.Media.Color.FromArgb(200, 55, 55, 55)); // 背景が明るい場合は黒線 // hsvの値域を調整 if (h255RadioButton.IsChecked == true) h = (byte)(h * 255.0 / 360.0); if (s100RadioButton.IsChecked == true) s = (byte)(s * 100.0 / 255.0); if (v100RadioButton.IsChecked == true) v = (byte)(v * 100.0 / 255.0); // 値更新 string code = $"#{r.ToString("X2")}{g.ToString("X2")}{b.ToString("X2")}"; colorCodeTextBox.Text = code; rTextBox.Text = r.ToString(); gTextBox.Text = g.ToString(); bTextBox.Text = b.ToString(); hTextBox.Text = ((int)h).ToString(); sTextBox.Text = ((int)s).ToString(); vTextBox.Text = ((int)v).ToString(); grayTextBox.Text = gray.ToString(); colorRectangle.Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(r, g, b)); } /// <summary> /// マウスホイールでスライダーを制御するイベント。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void diamSlider_MouseWheel(object sender, MouseWheelEventArgs e) { if (e.Delta > 0) { diamSlider.Value++; } else if (e.Delta < 0) { diamSlider.Value--; } } /// <summary> /// スライダーの値をもとにサーチ半径表示を更新 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void diamSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { _diam = (int)diamSlider.Value; diamTextBlock.Text = $"{_diam} px"; brushEllipse.Width = brushEllipse.Height = _diam + 2; } /// <summary> /// 終了イベント /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void closeButton_Click(object sender, RoutedEventArgs e) { Close(); } } }用户界面 (XAML)
主窗口.xaml<Window x:Class="ColorPickerPj.MainWindow" xmlns:system="clr-namespace:System;assembly=mscorlib" 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:ColorPickerPj" mc:Ignorable="d" Title="MainWindow" Height="165.201" Width="363.467" Topmost="True" SizeToContent="WidthAndHeight" Background="#E6444444" x:Name="mainWindow" WindowStyle="None" AllowsTransparency="True"> <Window.Resources> <Style TargetType="TextBlock" x:Key="normalTextBlock"> <Setter Property="Foreground" Value="#FFCCCCCC"/> </Style> <Style TargetType="TextBlock" x:Key="valueTextBlock"> <Setter Property="Foreground" Value="#FFCCCCCC"/> <Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="Margin" Value="0,0,5,0"/> </Style> <Style TargetType="RadioButton"> <Setter Property="Foreground" Value="#FFCCCCCC"/> <Setter Property="FontSize" Value="9"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> </Style> <!-- 通常時のボタンの色 --> <SolidColorBrush x:Key="NormalButtonBrush" Color="Transparent"/> <!-- 通常時のボタンの背景色 --> <SolidColorBrush x:Key="NormalBackgroundBrush" Color="Transparent"/> <!-- マウスオーバー時のボタンの色 --> <SolidColorBrush x:Key="HoverButtonBrush" Color="Transparent"/> <!-- マウスオーバー時のボタンの背景色 --> <SolidColorBrush x:Key="HoverBackgroundBrush" Color="Transparent"/> <!-- カスタムボタンのスタイル --> <Style x:Key="HoverButtonStyle" TargetType="{x:Type Button}"> <Setter Property="Foreground" Value="{StaticResource NormalButtonBrush}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border Name="HoverButtonBorder" BorderThickness="2" BorderBrush="{StaticResource NormalButtonBrush}" Background="{StaticResource NormalBackgroundBrush}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <ControlTemplate.Triggers> <!-- マウスオーバー --> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="HoverButtonBorder" Property="BorderBrush" Value="{StaticResource HoverButtonBrush}" /> <Setter TargetName="HoverButtonBorder" Property="Background" Value="{StaticResource HoverBackgroundBrush}" /> <Setter Property="Foreground" Value="{StaticResource HoverButtonBrush}"/> </Trigger> <!-- ボタンを押した時に、背景色を透過 --> <Trigger Property="IsPressed" Value="True"> <Setter TargetName="HoverButtonBorder" Property="Background" Value="{StaticResource NormalBackgroundBrush}" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid Margin="5"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="72"/> <RowDefinition Height="50"/> </Grid.RowDefinitions> <!-- 中段 --> <Grid Grid.Row="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="120px"/> <ColumnDefinition Width="72px"/> <ColumnDefinition Width="120px"/> </Grid.ColumnDefinitions> <!-- 色情報表示部(文字) --> <Grid Grid.Column="0" Margin="3"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="2.5*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="2.5*"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="2.5*"/> </Grid.ColumnDefinitions> <TextBlock Style="{StaticResource normalTextBlock}" x:Name="colorCodeTextBox" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="6" Text="#xxxxxx" TextAlignment="Center"/> <TextBlock Style="{StaticResource normalTextBlock}" Grid.Row="1" Grid.Column="0" Text="R" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FFCC8F8F"/> <TextBlock Style="{StaticResource normalTextBlock}" Grid.Row="1" Grid.Column="2" Text="G" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FF90CC8F"/> <TextBlock Style="{StaticResource normalTextBlock}" Grid.Row="1" Grid.Column="4" Text="B" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FF8F96EE"/> <TextBlock Style="{StaticResource valueTextBlock}" x:Name="rTextBox" Grid.Row="1" Grid.Column="1"/> <TextBlock Style="{StaticResource valueTextBlock}" x:Name="gTextBox" Grid.Row="1" Grid.Column="3"/> <TextBlock Style="{StaticResource valueTextBlock}" x:Name="bTextBox" Grid.Row="1" Grid.Column="5"/> <TextBlock Style="{StaticResource normalTextBlock}" Grid.Row="2" Grid.Column="0" Text="H" VerticalAlignment="Center" HorizontalAlignment="Center"/> <TextBlock Style="{StaticResource normalTextBlock}" Grid.Row="2" Grid.Column="2" Text="S" VerticalAlignment="Center" HorizontalAlignment="Center"/> <TextBlock Style="{StaticResource normalTextBlock}" Grid.Row="2" Grid.Column="4" Text="V" VerticalAlignment="Center" HorizontalAlignment="Center"/> <TextBlock Style="{StaticResource valueTextBlock}" x:Name="hTextBox" Grid.Row="2" Grid.Column="1"/> <TextBlock Style="{StaticResource valueTextBlock}" x:Name="sTextBox" Grid.Row="2" Grid.Column="3"/> <TextBlock Style="{StaticResource valueTextBlock}" x:Name="vTextBox" Grid.Row="2" Grid.Column="5"/> <StackPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="6" Orientation="Horizontal"> <TextBlock Style="{StaticResource normalTextBlock}" Text="Grayscale " VerticalAlignment="Center"/> <TextBlock Style="{StaticResource valueTextBlock}" x:Name="grayTextBox"/> </StackPanel> </Grid> <!-- 色情報表示部(色) --> <Grid Grid.Column="1" Margin="3"> <Rectangle x:Name="colorRectangle" Fill="#FF123456" RadiusX="16" RadiusY="16"/> <Ellipse x:Name="brushEllipse" Stroke="Black"/> </Grid> <!-- サーチ直径調整部 --> <Grid Grid.Column="2" Margin="3"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal" Grid.Row="0"> <TextBlock Style="{StaticResource normalTextBlock}" Text="サーチ直径 "/> <TextBlock Style="{StaticResource normalTextBlock}" x:Name="diamTextBlock" Text="diam px"/> </StackPanel> <Slider x:Name="diamSlider" Grid.Row="2" Minimum="1" Maximum="32" IsSnapToTickEnabled="True" TickFrequency="1" LargeChange="8" ValueChanged="diamSlider_ValueChanged" MouseWheel="diamSlider_MouseWheel" Value="1"/> </Grid> </Grid> <!-- 下段 --> <Grid Grid.Row="1" Margin="3" Width="150" HorizontalAlignment="Left"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="Hの値域" Style="{StaticResource normalTextBlock}" FontSize="9" VerticalAlignment="Center"/> <TextBlock Grid.Row="1" Grid.Column="0" Text="Sの値域" Style="{StaticResource normalTextBlock}" FontSize="9" VerticalAlignment="Center"/> <TextBlock Grid.Row="2" Grid.Column="0" Text="Vの値域" Style="{StaticResource normalTextBlock}" FontSize="9" VerticalAlignment="Center"/> <RadioButton x:Name="h360RadioButton" Grid.Row="0" Grid.Column="1" Content="0-360" GroupName="groupH" IsChecked="True"/> <RadioButton x:Name="h255RadioButton" Grid.Row="0" Grid.Column="2" Content="0-255" GroupName="groupH"/> <RadioButton x:Name="s255RadioButton" Grid.Row="1" Grid.Column="1" Content="0-255" GroupName="groupS" IsChecked="True"/> <RadioButton x:Name="s100RadioButton" Grid.Row="1" Grid.Column="2" Content="0-100" GroupName="groupS"/> <RadioButton x:Name="v255RadioButton" Grid.Row="2" Grid.Column="1" Content="0-255" GroupName="groupV" IsChecked="True"/> <RadioButton x:Name="v100RadioButton" Grid.Row="2" Grid.Column="2" Content="0-100" GroupName="groupV"/> </Grid> <!-- 終了ボタン --> <!-- マウスオーバー時透明 参考 http://gootara.org/library/2016/07/wpf-hvr.html --> <Button x:Name="closeButton" Grid.Row="2" HorizontalAlignment="Right" Width="50" Margin="3" Cursor="Hand" Click="closeButton_Click" Style="{StaticResource HoverButtonStyle}"> <Image Source="Resource/close_icon.png" RenderOptions.BitmapScalingMode="HighQuality"> <Image.Effect> <DropShadowEffect BlurRadius="20" RenderingBias="Quality" ShadowDepth="0"/> </Image.Effect> </Image> <ToolTipService.ToolTip> <ToolTip Content="カラーピッカーを終了します"/> </ToolTipService.ToolTip> </Button> </Grid> </Grid> </Window>下载
我将源代码和 exe(软件主体)上传到 GitHub。
- GitHubhttps://github.com/ika-kk/ColorPickerPj
- exe下载链接https://github.com/ika-kk/ColorPickerPj/raw/master/ColorPickerPj/bin/Release/ColorPickerPj.exe
我只在我自己的环境中调试过它,但由于它是本机实现的,它应该在 Windows 10 或更高版本上运行良好。
但是,由于我没有设置任何东西就写了,我认为会出现警告,但可以通过以下方法执行。
当然,我没有编写任何恶意代码,但请您自担风险。
(有人请告诉我如何进行数字签名......)综上所述
其实功能多点可能更好,但我决定做一些简单的事情,所以我放弃了各种各样的东西。
- 颜色代码复制功能...是否可以使用快捷方式?
- 调色板建议功能...建议具有相同饱和度的颜色
- XY 坐标显示...有时我想要坐标值。这可以实施吗?
结果,我认为创建了一个相当易于使用的颜色选择器,
我能够实现我想要轻松查看大部分颜色信息的最初目的,所以我对此很满意。
如果您不介意,请使用它。就这样
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308623819.html