diff --git "a/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.Designer.cs" "b/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.Designer.cs"
index 9a8cfe5..cae129c 100644
--- "a/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.Designer.cs"
+++ "b/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.Designer.cs"
@@ -20,384 +20,387 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- #region Windows Form Designer generated code
+ #region Windows Form Designer generated code
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
- components = new System.ComponentModel.Container();
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
- label1 = new Label();
- txtDirectory = new TextBox();
- btnDirectory = new Button();
- btnPic = new Button();
- txtPic = new TextBox();
- label2 = new Label();
- btnSearch = new Button();
- btnIndex = new Button();
- picSource = new PictureBox();
- picDest = new PictureBox();
- label3 = new Label();
- dgvResult = new DataGridView();
- dgvContextMenuStrip = new ContextMenuStrip(components);
- 打开所在文件夹 = new ToolStripMenuItem();
- label4 = new Label();
- lbIndexCount = new Label();
- label5 = new Label();
- lbElpased = new Label();
- lblDestInfo = new Label();
- lbSrcInfo = new Label();
- lbSpeed = new Label();
- numLike = new NumericUpDown();
- cbRotate = new CheckBox();
- cbFlip = new CheckBox();
- label6 = new Label();
- lblProcess = new Label();
- cbRemoveInvalidIndex = new CheckBox();
- lblGithub = new LinkLabel();
- buttonClipSearch = new Button();
- ((System.ComponentModel.ISupportInitialize)picSource).BeginInit();
- ((System.ComponentModel.ISupportInitialize)picDest).BeginInit();
- ((System.ComponentModel.ISupportInitialize)dgvResult).BeginInit();
- dgvContextMenuStrip.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)numLike).BeginInit();
- SuspendLayout();
- //
- // label1
- //
- label1.AutoSize = true;
- label1.Location = new Point(12, 9);
- label1.Name = "label1";
- label1.Size = new Size(95, 17);
- label1.TabIndex = 0;
- label1.Text = "添加搜索文件夹:";
- //
- // txtDirectory
- //
- txtDirectory.AllowDrop = true;
- txtDirectory.Location = new Point(113, 6);
- txtDirectory.Name = "txtDirectory";
- txtDirectory.Size = new Size(504, 23);
- txtDirectory.TabIndex = 1;
- txtDirectory.DragDrop += txtDirectory_DragDrop;
- txtDirectory.DragEnter += txtDirectory_DragEnter;
- //
- // btnDirectory
- //
- btnDirectory.Location = new Point(620, 6);
- btnDirectory.Name = "btnDirectory";
- btnDirectory.Size = new Size(64, 23);
- btnDirectory.TabIndex = 2;
- btnDirectory.Text = "选择";
- btnDirectory.UseVisualStyleBackColor = true;
- btnDirectory.Click += btnDirectory_Click;
- //
- // btnPic
- //
- btnPic.Location = new Point(620, 39);
- btnPic.Name = "btnPic";
- btnPic.Size = new Size(64, 23);
- btnPic.TabIndex = 5;
- btnPic.Text = "选择";
- btnPic.UseVisualStyleBackColor = true;
- btnPic.Click += btnPic_Click;
- //
- // txtPic
- //
- txtPic.AllowDrop = true;
- txtPic.Location = new Point(113, 39);
- txtPic.Name = "txtPic";
- txtPic.Size = new Size(504, 23);
- txtPic.TabIndex = 0;
- txtPic.DragDrop += txtPic_DragDrop;
- txtPic.DragEnter += txtPic_DragEnter;
- //
- // label2
- //
- label2.AutoSize = true;
- label2.Location = new Point(13, 42);
- label2.Name = "label2";
- label2.Size = new Size(95, 17);
- label2.TabIndex = 3;
- label2.Text = "用于检索的图片:";
- //
- // btnSearch
- //
- btnSearch.Location = new Point(371, 66);
- btnSearch.Name = "btnSearch";
- btnSearch.Size = new Size(51, 23);
- btnSearch.TabIndex = 6;
- btnSearch.Text = "搜索";
- btnSearch.UseVisualStyleBackColor = true;
- btnSearch.Click += btnSearch_Click;
- //
- // btnIndex
- //
- btnIndex.Location = new Point(690, 6);
- btnIndex.Name = "btnIndex";
- btnIndex.Size = new Size(75, 23);
- btnIndex.TabIndex = 7;
- btnIndex.Text = "更新索引";
- btnIndex.UseVisualStyleBackColor = true;
- btnIndex.Click += btnIndex_Click;
- //
- // picSource
- //
- picSource.Location = new Point(618, 93);
- picSource.Name = "picSource";
- picSource.Size = new Size(272, 167);
- picSource.SizeMode = PictureBoxSizeMode.Zoom;
- picSource.TabIndex = 9;
- picSource.TabStop = false;
- picSource.LoadCompleted += picSource_LoadCompleted;
- picSource.DoubleClick += picSource_Click;
- //
- // picDest
- //
- picDest.Location = new Point(618, 288);
- picDest.Name = "picDest";
- picDest.Size = new Size(272, 189);
- picDest.SizeMode = PictureBoxSizeMode.Zoom;
- picDest.TabIndex = 10;
- picDest.TabStop = false;
- picDest.LoadCompleted += picDest_LoadCompleted;
- picDest.DoubleClick += picDest_Click;
- //
- // label3
- //
- label3.AutoSize = true;
- label3.Location = new Point(31, 70);
- label3.Name = "label3";
- label3.Size = new Size(80, 17);
- label3.TabIndex = 11;
- label3.Text = "查找相似度:";
- //
- // dgvResult
- //
- dgvResult.AllowUserToAddRows = false;
- dgvResult.AllowUserToDeleteRows = false;
- dgvResult.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
- dgvResult.BackgroundColor = SystemColors.Control;
- dgvResult.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
- dgvResult.Location = new Point(12, 93);
- dgvResult.Name = "dgvResult";
- dgvResult.ReadOnly = true;
- dgvResult.Size = new Size(605, 386);
- dgvResult.TabIndex = 13;
- dgvResult.CellClick += dgvResult_CellClick;
- dgvResult.CellContentClick += dgvResult_CellContentClick;
- dgvResult.CellDoubleClick += dgvResult_CellDoubleClick;
- dgvResult.CellMouseDown += dgvResult_CellMouseDown;
- dgvResult.KeyDown += dgvResult_KeyDown;
- dgvResult.KeyUp += dgvResult_KeyUp;
- //
- // dgvContextMenuStrip
- //
- dgvContextMenuStrip.Items.AddRange(new ToolStripItem[] { 打开所在文件夹 });
- dgvContextMenuStrip.Name = "dgvContextMenuStrip";
- dgvContextMenuStrip.Size = new Size(161, 26);
- //
- // 打开所在文件夹
- //
- 打开所在文件夹.Name = "打开所在文件夹";
- 打开所在文件夹.Size = new Size(160, 22);
- 打开所在文件夹.Text = "打开所在文件夹";
- 打开所在文件夹.Click += 打开所在文件夹_Click;
- //
- // label4
- //
- label4.AutoSize = true;
- label4.Location = new Point(697, 42);
- label4.Name = "label4";
- label4.Size = new Size(80, 17);
- label4.TabIndex = 14;
- label4.Text = "索引总数量:";
- //
- // lbIndexCount
- //
- lbIndexCount.AutoSize = true;
- lbIndexCount.Location = new Point(782, 42);
- lbIndexCount.Name = "lbIndexCount";
- lbIndexCount.Size = new Size(0, 17);
- lbIndexCount.TabIndex = 15;
- //
- // label5
- //
- label5.AutoSize = true;
- label5.Location = new Point(529, 70);
- label5.Name = "label5";
- label5.Size = new Size(68, 17);
- label5.TabIndex = 16;
- label5.Text = "搜索耗时:";
- //
- // lbElpased
- //
- lbElpased.AutoSize = true;
- lbElpased.Location = new Point(603, 69);
- lbElpased.Name = "lbElpased";
- lbElpased.Size = new Size(0, 17);
- lbElpased.TabIndex = 17;
- //
- // lblDestInfo
- //
- lblDestInfo.AutoSize = true;
- lblDestInfo.Location = new Point(627, 460);
- lblDestInfo.Name = "lblDestInfo";
- lblDestInfo.Size = new Size(0, 17);
- lblDestInfo.TabIndex = 18;
- //
- // lbSrcInfo
- //
- lbSrcInfo.AutoSize = true;
- lbSrcInfo.Location = new Point(626, 243);
- lbSrcInfo.Name = "lbSrcInfo";
- lbSrcInfo.Size = new Size(0, 17);
- lbSrcInfo.TabIndex = 19;
- //
- // lbSpeed
- //
- lbSpeed.AutoSize = true;
- lbSpeed.Location = new Point(697, 67);
- lbSpeed.Name = "lbSpeed";
- lbSpeed.Size = new Size(16, 17);
- lbSpeed.TabIndex = 20;
- lbSpeed.Text = " ";
- //
- // numLike
- //
- numLike.Location = new Point(114, 67);
- numLike.Minimum = new decimal(new int[] { 70, 0, 0, 0 });
- numLike.Name = "numLike";
- numLike.Size = new Size(45, 23);
- numLike.TabIndex = 21;
- numLike.Value = new decimal(new int[] { 80, 0, 0, 0 });
- //
- // cbRotate
- //
- cbRotate.AutoSize = true;
- cbRotate.Checked = true;
- cbRotate.CheckState = CheckState.Checked;
- cbRotate.Location = new Point(171, 69);
- cbRotate.Name = "cbRotate";
- cbRotate.Size = new Size(87, 21);
- cbRotate.TabIndex = 22;
- cbRotate.Text = "查找已旋转";
- cbRotate.UseVisualStyleBackColor = true;
- //
- // cbFlip
- //
- cbFlip.AutoSize = true;
- cbFlip.Location = new Point(275, 69);
- cbFlip.Name = "cbFlip";
- cbFlip.Size = new Size(87, 21);
- cbFlip.TabIndex = 23;
- cbFlip.Text = "查找已翻转";
- cbFlip.UseVisualStyleBackColor = true;
- //
- // label6
- //
- label6.AutoSize = true;
- label6.Location = new Point(31, 484);
- label6.Name = "label6";
- label6.Size = new Size(68, 17);
- label6.TabIndex = 25;
- label6.Text = "项目地址:";
- //
- // lblProcess
- //
- lblProcess.AutoSize = true;
- lblProcess.Location = new Point(793, 8);
- lblProcess.Name = "lblProcess";
- lblProcess.Size = new Size(12, 17);
- lblProcess.TabIndex = 27;
- lblProcess.Text = " ";
- //
- // cbRemoveInvalidIndex
- //
- cbRemoveInvalidIndex.AutoSize = true;
- cbRemoveInvalidIndex.Location = new Point(770, 9);
- cbRemoveInvalidIndex.Name = "cbRemoveInvalidIndex";
- cbRemoveInvalidIndex.Size = new Size(99, 21);
- cbRemoveInvalidIndex.TabIndex = 28;
- cbRemoveInvalidIndex.Text = "移除无效索引";
- cbRemoveInvalidIndex.UseVisualStyleBackColor = true;
- //
- // lblGithub
- //
- lblGithub.AutoSize = true;
- lblGithub.Location = new Point(93, 484);
- lblGithub.Name = "lblGithub";
- lblGithub.Size = new Size(227, 17);
- lblGithub.TabIndex = 29;
- lblGithub.TabStop = true;
- lblGithub.Text = "https://github.com/ldqk/ImageSearch";
- lblGithub.LinkClicked += lblGithub_LinkClicked;
- //
- // buttonClipSearch
- //
- buttonClipSearch.Location = new Point(427, 66);
- buttonClipSearch.Name = "buttonClipSearch";
- buttonClipSearch.Size = new Size(96, 23);
- buttonClipSearch.TabIndex = 30;
- buttonClipSearch.Text = "从剪切板搜索";
- buttonClipSearch.UseVisualStyleBackColor = true;
- buttonClipSearch.Click += buttonClipSearch_Click;
- //
- // Form1
- //
- AutoScaleDimensions = new SizeF(7F, 17F);
- AutoScaleMode = AutoScaleMode.Font;
- ClientSize = new Size(902, 506);
- Controls.Add(buttonClipSearch);
- Controls.Add(lblGithub);
- Controls.Add(cbRemoveInvalidIndex);
- Controls.Add(lblProcess);
- Controls.Add(label6);
- Controls.Add(cbFlip);
- Controls.Add(cbRotate);
- Controls.Add(numLike);
- Controls.Add(lbSpeed);
- Controls.Add(lbSrcInfo);
- Controls.Add(lblDestInfo);
- Controls.Add(lbElpased);
- Controls.Add(label5);
- Controls.Add(lbIndexCount);
- Controls.Add(label4);
- Controls.Add(dgvResult);
- Controls.Add(label3);
- Controls.Add(picDest);
- Controls.Add(picSource);
- Controls.Add(btnIndex);
- Controls.Add(btnSearch);
- Controls.Add(btnPic);
- Controls.Add(txtPic);
- Controls.Add(label2);
- Controls.Add(btnDirectory);
- Controls.Add(txtDirectory);
- Controls.Add(label1);
- FormBorderStyle = FormBorderStyle.FixedSingle;
- Icon = (Icon)resources.GetObject("$this.Icon");
- MaximizeBox = false;
- Name = "Form1";
- StartPosition = FormStartPosition.CenterScreen;
- Text = "本地以图搜图小工具 by 懒得勤快 (评估版本)";
- FormClosing += Form1_FormClosing;
- Load += Form1_Load;
- ((System.ComponentModel.ISupportInitialize)picSource).EndInit();
- ((System.ComponentModel.ISupportInitialize)picDest).EndInit();
- ((System.ComponentModel.ISupportInitialize)dgvResult).EndInit();
- dgvContextMenuStrip.ResumeLayout(false);
- ((System.ComponentModel.ISupportInitialize)numLike).EndInit();
- ResumeLayout(false);
- PerformLayout();
- }
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
+ label1 = new Label();
+ txtDirectory = new TextBox();
+ btnDirectory = new Button();
+ btnPic = new Button();
+ txtPic = new TextBox();
+ label2 = new Label();
+ btnSearch = new Button();
+ btnIndex = new Button();
+ picSource = new PictureBox();
+ picDest = new PictureBox();
+ label3 = new Label();
+ dgvResult = new DataGridView();
+ dgvContextMenuStrip = new ContextMenuStrip(components);
+ 打开所在文件夹 = new ToolStripMenuItem();
+ label4 = new Label();
+ lbIndexCount = new Label();
+ label5 = new Label();
+ lbElpased = new Label();
+ lblDestInfo = new Label();
+ lbSrcInfo = new Label();
+ lbSpeed = new Label();
+ numLike = new NumericUpDown();
+ cbRotate = new CheckBox();
+ cbFlip = new CheckBox();
+ label6 = new Label();
+ lblProcess = new Label();
+ cbRemoveInvalidIndex = new CheckBox();
+ lblGithub = new LinkLabel();
+ buttonClipSearch = new Button();
+ ((System.ComponentModel.ISupportInitialize)picSource).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)picDest).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)dgvResult).BeginInit();
+ dgvContextMenuStrip.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)numLike).BeginInit();
+ SuspendLayout();
+ //
+ // label1
+ //
+ label1.AutoSize = true;
+ label1.Location = new Point(12, 9);
+ label1.Name = "label1";
+ label1.Size = new Size(95, 17);
+ label1.TabIndex = 0;
+ label1.Text = "添加搜索文件夹:";
+ //
+ // txtDirectory
+ //
+ txtDirectory.AllowDrop = true;
+ txtDirectory.Location = new Point(113, 6);
+ txtDirectory.Name = "txtDirectory";
+ txtDirectory.Size = new Size(504, 23);
+ txtDirectory.TabIndex = 1;
+ txtDirectory.DragDrop += txtDirectory_DragDrop;
+ txtDirectory.DragEnter += txtDirectory_DragEnter;
+ //
+ // btnDirectory
+ //
+ btnDirectory.Location = new Point(620, 6);
+ btnDirectory.Name = "btnDirectory";
+ btnDirectory.Size = new Size(64, 23);
+ btnDirectory.TabIndex = 2;
+ btnDirectory.Text = "选择";
+ btnDirectory.UseVisualStyleBackColor = true;
+ btnDirectory.Click += btnDirectory_Click;
+ //
+ // btnPic
+ //
+ btnPic.Location = new Point(620, 39);
+ btnPic.Name = "btnPic";
+ btnPic.Size = new Size(64, 23);
+ btnPic.TabIndex = 5;
+ btnPic.Text = "选择";
+ btnPic.UseVisualStyleBackColor = true;
+ btnPic.Click += btnPic_Click;
+ //
+ // txtPic
+ //
+ txtPic.AllowDrop = true;
+ txtPic.Location = new Point(113, 39);
+ txtPic.Name = "txtPic";
+ txtPic.Size = new Size(504, 23);
+ txtPic.TabIndex = 0;
+ txtPic.DragDrop += txtPic_DragDrop;
+ txtPic.DragEnter += txtPic_DragEnter;
+ //
+ // label2
+ //
+ label2.AutoSize = true;
+ label2.Location = new Point(13, 42);
+ label2.Name = "label2";
+ label2.Size = new Size(95, 17);
+ label2.TabIndex = 3;
+ label2.Text = "用于检索的图片:";
+ //
+ // btnSearch
+ //
+ btnSearch.Location = new Point(371, 66);
+ btnSearch.Name = "btnSearch";
+ btnSearch.Size = new Size(51, 23);
+ btnSearch.TabIndex = 6;
+ btnSearch.Text = "搜索";
+ btnSearch.UseVisualStyleBackColor = true;
+ btnSearch.Click += btnSearch_Click;
+ //
+ // btnIndex
+ //
+ btnIndex.Location = new Point(690, 6);
+ btnIndex.Name = "btnIndex";
+ btnIndex.Size = new Size(75, 23);
+ btnIndex.TabIndex = 7;
+ btnIndex.Text = "更新索引";
+ btnIndex.UseVisualStyleBackColor = true;
+ btnIndex.Click += btnIndex_Click;
+ //
+ // picSource
+ //
+ picSource.BorderStyle = BorderStyle.FixedSingle;
+ picSource.Location = new Point(618, 93);
+ picSource.Name = "picSource";
+ picSource.Size = new Size(272, 166);
+ picSource.SizeMode = PictureBoxSizeMode.Zoom;
+ picSource.TabIndex = 9;
+ picSource.TabStop = false;
+ picSource.Tag = "1";
+ picSource.LoadCompleted += picSource_LoadCompleted;
+ picSource.Click += picSource_Click;
+ picSource.DoubleClick += picSource_DoubleClick;
+ //
+ // picDest
+ //
+ picDest.Location = new Point(618, 288);
+ picDest.Name = "picDest";
+ picDest.Size = new Size(272, 191);
+ picDest.SizeMode = PictureBoxSizeMode.Zoom;
+ picDest.TabIndex = 10;
+ picDest.TabStop = false;
+ picDest.LoadCompleted += picDest_LoadCompleted;
+ picDest.DoubleClick += picDest_Click;
+ //
+ // label3
+ //
+ label3.AutoSize = true;
+ label3.Location = new Point(31, 70);
+ label3.Name = "label3";
+ label3.Size = new Size(80, 17);
+ label3.TabIndex = 11;
+ label3.Text = "查找相似度:";
+ //
+ // dgvResult
+ //
+ dgvResult.AllowUserToAddRows = false;
+ dgvResult.AllowUserToDeleteRows = false;
+ dgvResult.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
+ dgvResult.BackgroundColor = SystemColors.Control;
+ dgvResult.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ dgvResult.Location = new Point(12, 93);
+ dgvResult.Name = "dgvResult";
+ dgvResult.ReadOnly = true;
+ dgvResult.Size = new Size(605, 386);
+ dgvResult.TabIndex = 13;
+ dgvResult.CellClick += dgvResult_CellClick;
+ dgvResult.CellContentClick += dgvResult_CellContentClick;
+ dgvResult.CellDoubleClick += dgvResult_CellDoubleClick;
+ dgvResult.CellMouseDown += dgvResult_CellMouseDown;
+ dgvResult.KeyDown += dgvResult_KeyDown;
+ dgvResult.KeyUp += dgvResult_KeyUp;
+ //
+ // dgvContextMenuStrip
+ //
+ dgvContextMenuStrip.Items.AddRange(new ToolStripItem[] { 打开所在文件夹 });
+ dgvContextMenuStrip.Name = "dgvContextMenuStrip";
+ dgvContextMenuStrip.Size = new Size(161, 26);
+ //
+ // 打开所在文件夹
+ //
+ 打开所在文件夹.Name = "打开所在文件夹";
+ 打开所在文件夹.Size = new Size(160, 22);
+ 打开所在文件夹.Text = "打开所在文件夹";
+ 打开所在文件夹.Click += 打开所在文件夹_Click;
+ //
+ // label4
+ //
+ label4.AutoSize = true;
+ label4.Location = new Point(697, 42);
+ label4.Name = "label4";
+ label4.Size = new Size(80, 17);
+ label4.TabIndex = 14;
+ label4.Text = "索引总数量:";
+ //
+ // lbIndexCount
+ //
+ lbIndexCount.AutoSize = true;
+ lbIndexCount.Location = new Point(782, 42);
+ lbIndexCount.Name = "lbIndexCount";
+ lbIndexCount.Size = new Size(0, 17);
+ lbIndexCount.TabIndex = 15;
+ //
+ // label5
+ //
+ label5.AutoSize = true;
+ label5.Location = new Point(529, 70);
+ label5.Name = "label5";
+ label5.Size = new Size(68, 17);
+ label5.TabIndex = 16;
+ label5.Text = "搜索耗时:";
+ //
+ // lbElpased
+ //
+ lbElpased.AutoSize = true;
+ lbElpased.Location = new Point(603, 69);
+ lbElpased.Name = "lbElpased";
+ lbElpased.Size = new Size(0, 17);
+ lbElpased.TabIndex = 17;
+ //
+ // lblDestInfo
+ //
+ lblDestInfo.AutoSize = true;
+ lblDestInfo.Location = new Point(627, 483);
+ lblDestInfo.Name = "lblDestInfo";
+ lblDestInfo.Size = new Size(0, 17);
+ lblDestInfo.TabIndex = 18;
+ //
+ // lbSrcInfo
+ //
+ lbSrcInfo.AutoSize = true;
+ lbSrcInfo.Location = new Point(626, 262);
+ lbSrcInfo.Name = "lbSrcInfo";
+ lbSrcInfo.Size = new Size(0, 17);
+ lbSrcInfo.TabIndex = 19;
+ //
+ // lbSpeed
+ //
+ lbSpeed.AutoSize = true;
+ lbSpeed.Location = new Point(697, 67);
+ lbSpeed.Name = "lbSpeed";
+ lbSpeed.Size = new Size(16, 17);
+ lbSpeed.TabIndex = 20;
+ lbSpeed.Text = " ";
+ //
+ // numLike
+ //
+ numLike.Location = new Point(114, 67);
+ numLike.Minimum = new decimal(new int[] { 70, 0, 0, 0 });
+ numLike.Name = "numLike";
+ numLike.Size = new Size(45, 23);
+ numLike.TabIndex = 21;
+ numLike.Value = new decimal(new int[] { 80, 0, 0, 0 });
+ //
+ // cbRotate
+ //
+ cbRotate.AutoSize = true;
+ cbRotate.Checked = true;
+ cbRotate.CheckState = CheckState.Checked;
+ cbRotate.Location = new Point(171, 69);
+ cbRotate.Name = "cbRotate";
+ cbRotate.Size = new Size(87, 21);
+ cbRotate.TabIndex = 22;
+ cbRotate.Text = "查找已旋转";
+ cbRotate.UseVisualStyleBackColor = true;
+ //
+ // cbFlip
+ //
+ cbFlip.AutoSize = true;
+ cbFlip.Location = new Point(275, 69);
+ cbFlip.Name = "cbFlip";
+ cbFlip.Size = new Size(87, 21);
+ cbFlip.TabIndex = 23;
+ cbFlip.Text = "查找已翻转";
+ cbFlip.UseVisualStyleBackColor = true;
+ //
+ // label6
+ //
+ label6.AutoSize = true;
+ label6.Location = new Point(31, 484);
+ label6.Name = "label6";
+ label6.Size = new Size(68, 17);
+ label6.TabIndex = 25;
+ label6.Text = "项目地址:";
+ //
+ // lblProcess
+ //
+ lblProcess.AutoSize = true;
+ lblProcess.Location = new Point(793, 8);
+ lblProcess.Name = "lblProcess";
+ lblProcess.Size = new Size(12, 17);
+ lblProcess.TabIndex = 27;
+ lblProcess.Text = " ";
+ //
+ // cbRemoveInvalidIndex
+ //
+ cbRemoveInvalidIndex.AutoSize = true;
+ cbRemoveInvalidIndex.Location = new Point(770, 9);
+ cbRemoveInvalidIndex.Name = "cbRemoveInvalidIndex";
+ cbRemoveInvalidIndex.Size = new Size(99, 21);
+ cbRemoveInvalidIndex.TabIndex = 28;
+ cbRemoveInvalidIndex.Text = "移除无效索引";
+ cbRemoveInvalidIndex.UseVisualStyleBackColor = true;
+ //
+ // lblGithub
+ //
+ lblGithub.AutoSize = true;
+ lblGithub.Location = new Point(93, 484);
+ lblGithub.Name = "lblGithub";
+ lblGithub.Size = new Size(227, 17);
+ lblGithub.TabIndex = 29;
+ lblGithub.TabStop = true;
+ lblGithub.Text = "https://github.com/ldqk/ImageSearch";
+ lblGithub.LinkClicked += lblGithub_LinkClicked;
+ //
+ // buttonClipSearch
+ //
+ buttonClipSearch.Location = new Point(427, 66);
+ buttonClipSearch.Name = "buttonClipSearch";
+ buttonClipSearch.Size = new Size(96, 23);
+ buttonClipSearch.TabIndex = 30;
+ buttonClipSearch.Text = "从剪切板搜索";
+ buttonClipSearch.UseVisualStyleBackColor = true;
+ buttonClipSearch.Click += buttonClipSearch_Click;
+ //
+ // Form1
+ //
+ AutoScaleDimensions = new SizeF(7F, 17F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(902, 506);
+ Controls.Add(buttonClipSearch);
+ Controls.Add(lblGithub);
+ Controls.Add(cbRemoveInvalidIndex);
+ Controls.Add(lblProcess);
+ Controls.Add(label6);
+ Controls.Add(cbFlip);
+ Controls.Add(cbRotate);
+ Controls.Add(numLike);
+ Controls.Add(lbSpeed);
+ Controls.Add(lbSrcInfo);
+ Controls.Add(lblDestInfo);
+ Controls.Add(lbElpased);
+ Controls.Add(label5);
+ Controls.Add(lbIndexCount);
+ Controls.Add(label4);
+ Controls.Add(dgvResult);
+ Controls.Add(label3);
+ Controls.Add(picDest);
+ Controls.Add(picSource);
+ Controls.Add(btnIndex);
+ Controls.Add(btnSearch);
+ Controls.Add(btnPic);
+ Controls.Add(txtPic);
+ Controls.Add(label2);
+ Controls.Add(btnDirectory);
+ Controls.Add(txtDirectory);
+ Controls.Add(label1);
+ FormBorderStyle = FormBorderStyle.FixedSingle;
+ Icon = (Icon)resources.GetObject("$this.Icon");
+ MaximizeBox = false;
+ Name = "Form1";
+ StartPosition = FormStartPosition.CenterScreen;
+ Text = "本地以图搜图小工具 by 懒得勤快 (评估版本)";
+ FormClosing += Form1_FormClosing;
+ Load += Form1_Load;
+ ((System.ComponentModel.ISupportInitialize)picSource).EndInit();
+ ((System.ComponentModel.ISupportInitialize)picDest).EndInit();
+ ((System.ComponentModel.ISupportInitialize)dgvResult).EndInit();
+ dgvContextMenuStrip.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)numLike).EndInit();
+ ResumeLayout(false);
+ PerformLayout();
+ }
- #endregion
+ #endregion
- private Label label1;
+ private Label label1;
private TextBox txtDirectory;
private Button btnDirectory;
private Button btnPic;
diff --git "a/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.cs" "b/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.cs"
index e047a5e..902d887 100644
--- "a/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.cs"
+++ "b/\344\273\245\345\233\276\346\220\234\345\233\276/Form1.cs"
@@ -18,590 +18,610 @@ namespace 以图搜图;
public partial class Form1 : Form
{
- public Form1()
- {
- InitializeComponent();
- CheckForIllegalCrossThreadCalls = false;
- }
-
- private async void Form1_Load(object sender, EventArgs e)
- {
- lbIndexCount.Text = "正在加载索引...";
- if (File.Exists("index.json"))
- {
- _index = await JsonSerializer.DeserializeAsync>(File.OpenRead("index.json")).ConfigureAwait(false);
- }
- if (File.Exists("frame_index.json"))
- {
- _frameIndex = await JsonSerializer.DeserializeAsync>>(File.OpenRead("frame_index.json")).ConfigureAwait(false);
- }
-
- if (_index.Count + _frameIndex.Count > 0)
- {
- lbIndexCount.Text = _index.Count + _frameIndex.Count + "文件";
- }
- else
- {
- lbIndexCount.Text = "请先创建索引";
- }
- }
-
- private void btnDirectory_Click(object sender, EventArgs e)
- {
- using var dialog = new FolderBrowserDialog();
- var result = dialog.ShowDialog();
- if (result == DialogResult.OK)
- {
- txtDirectory.Text = dialog.SelectedPath;
- }
- }
-
- private void btnPic_Click(object sender, EventArgs e)
- {
- using var dialog = new OpenFileDialog();
- if (dialog.ShowDialog() == DialogResult.OK)
- {
- txtPic.Text = dialog.FileName;
- }
- }
-
- private ConcurrentDictionary _index = new();
- private ConcurrentDictionary> _frameIndex = new();
- private static readonly ReaderWriterLockSlim ReaderWriterLock = new();
- private bool _removingInvalidIndex;
- public bool IndexRunning { get; set; }
-
- private async void btnIndex_Click(object sender, EventArgs e)
- {
- if (IndexRunning)
- {
- IndexRunning = false;
- btnIndex.Text = "更新索引";
- return;
- }
-
- if (string.IsNullOrEmpty(txtDirectory.Text))
- {
- MessageBox.Show("请先选择文件夹");
- return;
- }
-
- if (cbRemoveInvalidIndex.Checked)
- {
- _ = Task.Run(() =>
- {
- _removingInvalidIndex = true;
- foreach (var key in _index.Keys.Except(_index.Keys.GroupBy(x => string.Join('\\', x.Split('\\')[..2])).SelectMany(g => Directory.EnumerateFiles(FindLCP(g.ToArray()), "*", SearchOption.AllDirectories).Where(s => Regex.IsMatch(s, "(jpg|jpeg|png|bmp)$", RegexOptions.IgnoreCase)))).AsParallel().WithDegreeOfParallelism(32).Where(s => !File.Exists(s)))
- {
- _index.TryRemove(key, out _);
- }
-
- foreach (var key in _frameIndex.Keys.Except(_frameIndex.Keys.GroupBy(x => string.Join('\\', x.Split('\\')[..2])).SelectMany(g => Directory.EnumerateFiles(FindLCP(g.ToArray()), "*.gif", SearchOption.AllDirectories))).AsParallel().WithDegreeOfParallelism(32).Where(s => !File.Exists(s)))
- {
- _frameIndex.TryRemove(key, out _);
- }
-
- ReaderWriterLock.EnterWriteLock();
- File.WriteAllText("index.json", JsonSerializer.Serialize(_index), Encoding.UTF8);
- File.WriteAllText("frame_index.json", JsonSerializer.Serialize(_frameIndex), Encoding.UTF8);
- ReaderWriterLock.ExitWriteLock();
- _removingInvalidIndex = false;
- lbIndexCount.Text = _index.Count + _frameIndex.Count + "文件";
- }).ConfigureAwait(false);
- }
-
- IndexRunning = true;
- btnIndex.Text = "停止索引";
- cbRemoveInvalidIndex.Hide();
- var imageHasher = new ImageHasher(new ImageSharpTransformer());
- int? filesCount = null;
- _ = Task.Run(() => filesCount = Directory.EnumerateFiles(txtDirectory.Text, "*", SearchOption.AllDirectories).Except(_index.Keys).Count(s => Regex.IsMatch(s, "(gif|jpg|jpeg|png|bmp)$", RegexOptions.IgnoreCase))).ConfigureAwait(false);
- var local = new ThreadLocal(true);
- await Task.Run(() =>
- {
- var sw = Stopwatch.StartNew();
- long size = 0;
- Directory.EnumerateFiles(txtDirectory.Text, "*", SearchOption.AllDirectories).Except(_index.Keys).Where(s => Regex.IsMatch(s, "(jpg|jpeg|png|bmp)$", RegexOptions.IgnoreCase)).Chunk(Environment.ProcessorCount * 2).AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount * 2).ForAll(g =>
- {
- foreach (var s in g)
- {
- if (IndexRunning)
- {
- if (lblProcess.InvokeRequired)
- {
- local.Value++;
- lblProcess.Invoke(() => lblProcess.Text = $"{local.Values.Sum()}/{filesCount}");
- }
- try
- {
- _index.GetOrAdd(s, _ => imageHasher.DifferenceHash256(s));
- size += new FileInfo(s).Length;
- }
- catch
- {
- LogManager.Info(s + "格式不正确");
- }
- }
- else
- {
- break;
- }
- }
- });
- Directory.EnumerateFiles(txtDirectory.Text, "*.gif", SearchOption.AllDirectories).Except(_frameIndex.Keys).Chunk(Environment.ProcessorCount * 2).AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount * 2).ForAll(g =>
- {
- foreach (var s in g)
- {
- if (IndexRunning)
- {
- if (lblProcess.InvokeRequired)
- {
- local.Value++;
- lblProcess.Invoke(() => lblProcess.Text = $"{local.Values.Sum()}/{filesCount}");
- }
- try
- {
- using var gif = Image.Load(s);
- for (var i = 0; i < gif.Frames.Count; i++)
- {
- using var frame = gif.Frames.ExportFrame(i);
- var hash = imageHasher.DifferenceHash256(frame);
- _frameIndex.GetOrAdd(s, _ => []).Add(hash);
- }
-
- size += new FileInfo(s).Length;
- }
- catch
- {
- LogManager.Info(s + "格式不正确");
- }
- }
- else
- {
- break;
- }
- }
- });
- lbSpeed.Text = $"索引速度: {Math.Round(local.Values.Sum() * 1.0 / sw.Elapsed.TotalSeconds)} items/s({size * 1f / 1048576 / sw.Elapsed.TotalSeconds:N}MB/s)";
- lbIndexCount.Text = _index.Count + _frameIndex.Count + "文件";
- cbRemoveInvalidIndex.Show();
- ReaderWriterLock.EnterWriteLock();
- File.WriteAllText("index.json", JsonSerializer.Serialize(_index), Encoding.UTF8);
- File.WriteAllText("frame_index.json", JsonSerializer.Serialize(_frameIndex), Encoding.UTF8);
- ReaderWriterLock.ExitWriteLock();
- MessageBox.Show("索引创建完成,耗时:" + sw.Elapsed.TotalSeconds + "s");
- }).ConfigureAwait(false);
- IndexRunning = false;
- btnIndex.Text = "更新索引";
- }
-
- private static string FindLCP(string[] strs)
- {
- if (strs == null || strs.Length == 0) return "";
-
- // 找到最短的字符串,因为LCP不会超过最短字符串的长度
- string shortest = strs[0];
- for (int i = 1; i < strs.Length; i++)
- {
- if (strs[i].Length < shortest.Length)
- shortest = strs[i];
- }
-
- // 逐字符比较,直到找到不匹配的字符
- for (int i = 0; i < shortest.Length; i++)
- {
- char c = shortest[i];
- for (int j = 1; j < strs.Length; j++)
- {
- // 如果当前索引越界或字符不匹配,则返回当前LCP
- if (i >= strs[j].Length || strs[j][i] != c)
- return shortest.Substring(0, i);
- }
- }
-
- // 如果所有字符串共享整个最短字符串,则返回它
- return shortest;
- }
-
- private void btnSearch_Click(object sender, EventArgs e)
- {
- if (string.IsNullOrEmpty(txtPic.Text))
- {
- MessageBox.Show("请先选择图片");
- return;
- }
-
- if (_index.Count == 0)
- {
- MessageBox.Show("当前没有任何索引,请先添加文件夹创建索引后再搜索");
- return;
- }
-
- if (!new FileInfo(txtPic.Text).DetectFiletype().MimeType.StartsWith("image"))
- {
- MessageBox.Show("不是图像文件,无法检索");
- return;
- }
-
- var sim = (float)numLike.Value / 100;
- var hasher = new ImageHasher();
- var sw = Stopwatch.StartNew();
- var hashs = new ConcurrentBag();
- var actions = new List();
-
- if (txtPic.Text.EndsWith("gif"))
- {
- using (var gif = Image.Load(txtPic.Text))
- {
- for (var i = 0; i < gif.Frames.Count; i++)
- {
- var frame = gif.Frames.ExportFrame(i);
- actions.Add(() =>
- {
- hashs.Add(frame.DifferenceHash256());
- frame.Dispose();
- });
- }
- Parallel.Invoke(actions.ToArray());
- }
- }
- else
- {
- hashs.Add(hasher.DifferenceHash256(txtPic.Text));
- using (var image = Image.Load(txtPic.Text))
- {
- if (cbRotate.Checked)
- {
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Rotate(90));
- var rotate90 = clone.DifferenceHash256();
- hashs.Add(rotate90);
- });
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Rotate(180));
- var rotate180 = clone.DifferenceHash256();
- hashs.Add(rotate180);
- });
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Rotate(270));
- var rotate270 = clone.DifferenceHash256();
- hashs.Add(rotate270);
- });
- }
-
- if (cbFlip.Checked)
- {
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Flip(FlipMode.Horizontal));
- var flipH = clone.DifferenceHash256();
- hashs.Add(flipH);
- });
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Flip(FlipMode.Vertical));
- var flipV = clone.DifferenceHash256();
- hashs.Add(flipV);
- });
- }
- Parallel.Invoke(actions.ToArray());
- }
- }
-
- var list = new List();
- if (txtPic.Text.EndsWith("gif"))
- {
- list.AddRange(_frameIndex.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult
- {
- 路径 = x.Key,
- 匹配度 = x.Value.SelectMany(h => hashs.Select(hh => ImageHasher.Compare(h, hh))).Where(f => f >= sim).OrderDescending().Take(10).DefaultIfEmpty().Average()
- }).Where(x => x.匹配度 >= sim));
- }
- else
- {
- list.AddRange(_frameIndex.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult
- {
- 路径 = x.Key,
- 匹配度 = x.Value.SelectMany(h => hashs.Select(hh => ImageHasher.Compare(h, hh))).Max()
- }).Where(x => x.匹配度 >= sim));
- list.AddRange(_index.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult
- {
- 路径 = x.Key,
- 匹配度 = hashs.Select(h => ImageHasher.Compare(x.Value, h)).Max()
- }).Where(x => x.匹配度 >= sim));
- }
-
- list = list.OrderByDescending(a => a.匹配度).ToList();
- lbElpased.Text = sw.ElapsedMilliseconds + "ms";
- if (list.Count > 0)
- {
- picSource.ImageLocation = txtPic.Text;
- picSource.Refresh();
- }
-
- dgvResult.DataSource = list;
- dgvResult.Focus();
- }
-
- private void dgvResult_CellContentClick(object sender, DataGridViewCellEventArgs e)
- {
- }
-
- private void dgvResult_CellClick(object sender, DataGridViewCellEventArgs e)
- {
- picDest.ImageLocation = dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString();
- }
-
- private void picSource_LoadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
- {
- lbSrcInfo.Text = $"分辨率:{picSource.Image.Width}x{picSource.Image.Height},大小:{new FileInfo(picSource.ImageLocation).Length / 1024}KB";
- }
-
- private void picDest_LoadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
- {
- lblDestInfo.Text = $"分辨率:{picDest.Image.Width}x{picDest.Image.Height},大小:{new FileInfo(picDest.ImageLocation).Length / 1024}KB";
- }
-
- private void lblGithub_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
- {
- Process.Start(new ProcessStartInfo { FileName = lblGithub.Text, UseShellExecute = true });
- }
-
- private void txtDirectory_DragEnter(object sender, DragEventArgs e)
- {
- e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Link : DragDropEffects.None;
- }
-
- private void txtDirectory_DragDrop(object sender, DragEventArgs e)
- {
- ((TextBox)sender).Text = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
- }
-
- private void txtPic_DragEnter(object sender, DragEventArgs e)
- {
- e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Link : DragDropEffects.None;
- }
-
- private void txtPic_DragDrop(object sender, DragEventArgs e)
- {
- ((TextBox)sender).Text = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
- }
-
- private void buttonClipSearch_Click(object sender, EventArgs e)
- {
- if (Clipboard.ContainsFileDropList())
- {
- txtPic.Text = Clipboard.GetFileDropList()[0];
- btnSearch_Click(sender, e);
- return;
- }
-
- if (Clipboard.ContainsText())
- {
- var text = Clipboard.GetText();
- if (File.Exists(text))
- {
- btnSearch_Click(sender, e);
- }
- else
- {
- dgvResult.DataSource = null;
- picSource.Image = null;
- picSource.Refresh();
- }
- return;
- }
-
- if (Clipboard.ContainsImage())
- {
- using var sourceImage = Clipboard.GetImage();
- var filename = Path.Combine(Environment.GetEnvironmentVariable("temp"), SnowFlake.NewId);
- sourceImage.Save(filename, ImageFormat.Jpeg);
- var sim = (float)numLike.Value / 100;
- var hasher = new ImageHasher();
- var sw = Stopwatch.StartNew();
- var hash = hasher.DifferenceHash256(filename);
- var hashs = new ConcurrentBag { hash };
- using (var image = Image.Load(filename))
- {
- var actions = new List();
- if (cbRotate.Checked)
- {
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Rotate(90));
- var rotate90 = clone.DifferenceHash256();
- hashs.Add(rotate90);
- });
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Rotate(180));
- var rotate180 = clone.DifferenceHash256();
- hashs.Add(rotate180);
- });
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Rotate(270));
- var rotate270 = clone.DifferenceHash256();
- hashs.Add(rotate270);
- });
- }
-
- if (cbFlip.Checked)
- {
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Flip(FlipMode.Horizontal));
- var flipH = clone.DifferenceHash256();
- hashs.Add(flipH);
- });
- actions.Add(() =>
- {
- using var clone = image.Clone(c => c.Flip(FlipMode.Vertical));
- var flipV = clone.DifferenceHash256();
- hashs.Add(flipV);
- });
- }
- Parallel.Invoke(actions.ToArray());
- }
-
- var list = _frameIndex.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult()
- {
- 路径 = x.Key,
- 匹配度 = x.Value.SelectMany(h => hashs.Select(hh => ImageHasher.Compare(h, hh))).Max()
- }).Where(x => x.匹配度 >= sim).ToList();
- list.AddRange(_index.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult()
- {
- 路径 = x.Key,
- 匹配度 = hashs.Select(h => ImageHasher.Compare(x.Value, h)).Max()
- }).Where(x => x.匹配度 >= sim));
- list = list.OrderByDescending(a => a.匹配度).ToList();
- lbElpased.Text = sw.ElapsedMilliseconds + "ms";
- if (list.Count > 0)
- {
- picSource.ImageLocation = filename;
- picSource.Refresh();
- }
-
- dgvResult.DataSource = list;
- Task.Run(() =>
- {
- Thread.Sleep(1000);
- File.Delete(filename);
- }).ContinueWith(_ => 0).ConfigureAwait(false);
- }
- }
-
- private void dgvResult_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
- {
- if (e.Button == MouseButtons.Right && dgvResult.SelectedCells.Count > 0)
- {
- dgvContextMenuStrip.Show(MousePosition.X, MousePosition.Y);
- return;
- }
-
- dgvContextMenuStrip.Hide();
- }
-
- private void 打开所在文件夹_Click(object sender, EventArgs e)
- {
- ExplorerFile(dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString());
- }
-
- private void dgvResult_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
- {
- Process.Start(new ProcessStartInfo { FileName = dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString(), UseShellExecute = true });
- }
-
- ///
- /// 打开路径并定位文件...对于@"h:\Bleacher Report - Hardaway with the safe call ??.mp4"这样的,explorer.exe /select,d:xxx不认,用API整它
- ///
- [DllImport("shell32.dll", ExactSpelling = true)]
- private static extern void ILFree(IntPtr pidlList);
-
- [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
- private static extern IntPtr ILCreateFromPathW(string pszPath);
-
- [DllImport("shell32.dll", ExactSpelling = true)]
- private static extern int SHOpenFolderAndSelectItems(IntPtr pidlList, uint cild, IntPtr children, uint dwFlags);
-
- public void ExplorerFile(string filePath)
- {
- if (!File.Exists(filePath))
- {
- return;
- }
-
- var pidlList = ILCreateFromPathW(filePath);
- if (pidlList != IntPtr.Zero)
- {
- try
- {
- Marshal.ThrowExceptionForHR(SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0));
- }
- catch
- {
- }
- finally
- {
- ILFree(pidlList);
- }
- }
- }
-
- protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
- {
- if (keyData == (Keys.Control | Keys.V))
- {
- buttonClipSearch_Click(null, null);
- }
- return base.ProcessCmdKey(ref msg, keyData);
- }
-
- private void dgvResult_KeyUp(object sender, KeyEventArgs e)
- {
- }
-
- private void dgvResult_KeyDown(object sender, KeyEventArgs e)
- {
- if (e.KeyCode is Keys.Up or Keys.Down)
- {
- picDest.ImageLocation = dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString();
- }
- }
-
- private void picSource_Click(object sender, EventArgs e)
- {
- Process.Start(new ProcessStartInfo { FileName = picSource.ImageLocation, UseShellExecute = true });
- }
-
- private void picDest_Click(object sender, EventArgs e)
- {
- Process.Start(new ProcessStartInfo { FileName = picDest.ImageLocation, UseShellExecute = true });
- }
-
- private void Form1_FormClosing(object sender, FormClosingEventArgs e)
- {
- if (IndexRunning)
- {
- MessageBox.Show("正在索引文件,关闭程序前请先取消");
- e.Cancel = true;
- return;
- }
-
- if (_removingInvalidIndex)
- {
- MessageBox.Show("正在移除无效的索引文件,请稍后再试");
- e.Cancel = true;
- }
- }
+ public Form1()
+ {
+ InitializeComponent();
+ CheckForIllegalCrossThreadCalls = false;
+ }
+
+ private async void Form1_Load(object sender, EventArgs e)
+ {
+ lbIndexCount.Text = "正在加载索引...";
+ if (File.Exists("index.json"))
+ {
+ _index = await JsonSerializer.DeserializeAsync>(File.OpenRead("index.json")).ConfigureAwait(false);
+ }
+ if (File.Exists("frame_index.json"))
+ {
+ _frameIndex = await JsonSerializer.DeserializeAsync>>(File.OpenRead("frame_index.json")).ConfigureAwait(false);
+ }
+
+ if (_index.Count + _frameIndex.Count > 0)
+ {
+ lbIndexCount.Text = _index.Count + _frameIndex.Count + "文件";
+ }
+ else
+ {
+ lbIndexCount.Text = "请先创建索引";
+ }
+
+ var bitmap = new Bitmap(picSource.Width, picSource.Height);
+ using var graphics = Graphics.FromImage(bitmap);
+ string text = "单击这里选择需要检索的图片";
+ Font font = new Font("微软雅黑LightUI", 9);
+ Brush brush = new SolidBrush(Color.Black);
+ graphics.DrawString(text, font, brush, new PointF(10, 10));
+ picSource.Image = bitmap;
+ }
+
+ private void btnDirectory_Click(object sender, EventArgs e)
+ {
+ using var dialog = new FolderBrowserDialog();
+ var result = dialog.ShowDialog();
+ if (result == DialogResult.OK)
+ {
+ txtDirectory.Text = dialog.SelectedPath;
+ }
+ }
+
+ private void btnPic_Click(object sender, EventArgs e)
+ {
+ using var dialog = new OpenFileDialog();
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ txtPic.Text = dialog.FileName;
+ picSource.ImageLocation = txtPic.Text;
+ picSource.CreateGraphics().Clear(Color.White);
+ }
+ }
+
+ private ConcurrentDictionary _index = new();
+ private ConcurrentDictionary> _frameIndex = new();
+ private static readonly ReaderWriterLockSlim ReaderWriterLock = new();
+ private bool _removingInvalidIndex;
+ public bool IndexRunning { get; set; }
+
+ private async void btnIndex_Click(object sender, EventArgs e)
+ {
+ if (IndexRunning)
+ {
+ IndexRunning = false;
+ btnIndex.Text = "更新索引";
+ return;
+ }
+
+ if (string.IsNullOrEmpty(txtDirectory.Text))
+ {
+ MessageBox.Show("请先选择文件夹");
+ return;
+ }
+
+ if (cbRemoveInvalidIndex.Checked)
+ {
+ _ = Task.Run(() =>
+ {
+ _removingInvalidIndex = true;
+ foreach (var key in _index.Keys.Except(_index.Keys.GroupBy(x => string.Join('\\', x.Split('\\')[..2])).SelectMany(g => Directory.EnumerateFiles(FindLCP(g.ToArray()), "*", SearchOption.AllDirectories).Where(s => Regex.IsMatch(s, "(jpg|jpeg|png|bmp)$", RegexOptions.IgnoreCase)))).AsParallel().WithDegreeOfParallelism(32).Where(s => !File.Exists(s)))
+ {
+ _index.TryRemove(key, out _);
+ }
+
+ foreach (var key in _frameIndex.Keys.Except(_frameIndex.Keys.GroupBy(x => string.Join('\\', x.Split('\\')[..2])).SelectMany(g => Directory.EnumerateFiles(FindLCP(g.ToArray()), "*.gif", SearchOption.AllDirectories))).AsParallel().WithDegreeOfParallelism(32).Where(s => !File.Exists(s)))
+ {
+ _frameIndex.TryRemove(key, out _);
+ }
+
+ ReaderWriterLock.EnterWriteLock();
+ File.WriteAllText("index.json", JsonSerializer.Serialize(_index), Encoding.UTF8);
+ File.WriteAllText("frame_index.json", JsonSerializer.Serialize(_frameIndex), Encoding.UTF8);
+ ReaderWriterLock.ExitWriteLock();
+ _removingInvalidIndex = false;
+ lbIndexCount.Text = _index.Count + _frameIndex.Count + "文件";
+ }).ConfigureAwait(false);
+ }
+
+ IndexRunning = true;
+ btnIndex.Text = "停止索引";
+ cbRemoveInvalidIndex.Hide();
+ var imageHasher = new ImageHasher(new ImageSharpTransformer());
+ int? filesCount = null;
+ _ = Task.Run(() => filesCount = Directory.EnumerateFiles(txtDirectory.Text, "*", SearchOption.AllDirectories).Except(_index.Keys).Count(s => Regex.IsMatch(s, "(gif|jpg|jpeg|png|bmp)$", RegexOptions.IgnoreCase))).ConfigureAwait(false);
+ var local = new ThreadLocal(true);
+ await Task.Run(() =>
+ {
+ var sw = Stopwatch.StartNew();
+ long size = 0;
+ Directory.EnumerateFiles(txtDirectory.Text, "*", SearchOption.AllDirectories).Except(_index.Keys).Where(s => Regex.IsMatch(s, "(jpg|jpeg|png|bmp)$", RegexOptions.IgnoreCase)).Chunk(Environment.ProcessorCount * 2).AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount * 2).ForAll(g =>
+ {
+ foreach (var s in g)
+ {
+ if (IndexRunning)
+ {
+ if (lblProcess.InvokeRequired)
+ {
+ local.Value++;
+ lblProcess.Invoke(() => lblProcess.Text = $"{local.Values.Sum()}/{filesCount}");
+ }
+ try
+ {
+ _index.GetOrAdd(s, _ => imageHasher.DifferenceHash256(s));
+ size += new FileInfo(s).Length;
+ }
+ catch
+ {
+ LogManager.Info(s + "格式不正确");
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ });
+ Directory.EnumerateFiles(txtDirectory.Text, "*.gif", SearchOption.AllDirectories).Except(_frameIndex.Keys).Chunk(Environment.ProcessorCount * 2).AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount * 2).ForAll(g =>
+ {
+ foreach (var s in g)
+ {
+ if (IndexRunning)
+ {
+ if (lblProcess.InvokeRequired)
+ {
+ local.Value++;
+ lblProcess.Invoke(() => lblProcess.Text = $"{local.Values.Sum()}/{filesCount}");
+ }
+ try
+ {
+ using var gif = Image.Load(s);
+ for (var i = 0; i < gif.Frames.Count; i++)
+ {
+ using var frame = gif.Frames.ExportFrame(i);
+ var hash = imageHasher.DifferenceHash256(frame);
+ _frameIndex.GetOrAdd(s, _ => []).Add(hash);
+ }
+
+ size += new FileInfo(s).Length;
+ }
+ catch
+ {
+ LogManager.Info(s + "格式不正确");
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ });
+ lbSpeed.Text = $"索引速度: {Math.Round(local.Values.Sum() * 1.0 / sw.Elapsed.TotalSeconds)} items/s({size * 1f / 1048576 / sw.Elapsed.TotalSeconds:N}MB/s)";
+ lbIndexCount.Text = _index.Count + _frameIndex.Count + "文件";
+ cbRemoveInvalidIndex.Show();
+ ReaderWriterLock.EnterWriteLock();
+ File.WriteAllText("index.json", JsonSerializer.Serialize(_index), Encoding.UTF8);
+ File.WriteAllText("frame_index.json", JsonSerializer.Serialize(_frameIndex), Encoding.UTF8);
+ ReaderWriterLock.ExitWriteLock();
+ MessageBox.Show("索引创建完成,耗时:" + sw.Elapsed.TotalSeconds + "s");
+ }).ConfigureAwait(false);
+ IndexRunning = false;
+ btnIndex.Text = "更新索引";
+ }
+
+ private static string FindLCP(string[] strs)
+ {
+ if (strs == null || strs.Length == 0) return "";
+
+ // 找到最短的字符串,因为LCP不会超过最短字符串的长度
+ string shortest = strs[0];
+ for (int i = 1; i < strs.Length; i++)
+ {
+ if (strs[i].Length < shortest.Length)
+ shortest = strs[i];
+ }
+
+ // 逐字符比较,直到找到不匹配的字符
+ for (int i = 0; i < shortest.Length; i++)
+ {
+ char c = shortest[i];
+ for (int j = 1; j < strs.Length; j++)
+ {
+ // 如果当前索引越界或字符不匹配,则返回当前LCP
+ if (i >= strs[j].Length || strs[j][i] != c)
+ return shortest.Substring(0, i);
+ }
+ }
+
+ // 如果所有字符串共享整个最短字符串,则返回它
+ return shortest;
+ }
+
+ private void btnSearch_Click(object sender, EventArgs e)
+ {
+ if (string.IsNullOrEmpty(txtPic.Text))
+ {
+ MessageBox.Show("请先选择图片");
+ return;
+ }
+
+ if (_index.Count == 0)
+ {
+ MessageBox.Show("当前没有任何索引,请先添加文件夹创建索引后再搜索");
+ return;
+ }
+
+ if (!new FileInfo(txtPic.Text).DetectFiletype().MimeType.StartsWith("image"))
+ {
+ MessageBox.Show("不是图像文件,无法检索");
+ return;
+ }
+
+ var sim = (float)numLike.Value / 100;
+ var hasher = new ImageHasher();
+ var sw = Stopwatch.StartNew();
+ var hashs = new ConcurrentBag();
+ var actions = new List();
+
+ if (txtPic.Text.EndsWith("gif"))
+ {
+ using (var gif = Image.Load(txtPic.Text))
+ {
+ for (var i = 0; i < gif.Frames.Count; i++)
+ {
+ var frame = gif.Frames.ExportFrame(i);
+ actions.Add(() =>
+ {
+ hashs.Add(frame.DifferenceHash256());
+ frame.Dispose();
+ });
+ }
+ Parallel.Invoke(actions.ToArray());
+ }
+ }
+ else
+ {
+ hashs.Add(hasher.DifferenceHash256(txtPic.Text));
+ using (var image = Image.Load(txtPic.Text))
+ {
+ if (cbRotate.Checked)
+ {
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Rotate(90));
+ var rotate90 = clone.DifferenceHash256();
+ hashs.Add(rotate90);
+ });
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Rotate(180));
+ var rotate180 = clone.DifferenceHash256();
+ hashs.Add(rotate180);
+ });
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Rotate(270));
+ var rotate270 = clone.DifferenceHash256();
+ hashs.Add(rotate270);
+ });
+ }
+
+ if (cbFlip.Checked)
+ {
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Flip(FlipMode.Horizontal));
+ var flipH = clone.DifferenceHash256();
+ hashs.Add(flipH);
+ });
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Flip(FlipMode.Vertical));
+ var flipV = clone.DifferenceHash256();
+ hashs.Add(flipV);
+ });
+ }
+ Parallel.Invoke(actions.ToArray());
+ }
+ }
+
+ var list = new List();
+ if (txtPic.Text.EndsWith("gif"))
+ {
+ list.AddRange(_frameIndex.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult
+ {
+ 路径 = x.Key,
+ 匹配度 = x.Value.SelectMany(h => hashs.Select(hh => ImageHasher.Compare(h, hh))).Where(f => f >= sim).OrderDescending().Take(10).DefaultIfEmpty().Average()
+ }).Where(x => x.匹配度 >= sim));
+ }
+ else
+ {
+ list.AddRange(_frameIndex.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult
+ {
+ 路径 = x.Key,
+ 匹配度 = x.Value.SelectMany(h => hashs.Select(hh => ImageHasher.Compare(h, hh))).Max()
+ }).Where(x => x.匹配度 >= sim));
+ list.AddRange(_index.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult
+ {
+ 路径 = x.Key,
+ 匹配度 = hashs.Select(h => ImageHasher.Compare(x.Value, h)).Max()
+ }).Where(x => x.匹配度 >= sim));
+ }
+
+ list = list.OrderByDescending(a => a.匹配度).ToList();
+ lbElpased.Text = sw.ElapsedMilliseconds + "ms";
+ if (list.Count > 0)
+ {
+ picSource.Refresh();
+ }
+
+ dgvResult.DataSource = list;
+ dgvResult.Focus();
+ }
+
+ private void dgvResult_CellContentClick(object sender, DataGridViewCellEventArgs e)
+ {
+ }
+
+ private void dgvResult_CellClick(object sender, DataGridViewCellEventArgs e)
+ {
+ picDest.ImageLocation = dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString();
+ }
+
+ private void picSource_LoadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
+ {
+ lbSrcInfo.Text = $"分辨率:{picSource.Image.Width}x{picSource.Image.Height},大小:{new FileInfo(picSource.ImageLocation).Length / 1024}KB";
+ }
+
+ private void picDest_LoadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
+ {
+ lblDestInfo.Text = $"分辨率:{picDest.Image.Width}x{picDest.Image.Height},大小:{new FileInfo(picDest.ImageLocation).Length / 1024}KB";
+ }
+
+ private void lblGithub_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ Process.Start(new ProcessStartInfo { FileName = lblGithub.Text, UseShellExecute = true });
+ }
+
+ private void txtDirectory_DragEnter(object sender, DragEventArgs e)
+ {
+ e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Link : DragDropEffects.None;
+ }
+
+ private void txtDirectory_DragDrop(object sender, DragEventArgs e)
+ {
+ ((TextBox)sender).Text = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
+ }
+
+ private void txtPic_DragEnter(object sender, DragEventArgs e)
+ {
+ e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Link : DragDropEffects.None;
+ }
+
+ private void txtPic_DragDrop(object sender, DragEventArgs e)
+ {
+ ((TextBox)sender).Text = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
+ }
+
+ private void buttonClipSearch_Click(object sender, EventArgs e)
+ {
+ if (Clipboard.ContainsFileDropList())
+ {
+ txtPic.Text = Clipboard.GetFileDropList()[0];
+ picSource.ImageLocation = txtPic.Text;
+ btnSearch_Click(sender, e);
+ return;
+ }
+
+ if (Clipboard.ContainsText())
+ {
+ var text = Clipboard.GetText();
+ if (File.Exists(text))
+ {
+ picSource.ImageLocation = text;
+ btnSearch_Click(sender, e);
+ }
+ else
+ {
+ dgvResult.DataSource = null;
+ picSource.Image = null;
+ picSource.Refresh();
+ }
+ return;
+ }
+
+ if (Clipboard.ContainsImage())
+ {
+ using var sourceImage = Clipboard.GetImage();
+ var filename = Path.Combine(Environment.GetEnvironmentVariable("temp"), SnowFlake.NewId);
+ sourceImage.Save(filename, ImageFormat.Jpeg);
+ picSource.ImageLocation = filename;
+ var sim = (float)numLike.Value / 100;
+ var hasher = new ImageHasher();
+ var sw = Stopwatch.StartNew();
+ var hash = hasher.DifferenceHash256(filename);
+ var hashs = new ConcurrentBag { hash };
+ using (var image = Image.Load(filename))
+ {
+ var actions = new List();
+ if (cbRotate.Checked)
+ {
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Rotate(90));
+ var rotate90 = clone.DifferenceHash256();
+ hashs.Add(rotate90);
+ });
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Rotate(180));
+ var rotate180 = clone.DifferenceHash256();
+ hashs.Add(rotate180);
+ });
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Rotate(270));
+ var rotate270 = clone.DifferenceHash256();
+ hashs.Add(rotate270);
+ });
+ }
+
+ if (cbFlip.Checked)
+ {
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Flip(FlipMode.Horizontal));
+ var flipH = clone.DifferenceHash256();
+ hashs.Add(flipH);
+ });
+ actions.Add(() =>
+ {
+ using var clone = image.Clone(c => c.Flip(FlipMode.Vertical));
+ var flipV = clone.DifferenceHash256();
+ hashs.Add(flipV);
+ });
+ }
+ Parallel.Invoke(actions.ToArray());
+ }
+
+ var list = _frameIndex.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult()
+ {
+ 路径 = x.Key,
+ 匹配度 = x.Value.SelectMany(h => hashs.Select(hh => ImageHasher.Compare(h, hh))).Max()
+ }).Where(x => x.匹配度 >= sim).ToList();
+ list.AddRange(_index.AsParallel().WithDegreeOfParallelism(32).Select(x => new SearchResult()
+ {
+ 路径 = x.Key,
+ 匹配度 = hashs.Select(h => ImageHasher.Compare(x.Value, h)).Max()
+ }).Where(x => x.匹配度 >= sim));
+ list = list.OrderByDescending(a => a.匹配度).ToList();
+ lbElpased.Text = sw.ElapsedMilliseconds + "ms";
+ if (list.Count > 0)
+ {
+ picSource.ImageLocation = filename;
+ picSource.Refresh();
+ }
+
+ dgvResult.DataSource = list;
+ Task.Run(() =>
+ {
+ Thread.Sleep(1000);
+ File.Delete(filename);
+ }).ContinueWith(_ => 0).ConfigureAwait(false);
+ }
+ }
+
+ private void dgvResult_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Right && dgvResult.SelectedCells.Count > 0)
+ {
+ dgvContextMenuStrip.Show(MousePosition.X, MousePosition.Y);
+ return;
+ }
+
+ dgvContextMenuStrip.Hide();
+ }
+
+ private void 打开所在文件夹_Click(object sender, EventArgs e)
+ {
+ ExplorerFile(dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString());
+ }
+
+ private void dgvResult_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
+ {
+ Process.Start(new ProcessStartInfo { FileName = dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString(), UseShellExecute = true });
+ }
+
+ ///
+ /// 打开路径并定位文件...对于@"h:\Bleacher Report - Hardaway with the safe call ??.mp4"这样的,explorer.exe /select,d:xxx不认,用API整它
+ ///
+ [DllImport("shell32.dll", ExactSpelling = true)]
+ private static extern void ILFree(IntPtr pidlList);
+
+ [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
+ private static extern IntPtr ILCreateFromPathW(string pszPath);
+
+ [DllImport("shell32.dll", ExactSpelling = true)]
+ private static extern int SHOpenFolderAndSelectItems(IntPtr pidlList, uint cild, IntPtr children, uint dwFlags);
+
+ public void ExplorerFile(string filePath)
+ {
+ if (!File.Exists(filePath))
+ {
+ return;
+ }
+
+ var pidlList = ILCreateFromPathW(filePath);
+ if (pidlList != IntPtr.Zero)
+ {
+ try
+ {
+ Marshal.ThrowExceptionForHR(SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0));
+ }
+ catch
+ {
+ }
+ finally
+ {
+ ILFree(pidlList);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ if (keyData == (Keys.Control | Keys.V))
+ {
+ buttonClipSearch_Click(null, null);
+ }
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ private void dgvResult_KeyUp(object sender, KeyEventArgs e)
+ {
+ }
+
+ private void dgvResult_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.KeyCode is Keys.Up or Keys.Down)
+ {
+ picDest.ImageLocation = dgvResult.SelectedCells[0].OwningRow.Cells["路径"].Value.ToString();
+ }
+ }
+
+ private void picSource_DoubleClick(object sender, EventArgs e)
+ {
+ Process.Start(new ProcessStartInfo { FileName = picSource.ImageLocation, UseShellExecute = true });
+ }
+
+ private void picDest_Click(object sender, EventArgs e)
+ {
+ Process.Start(new ProcessStartInfo { FileName = picDest.ImageLocation, UseShellExecute = true });
+ }
+
+ private void Form1_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (IndexRunning)
+ {
+ MessageBox.Show("正在索引文件,关闭程序前请先取消");
+ e.Cancel = true;
+ return;
+ }
+
+ if (_removingInvalidIndex)
+ {
+ MessageBox.Show("正在移除无效的索引文件,请稍后再试");
+ e.Cancel = true;
+ return;
+ }
+
+ ReaderWriterLock.Dispose();
+ }
+
+ private void picSource_Click(object sender, EventArgs e)
+ {
+ btnPic_Click(sender, e);
+ }
}
public record SearchResult
{
- public string 路径 { get; set; }
- public float 匹配度 { get; set; }
+ public string 路径 { get; set; }
+ public float 匹配度 { get; set; }
}
\ No newline at end of file
diff --git "a/\344\273\245\345\233\276\346\220\234\345\233\276/\344\273\245\345\233\276\346\220\234\345\233\276.csproj" "b/\344\273\245\345\233\276\346\220\234\345\233\276/\344\273\245\345\233\276\346\220\234\345\233\276.csproj"
index f847894..67f7bc3 100644
--- "a/\344\273\245\345\233\276\346\220\234\345\233\276/\344\273\245\345\233\276\346\220\234\345\233\276.csproj"
+++ "b/\344\273\245\345\233\276\346\220\234\345\233\276/\344\273\245\345\233\276\346\220\234\345\233\276.csproj"
@@ -16,6 +16,6 @@
-
+
\ No newline at end of file