From 7faac59c62da020aca2331b5fd3b2c0c7eeaa143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=87=92=E5=BE=97=E5=8B=A4=E5=BF=AB?= Date: Sun, 21 Jul 2024 13:53:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Form1.Designer.cs" | 753 +++++------ .../Form1.cs" | 1184 +++++++++-------- ...45\233\276\346\220\234\345\233\276.csproj" | 2 +- 3 files changed, 981 insertions(+), 958 deletions(-) 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