- 作者帖子
星星之火游客請教各方高人,有批量截取如圖標示的紅色版心或黃色頁面區域圖片的方法嗎?
因為古籍每部書的版心或頁面尺寸都不一樣,使用機械方法固定寬高裁取圖片不精準,在本站見看典古籍OCR功能非常智能,能自動截取頁面文字部分為圖片,但沒有整頁或版心部分截取保存圖片的功能,苦於本人不識編程,故向各大高人求取批量截取如圖標示的紅色版心或黃色頁面區域圖片的方法,謝謝!
未曾管理员这种干净规整的书页,也可以试试PS的色彩范围选择,然后裁切。录成一个动作,批量自动执行即可
鷦鷯巢於一枝游客笨人用笨方法,手動切過成百上千張的笨鳥路過。。。
我有一问游客我之前写过一个批量裁切工具,可以为全部图像、任意选中图像、单幅图像设置剪裁区域,然后执行一次批量裁切输出。
由于同一册书籍中,多数作品剪裁区域大体相同,因此可选择一组大致相同的图像,批量设置剪切参数,再逐张检视、微调参数即可。
设置参数时充分考虑到了操作效率问题,可用多种快捷键纯键盘操作,不需在键盘和鼠标间来回移动。使用稍熟练时,效率是远高于其他修图软件逐幅剪裁的。
由于这个小工具是集成在其他工作软件中的,分离出来需要花一点时间。有需要的朋友可以留个邮箱我统计一下,如果能利好较多朋友,改天有空我分离出来写成单纯的小工具发布。
我有一问游客另一个小工具是检测图像外围区域并自动删除的。比如老照片多见四边为黑色边框,可以自动识别并删除这些区域,生成新的图像。
源码如下,有兴趣的朋友可自行修改、使用。
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; }
鷦鷯巢於一枝游客@我有一问 #119679
静待大神的小工具发布。
星星之火游客
我有一问游客
摆渡游客期待,小软件,大智慧
星星之火游客@我有一问 #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)
原图像如下:
裁剪出来的部分效果如下:
水墨游客@未曾 先生,請問在回復是如何引用才可以保持源碼格式顯示不變?如上
@我有一问 #119680
大神貼出的源碼網站顯示的格式把源碼前面的空格全部消除了,再次引用此貼出的源碼因格式改變而無法運行,@未曾 先生,有方法修正這個格式嗎?
我有一问游客小工具本身有图像批量旋转功能。只是不能自动识别倾斜角度并自动旋转(个人觉得没必要,版面各种复杂情况太多了,程序做不到准确判断)。
未曾管理员@水墨 #119909
我有一问游客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); } } } } }
水墨游客
行千里游客- 作者帖子
正在查看 17 个帖子:1-17 (共 17 个帖子)
正在查看 17 个帖子:1-17 (共 17 个帖子)
正在查看 17 个帖子:1-17 (共 17 个帖子)