简 介: 本文中我们讨论了利用 C++, Python对图像进行剪切的基本方法, 这些方法都是应用了对于矩阵的切片操作完成的。指定剪切图片在图像数据矩阵中对应的高、宽的范围,对应范围的数据代表了切割出来的图像。通过 imwirte, imshow 可以将切割下的图片进行存储和显示。 后面也进一步讨论了如何对大的图片进行分割,形成许多小的图片的方法。

关键词 图片剪切crop

前 言
目 录
Contents
为什么需要
图像裁剪?
OpenCV如何完
成图片裁剪的?
所需要软件包
或者头文件
图片裁剪
读入图片
图片裁剪
分割图片
分割图像的代码
显示和存储
分割的图像
分割图像总结
网络应用程序

本文来自于 Cropping an Image using OpenCV 中对于OpenCV中裁剪图片方法的介绍。

 

§00   言
---   好了,为了你自己的需要,让我们看看如何应用OpenCV来裁剪一个图像。

0.1 为什么需要图像裁剪?

  首先,为什么我们需要对图像进行裁剪?

  裁剪图像是将图片中不需要的部分去掉,或者对图片中重要的部分进行凸显。

0.2 OpenCV如何完成图片裁剪的?

  在OpenCV中并没有用于裁剪图片的特殊函数,而是通过NumPy 矩阵的切片功能来完成对于图片的裁剪的。每一个被读入计算机内存的图片都是存储在2D的矩阵(每一个颜色通道都是存储在2D矩阵,一个图片可能包含有三个颜色通道)。只需要指明需要裁剪下来的区域对应的像素的高宽位置,我们就可以办到了。

0.3 样例代码

  下面的代码片段演示了在Python,C++中如何完成图片的裁剪的。在本文的后面将会对他们进行详细的介绍。如果你仅仅希望参照一些可执行的样例完成你手头的工作,看看这些代码也就够了。

  • C++
// Include Libraries
#include<opencv2/opencv.hpp>
#include<iostream>

// Namespace nullifies the use of cv::function();
using namespace std;
using namespace cv;

int main()
{
	// Read image
	Mat img = imread("test.jpg");
	cout << "Width : " << img.size().width << endl;
	cout << "Height: " << img.size().height << endl;
	cout<<"Channels: :"<< img.channels() << endl;
	// Crop image
	Mat cropped_image = img(Range(80,280), Range(150,330));

	//display image
	imshow(" Original Image", img);
	imshow("Cropped Image", cropped_image);

	//Save the cropped Image
	imwrite("Cropped Image.jpg", cropped_image);

	// 0 means loop infinitely
	waitKey(0);
	destroyAllWindows();
	return 0;
}

0.4 所需要软件包或者头文件

  • Python
# Importing the cv2 library
import cv2 
  • C++
#include<opencv2/opencv.hpp>
#include<iostream>

// Namespace nullifies the use of cv::function(); 
using namespace std;
using namespace cv;

 

§01 片裁剪


  面这个图片就是后面代码中所使用的图片样例。

▲ 图4.1  后面软件样例中所使用的图片

▲ 图4.1 后面软件样例中所使用的图片

1.1 读入图片

  • Python
img=cv2.imread('test.png')

# Prints Dimensions of the image
print(img.shape) 

# Display the image
cv2.imshow("original", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • C++
Mat img = imread("test.jpg");

//Print the height and width of the image
cout << "Width : " << img.size().width << endl;
cout << "Height: " << img.size().height << endl;
cout << "Channels: " << img.channels() << endl;

// Display image
imshow("Image", img);
waitKey(0);
destroyAllWindows();

  上面的代码将图片读入计算机并进行显示,包括显示图片的尺寸。 图片的尺寸不仅仅包括宽、高,也包括有图片的颜色通道数量,比如对于RGB彩色图像它就包括有三个颜色通道:红、绿、蓝。

1.2 图片裁剪

  下面进行图片的裁剪,将包含有花朵的部分裁剪下来。

  • Python
cropped_image = img[80:280, 150:330] # Slicing to crop the image

# Display the cropped image
cv2.imshow("cropped", cropped_image)
cv2.waitKey(0)
cv2.destroyAllWindows() 
  • C++
Mat crop = img(Range(80,280),Range(150,330)); // Slicing to crop the image

// Display the cropped image
imshow("Cropped Image", crop);

waitKey(0);
destroyAllWindows();
return 0;

▲ 图1.2.1  图片裁剪后的结果

▲ 图1.2.1 图片裁剪后的结果

  在Python程序中,你使用的NumPy矩阵的切片来完成图片的裁剪。对于矩阵的切片,你需要指明对于矩阵索引的起始和结束的索引数值,包括第一个和第二个坐标轴。

  • 通常情况下,第一个坐标表示了图像的高度;
  • 第二个坐标轴表示了图像的宽度;

  如果按照传统的方式,用二维矩阵来表示图片,第一个坐标代表着矩阵的 行(在图像中行代表了图片的Y轴的方向),那么如何应用NumPy矩阵的切片来完成图片的裁剪呢?下面给出了对应的代码片段。

cropped = img[start_row:end_row, start_col:end_col]

  在C++中,我们使用了 Range() 函数了对图片进行裁剪:

  • 与Python一样,也是使用了切片操作;
  • 同样,图片表示为2D矩阵,采用相同坐标定义方式。

  下面就是在C++vs完成对图片进行裁剪的语法:

img(Range(start_row, end_row), Range(start_col, end_col))

 

§02 割图片


  应用中,我们时常需要把一个大图分割成很多小的图片。 在OpenCV中提供了这样的方法。 使用一个循环来吧一个大图中的不同部位分割成一些小的图像块。 有限需要获得图片的尺寸以及图像块的大小。

  • Python
img =  cv2.imread("test_cropped.jpg")
image_copy = img.copy() 
imgheight=img.shape[0]
imgwidth=img.shape[1]
  • C++
Mat img = imread("test_cropped.jpg");
Mat image_copy = img.clone();
int imgheight = img.rows;
int imgwidth = img.cols;

  载入宽和高数值,来指明图片中那个范围内需要被剪切下来。 在Python中可以使用 range() 函数。下载吗通过两重循环来完成图像切割。

  1. 第一重循环是在宽度方向上;
  2. 第二重循环是在高度方向上;

2.1 分割图像的代码

  下面我们将图像分割成76×104(宽×高)的图像小块,步长(也就是在图像上移动的像素个数)在内循环和外循环(对应的宽和高)是相同的。

  • Python
M = 76
N = 104
x1 = 0
y1 = 0

for y in range(0, imgheight, M):
    for x in range(0, imgwidth, N):
        if (imgheight - y) < M or (imgwidth - x) < N:
            break
            
        y1 = y + M
        x1 = x + N

        # check whether the patch width or height exceeds the image width or height
        if x1 >= imgwidth and y1 >= imgheight:
            x1 = imgwidth - 1
            y1 = imgheight - 1
            #Crop into patches of size MxN
            tiles = image_copy[y:y+M, x:x+N]
            #Save each patch into file directory
            cv2.imwrite('saved_patches/'+'tile'+str(x)+'_'+str(y)+'.jpg', tiles)
            cv2.rectangle(img, (x, y), (x1, y1), (0, 255, 0), 1)
        elif y1 >= imgheight: # when patch height exceeds the image height
            y1 = imgheight - 1
            #Crop into patches of size MxN
            tiles = image_copy[y:y+M, x:x+N]
            #Save each patch into file directory
            cv2.imwrite('saved_patches/'+'tile'+str(x)+'_'+str(y)+'.jpg', tiles)
            cv2.rectangle(img, (x, y), (x1, y1), (0, 255, 0), 1)
        elif x1 >= imgwidth: # when patch width exceeds the image width
            x1 = imgwidth - 1
            #Crop into patches of size MxN
            tiles = image_copy[y:y+M, x:x+N]
            #Save each patch into file directory
            cv2.imwrite('saved_patches/'+'tile'+str(x)+'_'+str(y)+'.jpg', tiles)
            cv2.rectangle(img, (x, y), (x1, y1), (0, 255, 0), 1)
        else:
            #Crop into patches of size MxN
            tiles = image_copy[y:y+M, x:x+N]
            #Save each patch into file directory
            cv2.imwrite('saved_patches/'+'tile'+str(x)+'_'+str(y)+'.jpg', tiles)
            cv2.rectangle(img, (x, y), (x1, y1), (0, 255, 0), 1)
  • C++
int M = 76;
int N = 104;

int x1 = 0;
int y1 = 0;
for (int y = 0; y<imgheight; y=y+M)
{
    for (int x = 0; x<imgwidth; x=x+N)
    {
        if ((imgheight - y) < M || (imgwidth - x) < N)
        {
            break;
        }
        y1 = y + M;
        x1 = x + N;
        string a = to_string(x);
        string b = to_string(y);

        if (x1 >= imgwidth && y1 >= imgheight)
        {
            x = imgwidth - 1;
            y = imgheight - 1;
            x1 = imgwidth - 1;
            y1 = imgheight - 1;

            // crop the patches of size MxN
            Mat tiles = image_copy(Range(y, imgheight), Range(x, imgwidth));
            //save each patches into file directory
            imwrite("saved_patches/tile" + a + '_' + b + ".jpg", tiles);  
            rectangle(img, Point(x,y), Point(x1,y1), Scalar(0,255,0), 1);    
        }
        else if (y1 >= imgheight)
        {
            y = imgheight - 1;
            y1 = imgheight - 1;

            // crop the patches of size MxN
            Mat tiles = image_copy(Range(y, imgheight), Range(x, x+N));
            //save each patches into file directory
            imwrite("saved_patches/tile" + a + '_' + b + ".jpg", tiles);  
            rectangle(img, Point(x,y), Point(x1,y1), Scalar(0,255,0), 1);    
        }
        else if (x1 >= imgwidth)
        {
            x = imgwidth - 1;   
            x1 = imgwidth - 1;

            // crop the patches of size MxN
            Mat tiles = image_copy(Range(y, y+M), Range(x, imgwidth));
            //save each patches into file directory
            imwrite("saved_patches/tile" + a + '_' + b + ".jpg", tiles);  
            rectangle(img, Point(x,y), Point(x1,y1), Scalar(0,255,0), 1);    
        }
        else
        {
            // crop the patches of size MxN
            Mat tiles = image_copy(Range(y, y+M), Range(x, x+N));
            //save each patches into file directory
            imwrite("saved_patches/tile" + a + '_' + b + ".jpg", tiles);  
            rectangle(img, Point(x,y), Point(x1,y1), Scalar(0,255,0), 1);    
        }
    }
}

2.2 显示和存储分割的图像

  接下来显示这些分割出来的图像小块。 使用 imshow() 函数完成显示。 将图像小块存储磁盘目录中,使用imwrite() 函数。

  • Python
#Save full image into file directory
cv2.imshow("Patched Image",img)
cv2.imwrite("patched.jpg",img)
 
cv2.waitKey()
cv2.destroyAllWindows()
  • C++
imshow("Patched Image", img);
imwrite("patched.jpg",img);
waitKey();
destroyAllWindows();

  下面动图显示了分割图像的过程。

▲ 图2.1  分割图像的过程

▲ 图2.1 分割图像的过程

  图像经过分割后,大体如下图所示:
▲ 图2.2.2  显示了原始图像分割成图像小块

▲ 图2.2.2 显示了原始图像分割成图像小块

▲ 图2.2.2  图像分割后的结果布局

▲ 图2.2.2 图像分割后的结果布局

 

割图像总结 ※


  文中我们讨论了利用 C++, Python对图像进行剪切的基本方法, 这些方法都是应用了对于矩阵的切片操作完成的。指定剪切图片在图像数据矩阵中对应的高、宽的范围,对应范围的数据代表了切割出来的图像。

  通过 imwirte, imshow 可以将切割下的图片进行存储和显示。 后面也进一步讨论了如何对大的图片进行分割,形成许多小的图片的方法。

3.1 网络应用程序

  在网络上我们开发了一个小的应用程序:

  • 通过App你的图片来进行剑桥;
  • 可以指明剪切的尺寸来完成图片的操作。

  这个Streamlit应用程序可以点击这 个链接 来查看。

▲ 图3.1 剪切图片的网络应用程序

▲ 图3.1 剪切图片的网络应用程序


■ 相关文献链接:

● 相关图表链接:

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐