图片处理(Picture Lab)活动5:修改图片
尽管数字图片有以百万计的像素,现代电脑的运行速度已经足够快,能够在一瞬间将它们全部处理完。你将在class Picture中编写处理数字图片的方法。class Picture继承了class SimplePicture,而后者又实现了interface DigitalPicture。它们之间的关系由下面的统一模型语言(Unified Modeling Language, UML)的图示所表示。

UML图示显示class间的关系。每个class表示为方框,而最顶部是class名。中间部分是class中的field,而最底下显示class的各个method。三角箭头表示继承关系,由child class指向parent class。直线表示组成关系(“has-a” relationship),数字则表示有多少个object。比如上图中,SimplePicture可以包含一至多个Pixel,而一个Pixel里只包含一个Color。UML独立于具体的编程语言,因而其method的写法和Java签名有一定的区别。
问题
- 打开
Picture.java并查找getPixels2Dmethod。 - 打开
SimplePicture.java并查找getPixels2Dmethod。 - 以下代码能通过编译么?
DigitalPicture p = new DigitalPicture(); - 假定
SimplePicture有默认构造函数,以下代码能通过编译么?
DigitalPicture p = new SimplePicture(); - 假定
Picture有默认构造函数,以下代码能通过编译么?
DigitalPicture p = new Picture(); - 假定
Picture有默认构造函数,以下代码能通过编译么?
SimplePicture p = new Picture(); - 假定
SimplePicture有默认构造函数,以下代码能通过编译么?
Picture p = new SimplePicture();
DigitalPicture是一个interface。一般而言,interface只有public abstract的method。abstract method通常没有具体实现,你可以观察到DigitalPicture中的所有method都没有任何Java语句来描述它们的执行逻辑。没有实现的method有什么用处呢?
通过interface,我们能把什么和如何区分开来。interface确定某个类型的object能做什么,但不管它如何做到这一点。你并不能直接把interface实例化,不过某个class可以实现interface,就像SimplePicture所做的那样。非abstract的class会为interface中声明的所有method提供具体实现。这可以是直接编写,也可通过继承的方式。你可以将变量声明为interface的类型,再给它赋值成任意一个实现了该interface的class的object。例如,Java有名为List的interface,声明List应该有add、remove和get等method。但是一个List的object在AP计算机中则会是ArrayList。
List<String> nameList = new ArrayList<String>();
为什么你不直接将nameList声明为ArrayList呢?事实上,Java中还有List的其他不同实现。将nameList定义为List而非ArrayList,能够方便你在以List为参数的函数中使用它,以及你的代码在未来转换到List的另一个实现上。只要你的代码仅利用到interface本身定义的一系列功能,interface就能给开发带来灵活性,减小在未来可能需要的代码修改。
因为DigitalPicture定义了一个getPixels2D method以返回Pixel object的二维数组,SimplePicture实现了这个interface而Picture又继承了SimplePicture,你可以直接在Picture object上调用getPixels2D 。之后,你就能通过对二维数组进行循环操作每个Pixel的方式来修改图片了。你既可以设置每个Pixel的红、绿和蓝色值,也可以一次读取或修改整个Color。你可以使用Color的构造函数,通过提供红、绿和蓝色值的方式来创建新Color object。
Color myColor = new Color(255,30,120);
你觉得把images目录下的海滩图片的所有蓝色值都清零之后会看到什么?觉得还能看到海滩么?运行class Picture的main method。其会从名为beach.jpg的图像文件读取数据并创建对应的Picture object,在探索窗口中显示(内存中的)图片,调用清零蓝色值的method,再另开一个探索窗口显示处理结果。
class Picture的main method由以下代码所示:
public static void main(String[] args)
{
Picture beach = new Picture("beach.jpg");
beach.explore();
beach.zeroBlue();
beach.explore();
}
练习
- 打开
PictureTester.java并运行其mainmethod。你应该得到和运行class Picture的mainmethod一致的结果。class PictureTester包含测试class Picture的staticmethod。 - 将
PictureTester里mainmethod中的其他语句取消注释,测试Picture.java的其他method。对于不想运行的测试可以再度注释掉。你还可以在PictureTester添加针对你自己在Picture里新编写的method的测试。
class Picture的zeroBlue method从当前图片中获取对应的Pixel object的二维数组。接下来,它使用嵌套for-each循环遍历图片的每个像素。在内层循环中,当前像素(定义的名为pixelObj的Pixel object)的蓝色值被设为零。注意for-each循环不能改变数组本身的值,但对于对象数组,可以改变每个对象的内部状态。
class Picture的zeroBlue method由以下代码所示:
public void zeroBlue()
{
Pixel[][] pixels = this.getPixels2D();
for (Pixel[] rowArray : pixels)
{
for (Pixel pixelObj : rowArray)
{
pixelObj.setBlue(0);
}
}
}
练习
- 以
zeroBlue方法为起点,编写只保留蓝色值,即清零红色和绿色值的keepOnlyBluemethod。在class PictureTester中创建一个测试新method的staticmethod,并确保在PictureTester的mainmethod中调用它。 - 编写
negatemethod来将图片中的所有像素取反色,即把红色值设为255减去原红色值,绿色值设为255减去原绿色值,蓝色值设为255减去原蓝色值。在class PictureTester中创建一个测试新method的staticmethod,并确保在PictureTester的mainmethod中调用它。 - 编写
grayscalemethod来将图片转换为灰度,即将红、绿和蓝色值均设为它们三者的平均值(求和之后除3)。在class PictureTester中创建一个测试新method的staticmethod,并确保在PictureTester的mainmethod中调用它。 - 挑战——探索
images目录下的water.jpg图片。编写fixUnderwatermethod来修改像素颜色,使得鱼能够更清晰可见。在class PictureTester中创建一个测试新method的staticmethod,并确保在PictureTester的mainmethod中调用它。
0 条评论