正在查看 17 个帖子:1-17 (共 17 个帖子)
  • 作者
    帖子
  • @119593 回复 ⚑举报 

    星星之火
    游客

    請教各方高人,有批量截取如圖標示的紅色版心黃色頁面區域圖片的方法嗎?
    因為古籍每部書的版心或頁面尺寸都不一樣,使用機械方法固定寬高裁取圖片不精準,在本站見看典古籍OCR功能非常智能,能自動截取頁面文字部分為圖片,但沒有整頁或版心部分截取保存圖片的功能,苦於本人不識編程,故向各大高人求取批量截取如圖標示的紅色版心或黃色頁面區域圖片的方法,謝謝!

    a1

     

    @119602 回复 ⚑举报 

    未曾
    管理员

    这种干净规整的书页,也可以试试PS的色彩范围选择,然后裁切。录成一个动作,批量自动执行即可

    @119608 回复 ⚑举报 

    鷦鷯巢於一枝
    游客

    笨人用笨方法,手動切過成百上千張的笨鳥路過。。。

    @119679 回复 ⚑举报 

    我有一问
    游客

    我之前写过一个批量裁切工具,可以为全部图像、任意选中图像、单幅图像设置剪裁区域,然后执行一次批量裁切输出。

    由于同一册书籍中,多数作品剪裁区域大体相同,因此可选择一组大致相同的图像,批量设置剪切参数,再逐张检视、微调参数即可。

    设置参数时充分考虑到了操作效率问题,可用多种快捷键纯键盘操作,不需在键盘和鼠标间来回移动。使用稍熟练时,效率是远高于其他修图软件逐幅剪裁的。

    由于这个小工具是集成在其他工作软件中的,分离出来需要花一点时间。有需要的朋友可以留个邮箱我统计一下,如果能利好较多朋友,改天有空我分离出来写成单纯的小工具发布。

    Snipaste_2023-11-22_07-12-45

    @119680 回复 ⚑举报 

    我有一问
    游客

    另一个小工具是检测图像外围区域并自动删除的。比如老照片多见四边为黑色边框,可以自动识别并删除这些区域,生成新的图像。

    源码如下,有兴趣的朋友可自行修改、使用。

    public 检测照片黑色区域并删除()
    {
    InitializeComponent();
    }
    // 输入图片路径和输出图片路径
    private static string directoryPath = @"Z:\历史老照片";
    string outputImagePath = directoryPath +"\\新建文件夹";
    
    private void 检测照片黑色区域并删除Button_Click(object sender, EventArgs e)
    {
    string[] files = Directory.GetFiles(directoryPath).OrderBy(f => f, new Form1.NaturalSortComparer())
    .ToArray(); // 按文件名升序排序
    if (!Directory.Exists(outputImagePath))
    {
    Directory.CreateDirectory(outputImagePath) ;
    }
    
    for (int i = 0; i < files.Length; i++)
    {
    string file1 = files[i];
    string name=file1.Split("\\").Last();
    // 加载输入图片
    using (Bitmap inputImage = new Bitmap(file1))
    {
    // 检测连续的黑色外框区域
    Rectangle cropRect = DetectBlackBorder(inputImage);
    
    // 如果检测到黑色外框区域,则裁剪图像
    if (cropRect.Width > 0 && cropRect.Height > 0)
    {
    using (Bitmap croppedImage = CropImage(inputImage, cropRect))
    {
    // 保存裁剪后的图片
    croppedImage.Save(@$"{outputImagePath}\{Path.GetFileNameWithoutExtension(name)}.png", ImageFormat.Png);
    }
    }
    else
    {
    // 如果没有检测到黑色外框区域,直接保存原始图片
    inputImage.Save(outputImagePath, ImageFormat.Png);
    }
    }
    }
    
    
    
    
    
    
    
    }
    // 检测连续的黑色外框区域
    static Rectangle DetectBlackBorder(Bitmap image)
    {
    int width = image.Width;
    int height = image.Height;
    
    // 定义黑色像素的颜色阈值
    Color blackThreshold = Color.FromArgb(10, 10, 10);
    
    // 初始化裁剪区域的边界
    int left = 0;
    int top = 0;
    int right = width;
    int bottom = height;
    
    // 从左侧开始检测黑色外框区域
    for (int x = 0; x < width; x++)
    {
    bool isBlackColumn = true;
    for (int y = 0; y < height; y++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackColumn = false;
    break;
    }
    }
    if (!isBlackColumn)
    {
    left = x;
    break;
    }
    }
    
    // 从右侧开始检测黑色外框区域
    for (int x = width - 1; x >= 0; x--)
    {
    bool isBlackColumn = true;
    for (int y = 0; y < height; y++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackColumn = false;
    break;
    }
    }
    if (!isBlackColumn)
    {
    right = x;
    break;
    }
    }
    
    // 从顶部开始检测黑色外框区域
    for (int y = 0; y < height; y++)
    {
    bool isBlackRow = true;
    for (int x = 0; x < width; x++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackRow = false;
    break;
    }
    }
    if (!isBlackRow)
    {
    top = y;
    break;
    }
    }
    
    // 从底部开始检测黑色外框区域
    for (int y = height - 1; y >= 0; y--)
    {
    bool isBlackRow = true;
    for (int x = 0; x < width; x++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackRow = false;
    break;
    }
    }
    if (!isBlackRow)
    {
    bottom = y;
    break;
    }
    }
    
    // 返回裁剪区域的矩形
    return new Rectangle(left, top, right - left + 1, bottom - top + 1);
    }
    
    // 裁剪图像
    static Bitmap CropImage(Bitmap image, Rectangle cropRect)
    {
    Bitmap croppedImage = new Bitmap(cropRect.Width, cropRect.Height);
    using (Graphics g = Graphics.FromImage(croppedImage))
    {
    g.DrawImage(image, new Rectangle(0, 0, cropRect.Width, cropRect.Height), cropRect, GraphicsUnit.Pixel);
    }
    return croppedImage;
    }
    @119686 回复 ⚑举报 

    鷦鷯巢於一枝
    游客

    @我有一问 #119679

    静待大神的小工具发布。

    @119749 回复 ⚑举报 

    星星之火
    游客

    @未曾 #119602

    謝謝站長!我先試試

    @我有一问 #119679

    謝謝大神!

    @我有一问 #119680

    另一个小工具是检测图像外围区域并自动删除的。比如老照片多见四边为黑色边框,可以自动识别并删除这些区域,生成新的图像。

    請教大神這個源碼需要安裝什麼環境可執行?

    大神這個检测图像外围区域的工具非常智能,可否設置成自動檢測圖像外圍區域黑色邊框(或其他設置),對黑色邊框以內區域(含黑色邊框)進行裁剪生成新的圖像?

     

     

    @119779 回复 ⚑举报 

    我有一问
    游客

    @星星之火 #119749

    代码是C#+.net 6.0环境的。理论上其他环境中小改一下也都同理。
    其中Color.FromArgb(10, 10, 10)是设置不同检测颜色的,也可设为其他颜色。

     

    @119811 回复 ⚑举报 

    摆渡
    游客

    期待,小软件,大智慧

    @119852 回复 ⚑举报 

    星星之火
    游客

    @我有一问 #119779

    謝謝大神!

    剛瀏覽博客見有一篇使用Python截取圖片的思路和大神的思路非常神似,這個方法可以對預提取部分進行傾斜修正,這個功能如果能應用於書籍圖片版心截圖並對傾斜的版心圖片進行修正,對圖片的處理很有幫助,大神,可以把這個功能集合到您的源碼中嗎?

    具體參見:

    OpenCV-Python 通过边缘检测识别物体并批量提取(大米识别为例)——minAreaRect批量生成物体的最小外接矩形(旋转矩形)并批量裁剪

    OpenCV版本:4.0.0.21(已兼容4.5.2.X版本)

    算法实现思路如下:

    对图像做降噪滤波处理
    提取边缘
    检测轮廓
    检测轮廓最小外接矩形(旋转矩形)
    旋转图像
    裁剪
    代码如下:

    import cv2
    import numpy as np
    
    image = cv2.imread("rice.jpg")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 转为灰度图
    blurred = cv2.GaussianBlur(gray, (11, 11), 0)
    edge = cv2.Canny(blurred, 30, 150) # 用Canny算子提取边缘
    cv2.imwrite("./results/edge.jpg", edge)
    
    contour = image.copy()
    (cnts, _) = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测
    cv2.drawContours(contour, cnts, -1, (0, 255, 0), 2) # 绘制轮廓
    cv2.imwrite("./results/contour.jpg", contour)
    
    count = 0 # 米粒个数
    margin = 5 # 裁剪边距
    draw_rect = image.copy()
    for i, contour in enumerate(cnts):
    area = cv2.contourArea(contour) # 计算包围形状的面积
    if area < 15: # 过滤面积小于15的形状
    continue
    count += 1
    rect = cv2.minAreaRect(contour) # 检测轮廓最小外接矩形,得到最小外接矩形的(中心(x,y), (宽,高), 旋转角度)
    box = np.int0(cv2.boxPoints(rect)) # 获取最小外接矩形的4个顶点坐标
    cv2.drawContours(draw_rect, [box], 0, (255, 0, 0), 2) # 绘制轮廓最小外接矩形
    
    h, w = image.shape[:2] # 原图像的高和宽
    rect_w, rect_h = int(rect[1][0]) + 1, int(rect[1][1]) + 1 # 最小外接矩形的宽和高
    if rect_w <= rect_h:
    x, y = int(box[1][0]), int(box[1][1]) # 旋转中心
    M2 = cv2.getRotationMatrix2D((x, y), rect[2], 1)
    rotated_image = cv2.warpAffine(image, M2, (w * 2, h * 2))
    y1, y2 = y - margin if y - margin > 0 else 0, y + rect_h + margin + 1
    x1, x2 = x - margin if x - margin > 0 else 0, x + rect_w + margin + 1
    rotated_canvas = rotated_image[y1: y2, x1: x2]
    else:
    x, y = int(box[2][0]), int(box[2][1]) # 旋转中心
    M2 = cv2.getRotationMatrix2D((x, y), rect[2] + 90, 1)
    rotated_image = cv2.warpAffine(image, M2, (w * 2, h * 2))
    y1, y2 = y - margin if y - margin > 0 else 0, y + rect_w + margin + 1
    x1, x2 = x - margin if x - margin > 0 else 0, x + rect_h + margin + 1
    rotated_canvas = rotated_image[y1: y2, x1: x2]
    print("rice #{}".format(count))
    # cv2.imshow("rotated_canvas", rotated_canvas)
    cv2.imwrite("./rotation-results/{}.jpg".format(count), rotated_canvas)
    cv2.waitKey(0)
    cv2.imwrite("./results/rect.jpg", draw_rect)

    原图像如下:

    j1

    裁剪出来的部分效果如下:

    j2

     

     

    @119909 回复 ⚑举报 

    水墨
    游客

    @未曾 先生,請問在回復是如何引用才可以保持源碼格式顯示不變?如上

    @我有一问 #119680

    大神貼出的源碼網站顯示的格式把源碼前面的空格全部消除了,再次引用此貼出的源碼因格式改變而無法運行,@未曾 先生,有方法修正這個格式嗎?

    @119913 回复 ⚑举报 

    我有一问
    游客

    小工具本身有图像批量旋转功能。只是不能自动识别倾斜角度并自动旋转(个人觉得没必要,版面各种复杂情况太多了,程序做不到准确判断)。

    @119916 回复 ⚑举报 

    未曾
    管理员

    @水墨 #119909

    Screenshot_2023-11-24-14-07-25-627_com.android.chrome-edit

    @119921 回复 ⚑举报 

    水墨
    游客

    @未曾 #119916

    收到,感謝先生!

    @我有一问 #119680

    感謝大神!請大神將此源碼按原格式再分享一次,小可再運行試試,謝謝!

    @119925 回复 ⚑举报 

    我有一问
    游客
    public partial class 检测照片黑色区域并删除 : Form
    {
    // 输入图片路径和输出图片路径
    private static string directoryPath = @"Z:\大图上传\舆图方志类\偏.舆图记录\新建文件夹 (2)";
    
    private string outputImagePath = directoryPath + "\\新建文件夹";
    
    public 检测照片黑色区域并删除()
    {
    InitializeComponent();
    }
    // 裁剪图像
    private static Bitmap CropImage(Bitmap image, Rectangle cropRect)
    {
    Bitmap croppedImage = new Bitmap(cropRect.Width, cropRect.Height);
    using (Graphics g = Graphics.FromImage(croppedImage))
    {
    g.DrawImage(image, new Rectangle(0, 0, cropRect.Width, cropRect.Height), cropRect, GraphicsUnit.Pixel);
    }
    return croppedImage;
    }
    
    // 检测连续的黑色外框区域
    private static Rectangle DetectBlackBorder(Bitmap image)
    {
    int width = image.Width;
    int height = image.Height;
    
    // 定义黑色像素的颜色阈值
    Color blackThreshold = Color.FromArgb(10, 10, 10);
    
    // 初始化裁剪区域的边界
    int left = 0;
    int top = 0;
    int right = width;
    int bottom = height;
    
    // 从左侧开始检测黑色外框区域
    for (int x = 0; x < width; x++)
    {
    bool isBlackColumn = true;
    for (int y = 0; y < height; y++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackColumn = false;
    break;
    }
    }
    if (!isBlackColumn)
    {
    left = x;
    break;
    }
    }
    
    // 从右侧开始检测黑色外框区域
    for (int x = width - 1; x >= 0; x--)
    {
    bool isBlackColumn = true;
    for (int y = 0; y < height; y++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackColumn = false;
    break;
    }
    }
    if (!isBlackColumn)
    {
    right = x;
    break;
    }
    }
    
    // 从顶部开始检测黑色外框区域
    for (int y = 0; y < height; y++)
    {
    bool isBlackRow = true;
    for (int x = 0; x < width; x++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackRow = false;
    break;
    }
    }
    if (!isBlackRow)
    {
    top = y;
    break;
    }
    }
    
    // 从底部开始检测黑色外框区域
    for (int y = height - 1; y >= 0; y--)
    {
    bool isBlackRow = true;
    for (int x = 0; x < width; x++)
    {
    Color pixelColor = image.GetPixel(x, y);
    if (pixelColor.R > blackThreshold.R || pixelColor.G > blackThreshold.G || pixelColor.B > blackThreshold.B)
    {
    isBlackRow = false;
    break;
    }
    }
    if (!isBlackRow)
    {
    bottom = y;
    break;
    }
    }
    
    // 返回裁剪区域的矩形
    return new Rectangle(left, top, right - left + 1, bottom - top + 1);
    }
    
    private void 检测照片黑色区域并删除Button_Click(object sender, EventArgs e)
    {
    string[] files = Directory.GetFiles(directoryPath).OrderBy(f => f, new Form1.NaturalSortComparer())
    .ToArray(); // 按文件名升序排序
    if (!Directory.Exists(outputImagePath))
    {
    Directory.CreateDirectory(outputImagePath);
    }
    
    for (int i = 0; i < files.Length; i++)
    {
    string file1 = files[i];
    string name = file1.Split("\\").Last();
    // 加载输入图片
    using (Bitmap inputImage = new Bitmap(file1))
    {
    // 检测连续的黑色外框区域
    Rectangle cropRect = DetectBlackBorder(inputImage);
    
    // 如果检测到黑色外框区域,则裁剪图像
    if (cropRect.Width > 0 && cropRect.Height > 0)
    {
    using (Bitmap croppedImage = CropImage(inputImage, cropRect))
    {
    // 保存裁剪后的图片
    croppedImage.Save(@$"{outputImagePath}\{Path.GetFileNameWithoutExtension(name)}.png", ImageFormat.Png);
    }
    }
    else
    {
    // 如果没有检测到黑色外框区域,直接保存原始图片
    inputImage.Save(outputImagePath, ImageFormat.Png);
    }
    }
    }
    }
    }
    @119930 回复 ⚑举报 

    水墨
    游客

    @我有一问 #119925

    感謝大神!

    @未曾 #119916

    先生,@我有一问 #119925大神按照您提示的方法再次分享了源碼,但網站顯示還是把源碼左邊原有的空格消除了,我也試著選擇預格式化再把左邊含有空格的代碼粘貼到錄入框內,代碼左邊的空格都被去除了

    如圖:

    1、選擇預格式化

    a1
    
    2、粘貼的原代碼
    a2
    
    3、粘貼後網站顯示的代碼被消除了左側的空格
    a3
    
    

     

    @119962 回复 ⚑举报 

    行千里
    游客

    @我有一问 #119925

    求大佬开发的小工具并源码,邮箱:3819828021@qq.com,谢谢!

     

正在查看 17 个帖子:1-17 (共 17 个帖子)
正在查看 17 个帖子:1-17 (共 17 个帖子)

上传图片

拖拽或点击选择图片(最多五张)

回复至:請教各方高人
您的信息:



发帖/回帖前,请了解相关版规

0,邮箱地址尽量真实有效,随意填写的可能会被系统误判为垃圾内容。
1,不要开书单。单个帖子尽量发布一种书籍需求。
2,在搜索不到相关主题的情况下,尽量发新帖(发帖标题最好带上书名)。不要在他人帖子中回复某种书籍需要。
3,发帖提问标题尽量简单明了。发帖内容不要太过简略,请对书籍内容、版本或作者作简要说明。
4,出版于1973年以后的资源需求或分享将会被清理删除。