当前位置: 首页 > 后端技术 > Python

用python在excel中画图的实现方法

时间:2023-03-25 19:47:34 Python

一、前言大学的时候学过EXCEL,看到有多少高手用excel画图,觉得不可思议。今天学了一个月的python,膨胀了就想用excel画图。当然,实用绘图这个词并不是很严谨。其实就是用opencv遍历每个像素的rgb值,然后转成16进制,最后调用openpyxl进行填充。1.1.效果如图1.2所示。要用到的库安装如下:importcv2#导入OpenCV库importxlsxwriter#用这个调整行高和列宽importopenpyxl#用这个填充颜色importnumpyasnp#下面两种是数据存储的两种方式。使用这种方式处理数据比列表更高效。具体可以查看文档importpandasaspd。除了第一个库,您可以直接在命令提示符下使用pip安装它。或者使用编辑器的一些自动安装功能也很方便。详情请参考本文第三节。如果你直接用pip3installopencv-python安装第一个库,不管你的网速多快,也会很慢几k/s,如下:如果能安装就没问题,还有关键可能等几分钟不行,直接出现几十行红字,看的头疼。百度了几次,发现是安装源的问题。切换到国内的安装源就可以了。使用如下命令:pip3install-ihttps://pypi.tuna.tsinghua.ed...opencv-python如下图所示,我将截取的安装速度与上面进行对比,结果直接安装了。二、代码单独解释。在本文中,我们使用面向对象的编程思想。2.1.对象定义和初始化类ImageToExcel():def__init__(self,image_path,excel_path):self.imgviewx=cv2.imread(image_path,cv2.IMREAD_COLOR)self.excel_path=excel_path前两行定义对象很好理解格式和初始化对象。image_path和excel_path这两个变量就是你的图片存放路径和后续的excel文件存放位置。第三行self.imgviewx=cv2.imread(image_path,cv2.IMREAD_COLOR)表示调用opencv的imread读取图片。第一个参数是实例化对象时传入的图片存放路径。该函数返回一个三维数组,意思是x,y,rgb是x,y坐标对应的rgb值,其中x,y的单位是1像素。最后将这个三维数组传递给对象的一个??属性imgviewx,等待后续的对象方法调用。我们打印出来如下。第四行<>是将对象实例化时传入的excel_path传递给对象的属性excel_path,同时也等待后续对象的方法调用。2.2.对象方法一:调整行高列宽防止图片变形#excel行高列宽调整defexcel_size(self):workbook=xlsxwriter.Workbook(self.excel_path)worksheet=workbook.add_worksheet('test')worksheet.set_column('A:CAA',1)forxinrange(2000):worksheet.set_row(x,8.4)workbook.close()其实后面在excel里面可以调整。第二行和第三行基本上一看就明白了。就是在第一次实例化对象时传入的路径中创建一个工作簿,并添加一个名为test的工作表。第三行意思是把A列到CAA的列宽设置为1(注:不知道为什么设置为1的时候工作表里是0.94,而且列宽也变小了)。第四行同义,但行高只能通过循环不能批量处理。最后一个貌似关闭了,但主要功能是保存。没有这一行,所有以前的设置都不会被保存。2.3.对象方法二:十进制转十六进制#十进制转十六进制deften2_16(self,num):num1=hex(num).replace('0x','')returnnum1iflen(num1)>1else'0'+num1这个方法不用说了,就是利用系统自带的函数hex,将十进制转为十六进制。我们都知道hex返回的十六进制是以0x开头的,但是在十六进制色码中明显不在,所以用replace去掉。如果rgb值在16以内,16进制显示就是1位,16进制色码也一样,所以如果最后一行是个位数,就在开头加0。2.4、对象的方法三:获取r、g、b值并使用方法一将其转化为十六进制颜色代码#获取像素数据并转化为十六进制defget_rgb_data(self):self.excel_size()data_r=pd.DataFrame(np.array(self.imgviewx)[:,:,2]).applymap(self.ten2_16)data_g=pd.DataFrame(np.array(self.imgviewx)[:,:,1]).applymap(self.ten2_16)data_b=pd.DataFrame(np.array(self.imgviewx)[:,:,0]).applymap(self.ten2_16)return(data_r+data_g+data_b).values其中第二个rowself.excel_size()调用该方法时,先调用方法1调整行高和列宽。后面会说,这个跟对象方法之间的参数传递有关,后面会说。三四五行的代码结构是一样的,我们挑一个吧。比如第三行data_r=pd.DataFrame(np.array(self.imgviewx)[:,:,2]).applymap(self.ten2_16)这段代码可以拆解成如下代码:r=np.array(self.imgviewx)[:,:,2]tmp=pd.DataFrame(r)data_r=tmp.applymap(self.ten2_16)这个很好理解。第一行的意思是将对象初始化开始时得到的包含目标图像所有像素点rgb值的三维列表转化为数组,从中提取r。第二行是将第一行得到的数组转化为DataFrame对象,存放到tmp变量中,供第三行处理。第三行是使用DataFrame中的applymap将r值转为十六进制。最后一行return(data_r+data_g+data_b).values的意思是将rgb值组合成16进制后,得到16进制颜色码,转成数组。2.5.对象方法4:颜色填充defcolor_fill(self):rgb_list=self.get_rgb_data()wb=openpyxl.load_workbook(self.excel_path)ws=wb['test']forx,tmp1inlist(enumerate(rgb_list)):print('总共有%s行,已经填充了%s行,还剩%s行'%(len(rgb_list),x+1,len(rgb_list)-x-1))fory,tmp2在列表中(枚举(tmp1)):ws.cell(x+1,y+1??).fill=openpyxl.styles.fills.GradientFill(stop=[str(tmp2),str(tmp2)])wb.save(self.excel_path)第二行rgb_list=self.get_rgb_data()很熟悉,没错,是在方法2中调用方法1的时候用到的。这里就是在这个方法中调用方法2,也就是方法3。唯一不同的是是否有返回值。我们在方法3中调用方法2,然后在方法2中调用方法1。这样,当我们在对象外部时,只需要实例化对象,调用方法3即可实现功能。第三、四行是调用openpyxl.load_workbook在方法一创建的工作簿中打开测试工作表。五七行两层循环嵌套很容易理解,就是用循环遍历每个工作表。第八行的代码可以简化。这是网上修改的填充一个渐变色的代码。最后一行是工作表的保存,没什么好说的。3.完整代码importcv2#导入OpenCV库importxlsxwriter#用这个调整行高和列宽importopenpyxl#用这个填充颜色importnumpyasnp#下面两种是数据存储的两种方式,用这个方法处理数据,比列表更有效高度和列宽调整defexcel_size(self):workbook=xlsxwriter.Workbook(self.excel_path)worksheet=workbook.add_worksheet('test')worksheet.set_column('A:CAA',1)forxinrange(2000):worksheet.set_row(x,8.4)workbook.close()#rgb到十六进制颜色代码deften2_16(self,num):tmp=hex(num).replace('0x','')returntmpiflen(tmp)>1else'0'+tmp#获取像素数据并转化为十六进制defget_rgb_data(self):self.excel_size()data_r=pd.DataFrame(np.array(self.imgviewx)[:,:,2])。申请map(self.ten2_16)data_g=pd.DataFrame(np.array(self.imgviewx)[:,:,1]).applymap(self.ten2_16)data_b=pd.DataFrame(np.array(self.imgviewx)[:,:,0]).applymap(self.ten2_16)返回(data_r+data_g+data_b).values#colorfillingdefcolor_fill(self):rgb_list=self.get_rgb_data()wb=openpyxl.load_workbook(self.excel_path)ws=wb['test']forx,tmp1inlist(enumerate(rgb_list)):print('有%s在总行中,已填充%s行,剩余%s行'%(len(rgb_list),x+1,len(rgb_list)-x-1))fory,tmp2inlist(enumerate(tmp1)):ws.cell(x+1,y+1).fill=openpyxl.styles.fills.GradientFill(stop=[str(tmp2),str(tmp2)])wb.save(self.excel_path)excel_path='test23.xlsx'image_path='tttt.png'image=ImageToExcel(image_path,excel_path)image.color_fill()最后四行和前两行可以直接写在第三行,这是实例化的另一个点object,image_pathtttt.jpg直接和我的py文件放在一起,否则运行会报错。这部分在抽取中的个数刚好相反,抽取时需要注意。如下:当然,你也可以尝试改变这个值,看看最后会得到什么结果。蓝色的太阳、红色的天空或绿色的帽子,这取决于你。其次,除了以上几点需要注意外,还有一点需要注意,那就是像素不能太高。如果我测试的是342*218,我的i7-6700u打开excel时不会很流畅。遍历的时候可以使用2像素或者4像素作为步长,但是这个我没试过,可能颗粒感比较明显(估计没试过),或者修改原图。