关于大程pic-miner的思考

调用图片的尝试及反思

Posted by kamzero on 2019-08-18

关于大程的一些思考

关于图片的尝试

​ 选题之初,我们极少考虑实现的细节,只是想着选一个有趣的题目,就自己找了黄金矿工这个题目。当夏学期开始,我开始思考实现细节,才发现,不规则的矿和长相生动的矿工要求我们必须可以调用图片,自己画的话效果会大打折扣。于是,我开始了调用图片的探索之路。

​ 最初的想法是找库,我试用了xege库,了解了其中调用图片的细节。ege库中可以创建一种PIMAGE类型,有getimage()和putimage()两个函数用于读入图片和贴图。那时,我的想法非常简单和幼稚,想用另一个图形库创建读取图片对象,用libgraphics库创建图形界面,将图片对象输出到lib库创建的图形界面上。助教的一席话点醒了我,lib库没有留相应的接口。且不说我们还遇到了许多问题,比如,ege库要求文件名后缀必须是.cpp,而lib库要求项目类型必须是c,否则就好出现bool类型的重定义问题。总而言之,这条路便走不通了。现在看来,当初放弃得有些早,后来了解到一点计算机图形学和windows图形编程,以及openGL库,在图书馆找到了相关书籍,发现绘图问题还是有很多种解决方案的。

​ 后来在自定义颜色时,我发现DefineColor()函数是按RGB格式来定义颜色的,我就有了新的想法,既然大多数图片中信息都是按一个个像素点来存储的 ,那么,我们在画图的时候,也可以一个一个像素点画上去。我当时接触了openCV,就写了一段python代码读取png格式图片中每一个像素点的bgr信息,存储到一个txt文件中。(很多语言和软件都可以实现这一过程)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import cv2
import numpy
import pylab
import matplotlib.pyplot as plt

img = cv2.imread("4.png")#+imgfile,cv2.IMREAD_GRAYSCALE D:\CS\项目-goldminer\图片\
print("图像的形状,返回一个图像的(行数,列数,通道数):",img.shape)
print("图像的像素数目:",img.size)
print("图像的数据类型:",img.dtype)
#----------------------------------------------------------------------------
"""
In windows the COLOR->GRAYSCALE: Y = 0.299R+0.587G+0.114B 测试是否三个通道的值是相同的。
某些图像三通道值相同,可以直接读灰度图来代替读单一通道。
"""
# sum = 0
# ans = 0
# for i in range(562):
# for j in range(715):
# if not(img[i][j][0] == img[i][j][1] and img[i][j][1] == img[i][j][2]):
# sum += 1
# else:
# ans += 1
# print(ans)
# print(sum)
#-----------------------------------------------------------------------------
"""
将图片数据写入txt文件
格式:
基础信息
行号:
像素值
行号:
像素值
......
"""
fname = open("4.txt",'w')
fname.write(str(img.shape)+'\n')#----1
#fname.write("图像的像素数目:"+str(img.size)+'\n')#----2
#fname.write("图像的数据类型:"+str(img.dtype)+'\n')#----3
Xlenth = img.shape[1]#图片列数
Ylenth = img.shape[0]#图片行数
a = 1#----4
for i in range(Ylenth):
#fname.write(str(a) + ':'+'\n')#----5
for j in range(Xlenth):
fname.write(str(img[i][j][0])+' ')
fname.write(str(img[i][j][1])+' ')
fname.write(str(img[i][j][2])+' ')
a += 1#----6
#fname.write('\n')
fname.close()
#---------------------------------------------------------------------------
"""
将txt文件中的数据读取进blist
并显示为"test"图片框进行测试。
注意进行测试前需要注释掉数据写入模块
中标记的六行代码,要不然读取会出错误。
"""
# blist = []
# split_char = ' '
# with open('C:/Users/Jake/Desktop/test01/'+txtfile, 'r') as bf:
# blist = [b.strip().split(split_char) for b in bf]
#
##从txt文件读入进来的值类型是char需要转换为int
# for i in range(Ylenth):
# for j in range(Xlenth):
# blist[i][j] = int(blist[i][j])
#
# tlist = numpy.array(blist)
# plt.figure()
# plt.imshow(tlist)
# plt.axis('off') # 不显示坐标轴
# pylab.show()
#------------------------------------------------------------------------------
"""
将图片显示在'image'图片框
"""

cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#----------------------------------------------------------------------

​ 又在大程项目中封装了两个函数,drawDot和drawPic,drawDot的功能是画出一个指定颜色的像素点,drawPic的功能是逐一读取文件中每一个像素点的RGB信息,定义当前颜色并画出像素点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
* 函数名:DrawDot
*
* 用法:DrawDot(red,green,blue,x,y,size,n)
*
* 功能:在(x,y)处画一个边长为size的正方形。
*
* 备注:当size很小时,视为一个点。它的颜色是(R,G,B)。
*
*/
void DrawDot(double red,double green,double blue,double x,double y,double size)
{
static flag=0;
switch (flag){
case 0:{
flag++;
break;
}
case 1:{
flag++;
break;
}
case 2:{
flag=0;
break;
}
}
static char *a[]={"a","b","c"};
DefineColor(a[flag],red,green,blue);
SetPenColor(a[flag]);



StartFilledRegion(1);
MovePen(x,y);
DrawLine(size,0);
DrawLine(0,size);
DrawLine(-size,0);
DrawLine(0,-size);
EndFilledRegion();

}
/* 函数名:DrawPic
*
* 用法:DrawPic(name,x,y)
*
* 功能:以(x,y)为左上角画一个图片,这个图片的名字为name
*
* 备注:
*
*/
void DrawPic(char a[],double x,double y){

freopen(a,"r",stdin);
int i,j;
int width,height;
int num=0;
int degree;
int red_,green_,blue_;
scanf("(%d,%d,%d)",&width,&height,&degree);
for( i = 0 ; i < width ; i ++ )
{
for ( j = 0 ; j < height ; j ++ )
{
scanf("%d",&blue_);
scanf("%d",&green_);
scanf("%d",&red_);
DrawDot(red_/255.0,green_/255.0,blue_/255.0,x+i*0.01,y-j*0.01,0.01);
}
}

}

​ 其中也遇到一些困难,我还去研究过lib库中几个函数的实现方式。遇到最大的问题就是,我在重定义已有颜色时,lib库中colorTable中三种色度存储的值会改变,而画出了的颜色却不会改变,这令我非常郁闷。重定义颜色非常重要,否则,一张图片少说也有几万个像素点,每一个像素点定义一种新颜色,占有内存会非常大。以及,lib库中每次更改颜色都用的是一个名为FindColorIndex的线性搜索函数,如果不能重定义颜色,程序会越跑越慢。lib库的实现部分在我看来写得并不是很优雅,留的接口不多,改动空间不大。其中画颜色的部分竟全部是由一个PenColor的int类型全局变量来控制,而PenColor的值是cindex赋的,意义是在当前颜色在colorTable中的下标。和两位队友讨论后,我们猜想是绘图部分在判断PenColor和oldColor相等后,即当前颜色在颜色表中的下标等于旧颜色的下标时,就不对画图颜色进行更改,不管此时colorTable中的RGB值是否改变。解决方案有二,一改库,二时在不用一个颜色来存当前画图颜色,二是采取寄存器或者缓存的思想,开一个小数组或者多设几个变量来存储当前颜色,避免重定义后立即调用的问题。我们采取了后者的思想,CBY同学改写并封装了DrawPic函数,问题得到了解决,可以优雅地在lib界面上绘制图片。

​ 最终由于读取图片速度不够快,而我们的程序又采用的是整体刷新,我们摒弃了调用图片的想法,转而把注意力放在了画图优化界面上。

展示课上学到的事

​ 我们组大概是只用lib库的组里做得比较好的,被迫需要进行展示。展示课上了解情况后,我发现有好几组和我们一样苦于难以调用图片,甚至同班同学那组因此直接用了C++来写,被助教打回去改成C。在展示课上,至少学到了两种不同于像素点绘制的调用图片方法,比如,把图片接到缓存里即可展示,甚至,这个库在被那位斯坦福教授创造之初,就是有调用图片的函数的,只是ZJU用于教学时删掉了,或是在流传中遗失了(我相信是前者)…正如点出这一点的男孩子所说,我们不光要会用这个库,还要知道它从哪里来,以及如何实现。确实,满脑子骚操作试图改库的那几天的快乐不亚于最初在体育馆边看球边构架起整个程序时的喜悦。

​ 少乱想,多折腾。