import
and refer to their components using dotted notation.The best way to learn how to program is to start doing something useful, so let's create a thumbnail for an image. We'll start by loading and displaying the original:
In [29]:
from skimage import novice
picture = novice.open('flower.png')
picture.show()
Out[29]:
print its original size:
In [30]:
print 'original size:', picture.size
and then change its size and redisplay it:
In [31]:
picture.size = (100, 100)
picture.show()
Out[31]:
If we just want to show the resized picture, we only need four of the six lines of code we just ran:
from skimage import novice picture = novice.open('flower.png') picture.size = (100, 100) picture.show()
There's a lot going on in those four lines, but once we understand them, and figure out how to avoid making the resized picture look squished, we'll understand about a quarter of what we need to accomplish most routine programming tasks.
The first line of our little program is called an import statement.
It tells Python to find a library called skimage
,
look in it for a sub-library called novice
,
and load that into memory so that we can use the things it contains.
Most programs start this way,
because most of the power of a programming language isn't actually in the language,
but in the libraries written for that language.
(By analogy,
most of the power of a language like English is not in the individual words,
but in the books written in it.)
Here's a simpler example of importing something:
In [32]:
import math
print 'pi is:', math.pi
The first line imports the standard math library,
which contains things like the value of π.
The second line is a print statement:
it tells Python to print the character string
(or "string" for short) 'pi is:'
and the value of π.
`print` automatically puts a single space between the things we ask it to print, and then starts a new line. If we just want to print a blank line, we use `print` on its own. if we don't want to start a new line, we put a comma at the end of the list of things we've asked it to print:
In [33]:
print 'first', # print the word 'first', but don't start a new line
print 'second', # print the word 'second', but don't start a new line
print # just start a new line
As the example above shows, we can put [comments](glossary.html#comment) in our programs to explain what they're doing. A comment starts with a '#' character and runs to the end of the line; Python ignores everything in comments when it's executing code.
Along with useful constants (like π),
libraries can contain functions.
A function is a piece of code that has been given a name
so that we can use it in other programs.
The math
library contains many useful functions,
two of which are shown below:
In [34]:
print 'square root of 5:', math.sqrt(5)
In [35]:
print 'cosine of pi/2:', math.cos(math.pi / 2)
Just as we we referred to π as math.pi
,
we refer to the functions in the math
library using dotted notation
as math.sqrt
(for square root),
math.cos
(for cosine),
and so on.
This helps us keep track of things,
just like calling something "Darwin's notebook" or "Gould's wallet".
If we want to do a little less typing,
we can give the library a nickname (or alias) as we import it:
In [36]:
import math as m
print 'square root of 7:', m.sqrt(7) # instead of math.sqrt
We can even import the things we want for direct use:
In [37]:
from math import sqrt
print 'square root of 9:', sqrt(9) # doesn't use the dot
When we printed the cosine of π/2, we got 6.12323399574e-17 (i.e., roughly 6×10-17) rather than 0. The reason is that the computer can't represent the value of π exactly in a finite number of digits, just as 0.333333333333… doest't capture 1/3 precisely. When we take the cosine of this approximation, we get something that is almost zero, but not quite. Small errors like this crop up in most scientific calculations; we'll look later at ways to handle them.
The second line of our four-line thumbnail program is:
picture = novice.open('flower.png')
At this point, novice.open
should make sense:
we have loaded the novice
sub-library from skimage
(which, by the way, stands for "SciKit Imaging Library"),
and we're using its open
function.
Instead of giving that function a number,
as we did with math.sqrt
and math.cos
,
we're giving it the name of the image file that we want to open
as a character string.
novice.open
finds this file,
copies its contents from disk into memory,
and gives us a reference to that chunk of memory.
We then store that reference in a variable called picture
so that we can do things to it later.
FIXME: diagram
A variable is just a name for a value—something we can use
to refer to that value later in the program.
Python creates a variable whenever we assign a value to a name using =
,
and we can use the variable's value after that
just by using the variable name:
In [38]:
mass = 90
age = 50
name = 'Subject #1'
print name, ':', age, '/', mass
To change a variable's value, we just assign it a new one:
In [39]:
age = 45 # correcting subject age
print name, ':', age, '/', mass
New values can be calculated from old:
In [40]:
mass_pounds = mass * 2.2
print 'mass in pounds:', mass_pounds
We can even calculate a new value based on an old one. In this case, Python reads the values of everything on the right side of the assignment before changing the variable on the left:
In [41]:
mass = mass * 2.2 # converting mass to pounds in place
print name, ':', age, '/', mass
And of course, we can use one variable to calculate a value for another:
In [42]:
ratio = mass / age
print 'pounds per year:', ratio
What we can't do is use a variable that we haven't assigned a value.
For example,
suppose we try to print mas
with one 's' instead of mass
:
In [43]:
print 'Subject mass:', mas # only one 's'
Other languages,
such as Perl,
do let programmers use variables they haven't defined,
and automatically give those variables default values like zero.
This occasionally saves a bit of typing,
but it can also cause hard-to-find errors,
since human eyes can all-too-easily mis-read mas
as mass
or temperatrue
as temperature
.
The table below shows some commonly-used arithmetic operators in Python.
Now that we know what variables and assignment are, we can also understand the rest of our program:
picture.size = (100, 100)
picture.show()
The first of these lines (line 3 of the original program)
assigns (100, 100)
to picture.size
.
The pair of values in parentheses is called a tuple;
like a coordinate pair in mathematics,
it's a single thing with two parts.
And as picture.size
's name suggests,
it is the size of our picture.
When we assign it a new value,
the skimage.novice
library changes the size of the picture as requested.
Finally,
the statement picture.show()
called a method of the picture which,
as its name suggests,
displays the picture.
A method is just a function that's part of a particular value,
just as math.sqrt
is a function that's part of the math
library.
If we had several pictures in memory,
each one would have a show
method that would do the same thing,
but display that particular picture.
Right now, our little program always creates a 100×100 thumbnail, even when the original image wasn't square. Suppose we want to create thumbnails that are always 100 pixels wide, but keep the aspect ratio of the original. If the original was 200×300, for example, our thumbnail would be 100×150, while a 300×200 image would be turned into a thumbnail that was 100 pixels wide and 66&frac23 pixels high.
That &frac23 is our first problem.
There's no such things as a fraction of a pixel,
so we have to decide whether we want to round off to the nearest whole number,
or throw away the fractional part (which in this case means rounding down).
The former seems more sensible,
and Python has a built-in function called round
to round things off,
so we'll do that.
Here's a quick test:
In [44]:
print 'rounding off 2/3 should give 1:', round(2/3)
Whoops: why is the result 0? And why is the result printed as 0.0 rather than 0?
The answer to both questions is that computers can store numbers as integers or floating point numbers (usually just called "floats"). Integers are the familiar values 1, 2, 3, 97, -6, and so on; floats have fractional parts, like 2.5 or -7.172483921748. In many cases, the difference isn't important—2×3 and 2.0×3.0 give the same answer:
In [45]:
print 'using integers:', 2*3
print 'using floats:', 2.0*3.0
However, if we divide one integer by another, Python only keeps the integer portion of the result, so 5/2 is 2 rather than 2.5, and 2/5 is 0 rather than 0.4:
In [46]:
print '5/2:', 5/2
print '2/5:', 2/5
If either argument of the division is a float, on the other hand, Python keeps the fraction when doing the calculation:
In [47]:
print '5.0/2:', 5.0/2
print '2/5.0:', 2/5.0
This is why round(2/3)
produced 0.0:
2/3 is the integer 0,
which round
turned into the float 0.0.
To get the answer we expected,
we can either use constant values:
In [48]:
print 'nearest whole number to 2/3:', round(2.0/3.0)
or use another built-in function called float
to turn one or the other argument of the division into a float:
In [49]:
print 'nearest whole number to 2/3:', round(float(2)/3)
There is another built-in function,
int
,
which turns floats into integers,
but unlike round
,
it truncates (just like division):
In [50]:
print 'int(0.66666666):', int(0.66666666)
There are two reasons why computers have two ways to represent numbers. The first is historical: for several decades, operations on integers were faster than operations on floats, so it made sense to let programmers use whichever they wanted. The other reason is that they really are different: integers are for counting things, while floats are for measuring quantitites.
We now have the tools we need to create proportional thumbnails. Once again, we'll start by loading and displaying:
In [65]:
flower = novice.open('flower.png')
flower.show()
Out[65]:
Notice that we didn't import the novice
library again.
Once it has been loaded into memory,
it is there until we end our session
(e.g., close our IPython Notebook or exit from the command-line interpreter).
Notice also that we called our variable flower
instead of picture
.
We could call the variable anything from a
to zzzzzzzzzzzzzzzzzzzz
—the
computer doesn't care,
and doesn't infer what kind of data a variable refers to from its name—but
using names like picture
and flower
makes it easier for people to understand what we're doing.
Next, let's find out how wide and high our image is:
In [66]:
print 'flower.width:', flower.width
print 'flower.height:', flower.height
If the thumbnail is going to be 100 pixels wide, we need to shrink the picture by a factor of 100/240. We need to use floats for that calculation, though, so we'll write it as 100.0/240. To keep the picture's height proportional, we need to shrink it by the same ratio, which is 180×100.0/240. Here is the calculation and the resulting image:
In [67]:
new_height = 180*100.0/240
flower.size = (100, new_height)
flower.show()
Out[67]:
And to check:
In [68]:
print 'flower.size after thumbnailing:', flower.size
That's great, but we can do better. Our next picture might not be exactly 240 pixels wide and 180 high, so instead of using those values in the calculation, we should use the picture's actual width and height:
In [69]:
flower = novice.open('flower.png') # reload because we changed the size of the previous copy in memory
new_height = flower.height * (100.0 / flower.width)
flower.size = (100, new_height)
flower.show()
Out[69]:
We have now accomplished our original task for one picture,
but what if we had to create thumbnails for a dozen?
Or for several thousand?
Typing in same four lines a thousand times would be very tedious,
and we would almost certainly make a mistake.
In fact,
we did make a mistake several times while writing this lesson:
we called the variable referring to the second copy of the image flower
,
then used picture.width
in the calculation that resized it.
To solve these problems,
we'll need to take a look at how functions are created,
and at how to tell a computer to do something many times over.
import library
, import library as nickname
, and from library import thing
to load code from libraries.variable = value
to assign a value to a variable.variable
if it does not already exist.