onlyclxy 发表于 2024-2-21 10:20

判断一个图是截图的思路有什么?

本帖最后由 onlyclxy 于 2024-2-22 12:31 编辑

解决好了
c#找了个免费的ocr识字的模块.对图片进行识字. 有字的标记为截图
这个效果 很好了,就 最后就剩几百张了不好区分了剩下的有点那种海报 封面图的,本身就有字. 就还得搞

然后我忽然想起来半年前搞AI, 用python搞了个反推图片提示词的代码..
感觉剩下的完全可以用那个,返回下提示词. 凡是推出来的提示词带有场景或者人物词汇的. 都视为正常图片

此贴终结, 感谢大家的集思广益


这几天把近一个多月的QQ的缓存图片导出来 (就是全部群聊里的图片和别人聊天的图片非接收的图片) 有7000多张,然后有很多没用的截图,需要去掉.就开始琢磨怎么去掉那截图的图片
一开始想着截图里一般都有字, 想着看看能不能识别到有字就定义为截图. 但是貌似没有什么现成的方法
于是又想了个方法, 算一个图里有多少个不重复的像素点. 然后找一个阈值,找5000像素点以下的图像 一般就是截图
我这边观察到 ,纯截图一个图大约3000多个像素点,一张正常的图片得几万个像素点了
其实我这个意思本来是想找色来着 因为截图那种, 色彩比较单一. 但是好像也没什么思路写算法
这一套弄下来效果其实还好了. 不过仍有很多截图, 像素点也特别多. 就不太好识别了.
大家还有什么想法能讨论下吗? 怎么能精准找到截图?


下面可以分享一下我前几天写的,一个是可以根据图片宽高比,分辨率, 2的幂次方来区分图片的,但是这个不能识别,只能当辅助识别
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

class ImageSorter
{
    static void Main()
    {
      Console.WriteLine("请输入图片所在的路径:");
      string inputPath = Console.ReadLine();

      if (string.IsNullOrEmpty(inputPath) || !Directory.Exists(inputPath))
      {
            Console.WriteLine("提供的路径无效或不存在。");
            return;
      }

      Console.WriteLine("请输入宽高比容差值(正数表示接近正方形的容差,负数表示接受更大的比例差异,默认直接按回车键忽略此条件):");
      string toleranceInput = Console.ReadLine();
      double? aspectRatioTolerance = string.IsNullOrEmpty(toleranceInput) ? null : (double?)double.Parse(toleranceInput);

      Console.WriteLine("请输入分辨率条件(例如 '>1920x1080', '<1000', '>=500x500',<宽x500,<100x高 ,直接按回车键忽略此条件):");
      string resolutionCondition = Console.ReadLine();

      Console.WriteLine("是否需要判断图片分辨率是否为2的幂次方?(是/否/只取不是,默认不判断):");
      string powerOfTwoOption = Console.ReadLine().Trim().ToLower();

      string targetFolder = Path.Combine(inputPath, "FilteredImages");
      if (!Directory.Exists(targetFolder))
      {
            Directory.CreateDirectory(targetFolder);
      }

      string logPath = Path.Combine(inputPath, "error.log");
      string[] imageFiles = Directory.GetFiles(inputPath, "*.*", SearchOption.TopDirectoryOnly)
            .Where(file => file.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ||
                           file.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) ||
                           file.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
            .ToArray();

      Parallel.ForEach(imageFiles, (file) =>
      {
            bool moveFile = false; // 标记是否移动文件
            try
            {
                using (Image image = Image.FromFile(file))
                {
                  bool aspectRatioMatched = aspectRatioTolerance == null || IsAspectRatioMatched(image, aspectRatioTolerance.Value);
                  bool resolutionMatched = IsResolutionMatched(image, resolutionCondition); // 假设此方法已实现
                  bool isPowerOfTwo = IsPowerOfTwo(image.Width) && IsPowerOfTwo(image.Height);

                  bool powerOfTwoCondition = (powerOfTwoOption == "是" && isPowerOfTwo) ||
                                             (powerOfTwoOption == "只取不是" && !isPowerOfTwo) ||
                                             powerOfTwoOption == "否" || string.IsNullOrEmpty(powerOfTwoOption);

                  if (aspectRatioMatched && resolutionMatched && powerOfTwoCondition)
                  {
                        moveFile = true;
                  }
                }
            }
            catch (Exception ex)
            {
                lock (logPath)
                {
                  File.AppendAllText(logPath, $"{DateTime.Now}: 处理文件 {file} 时出错 - {ex.Message}\n");
                }
                Console.WriteLine($"处理文件 {Path.GetFileName(file)} 时出错:{ex.Message}");
            }

            if (moveFile)
            {
                try
                {
                  string fileName = Path.GetFileName(file);
                  string targetPath = Path.Combine(targetFolder, fileName);
                  File.Move(file, targetPath);
                  Console.WriteLine($"文件 {fileName} 已被移动到 {targetFolder}。");
                }
                catch (Exception ex)
                {
                  lock (logPath)
                  {
                        File.AppendAllText(logPath, $"{DateTime.Now}: 移动文件 {file} 时出错 - {ex.Message}\n");
                  }
                  Console.WriteLine($"移动文件 {Path.GetFileName(file)} 时出错:{ex.Message}");
                }
            }
      });

      Console.WriteLine("处理完成。请按任意键退出...");
      Console.ReadKey();
    }

    private static bool IsAspectRatioMatched(Image image, double aspectRatioTolerance)
    {
      double aspectRatio = (double)image.Width / image.Height;

      if (aspectRatioTolerance >= 0)
      {
            // 正数容差值用于寻找接近正方形的图片
            return Math.Abs(aspectRatio - 1.0) <= aspectRatioTolerance;
      }
      else if (aspectRatioTolerance < 0)
      {
            // 负数容差值的绝对值作为宽高比阈值,用于寻找宽高比差异特别大的图片
            double aspectRatioThreshold = Math.Abs(aspectRatioTolerance);
            return aspectRatio >= aspectRatioThreshold || 1 / aspectRatio >= aspectRatioThreshold;
      }

      // 如果容差值为0,则不进行宽高比的判断
      return true;
    }


    private static bool IsPowerOfTwo(int n)
    {
      return n > 0 && (n & (n - 1)) == 0;
    }

    private static bool IsResolutionMatched(Image image, string condition)
    {
      if (string.IsNullOrEmpty(condition))
      {
            return true; // 如果没有分辨率条件,则默认匹配
      }

      // 解析条件字符串,匹配各种可能的输入格式
      Regex regex = new Regex(@"([<>=]*)(宽x|高x|)(\d+)(x(宽|高|)(\d+)|)");
      Match match = regex.Match(condition);

      if (!match.Success)
      {
            Console.WriteLine("分辨率条件格式不正确。");
            return false;
      }

      string comparison = match.Groups.Value;
      string firstDimension = match.Groups.Value;
      int firstValue = int.Parse(match.Groups.Value);
      string secondDimension = match.Groups.Value;
      int secondValue = match.Groups.Success ? int.Parse(match.Groups.Value) : 0;

      // 应用分辨率条件
      return ApplyResolutionCondition(image, comparison, firstDimension, firstValue, secondDimension, secondValue);
    }

    private static bool ApplyResolutionCondition(Image image, string comparison, string firstDimension, int firstValue, string secondDimension, int secondValue)
    {
      int width = image.Width;
      int height = image.Height;

      // 根据条件构建逻辑判断
      switch (comparison)
      {
            case ">":
                return (firstDimension == "宽x" && width > firstValue) || (secondDimension == "x高" && height > secondValue) ||
                     (firstDimension == "" && secondDimension == "" && (width > firstValue || height > firstValue));
            case "<":
                return (firstDimension == "宽x" && width < firstValue) || (secondDimension == "x高" && height < secondValue) ||
                     (firstDimension == "" && secondDimension == "" && (width < firstValue || height < firstValue));
            case ">=":
                return (firstDimension == "宽x" && width >= firstValue) || (secondDimension == "x高" && height >= secondValue) ||
                     (firstDimension == "" && secondDimension == "" && (width >= firstValue || height >= firstValue));
            case "<=":
                return (firstDimension == "宽x" && width <= firstValue) || (secondDimension == "x高" && height <= secondValue) ||
                     (firstDimension == "" && secondDimension == "" && (width <= firstValue || height <= firstValue));
            case "=":
                return (firstDimension == "宽x" && width == firstValue) || (secondDimension == "x高" && height == secondValue) ||
                     (firstDimension == "" && secondDimension == "" && (width == firstValue || height == firstValue));
            default:
                Console.WriteLine("未识别的比较符号。");
                return false;
      }
    }

}



下面是opencv写的,算不重复的像素点区分截图的, <10000 找截图的效果都还不错, 就是总会剩不少
using Emgu.CV;
using Emgu.CV.Structure;
using System;
using System.Collections.Concurrent;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {

      Console.WriteLine("请输入目录路径:");
      string directoryPath = Console.ReadLine();


      Console.WriteLine("请输入阈值条件 (<>=阈值), 直接回车默认为<5000:");
      string input = Console.ReadLine();
      bool isLessThan = string.IsNullOrEmpty(input) || input.StartsWith("<");
      int threshold = string.IsNullOrEmpty(input) ? 5000 : int.Parse(input.Substring(1));

      string[] supportedFormats = { "*.jpg", "*.jpeg", "*.png", "*.tga", "*.bmp", "gif" };
      var filePaths = supportedFormats.SelectMany(format => Directory.GetFiles(directoryPath, format, SearchOption.AllDirectories)).ToList();

      string newDirectory = Path.Combine(directoryPath, "Filtered");
      Directory.CreateDirectory(newDirectory); // 提前创建目标目录

      Parallel.ForEach(filePaths, (filePath) =>
      {
            try
            {
                using (var img = new Image<Bgr, byte>(filePath).Resize(0.5, Emgu.CV.CvEnum.Inter.Linear)) // 以0.5的比例降低分辨率
                {
                  var uniqueColors = new ConcurrentBag<Color>();
                  Parallel.For(0, img.Height, y =>
                  {
                        for (int x = 0; x < img.Width; x++)
                        {
                            Bgr color = img;
                            var colorObj = Color.FromArgb((int)color.Red, (int)color.Green, (int)color.Blue);
                            uniqueColors.Add(colorObj);
                        }
                  });

                  int uniqueColorCount = uniqueColors.Distinct().Count();
                  Console.WriteLine($"处理图片: {filePath}, 唯一颜色数量: {uniqueColorCount}");

                  if ((isLessThan && uniqueColorCount < threshold) || (!isLessThan && uniqueColorCount >= threshold))
                  {
                        string newFilePath = Path.Combine(newDirectory, Path.GetFileName(filePath));
                        File.Move(filePath, newFilePath);
                        Console.WriteLine($"图片 {filePath} 已移动到 {newFilePath}");
                  }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"处理文件 {filePath} 时发生错误: {ex.Message}");
            }
      });

      Console.WriteLine("处理完成。");
    }

    static void 查询阈值()
    {

      Console.WriteLine("请输入目录路径:");
      string directoryPath = Console.ReadLine();

      // 加载图像
      Image<Bgr, Byte> img = new Image<Bgr, byte>(directoryPath);

      // 获取所有像素的颜色值
      HashSet<Color> uniqueColors = new HashSet<Color>();
      for (int y = 0; y < img.Height; y++)
      {
            for (int x = 0; x < img.Width; x++)
            {
                Bgr color = img;
                // 将Bgr颜色转换为Color对象
                Color colorObj = Color.FromArgb((int)color.Red, (int)color.Green, (int)color.Blue);
                uniqueColors.Add(colorObj);
            }
      }

      // 检查唯一颜色数量是否超过设定值
      int threshold = 100; // 你可以根据需要设置这个阈值
      if (uniqueColors.Count > threshold)
      {
            Console.WriteLine($"图像中的唯一颜色数量超过了设定的阈值 {threshold}。实际数量为: {uniqueColors.Count}");
      }
      else
      {
            Console.WriteLine($"图像中的唯一颜色数量没有超过设定的阈值 {threshold}。实际数量为: {uniqueColors.Count}");
      }

    }
}

Pwaerm 发表于 2024-2-21 10:33

截图的文件大小和像素都比较低吧

czz404 发表于 2024-2-21 10:35

通过图片exif信息不知道可不可行,截图不会有iso等等信息

as614001 发表于 2024-2-21 10:37

一开始想着截图里一般都有字, 想着看看能不能识别到有字就定义为截图. 但是貌似没有什么现成的方法 》》》 ocr识别就可以了
于是又想了个方法, 算一个图里有多少个不重复的像素点. 然后找一个阈值,找5000像素点以下的图像 一般就是截图 》》》 好奇怪那不就是分别率
其实都不可取 ,应为并不知道你的截图类型,当然你也可两种方法混合筛选出可疑截图
最后,可根据照片信息读取进行判断,详情右键查看属性-详细信息 对比
需要对比一下截图图片和正常图片的详细信息,提取特征

RS水果 发表于 2024-2-21 10:39

czz404 发表于 2024-2-21 10:35
通过图片exif信息不知道可不可行,截图不会有iso等等信息

微信QQ 等通讯软件 你发图不勾选原图的情况下, 默认会去除exif的信息

这个判断并不可靠

依然爱你954 发表于 2024-2-21 10:40

格式 文件名

三滑稽甲苯 发表于 2024-2-21 10:41

训练/找一个 ai 来识别应该是可行的

木子汐 发表于 2024-2-21 10:51

opencv大图中找小图

dingqh 发表于 2024-2-21 10:52

Pwaerm 发表于 2024-2-21 10:33
截图的文件大小和像素都比较低吧

不一定,4K显示器的截图 就比较大....

爱飞的猫 发表于 2024-2-21 10:57

训练个 ai 模型然后把图喂给它

具体怎么写可以找 chatgpt 之类的生成式 ai 帮忙
页: [1] 2 3 4
查看完整版本: 判断一个图是截图的思路有什么?