Muhammad Ali wrote:
Hi,

This is my first attempt at python. I tried my hand at steganography for
fun. The code includes separate, combine, encode, decode functions. The
separate function takes a number and it's base and returns a list containing
each digit of the number in a list, vice versa for combine. The encode
function encodes each char's ascii value into each pixel's RGB values at the
least significant end. The decode function does the opposite. e.g.,

a = 97, so if we have at a pixel r, g, b (230, 107, 155) it will become
(230, 109, 157) when it's encoded.

Since I am new and don't speak the python idiom yet, I would like the
experts to pythonify the following code as much as possible for both python
2.6 and 3.0. Any other suggestion to improve the quality would also be
highly appreciated

Thanks a lot!


#The code starts here:

def separate(num, base):
    li = []
    while num / base > 0:
        li.insert(0, num % base)
        num = num / base

    li.insert(0,num)
    return li

def combine(tup, base):
    num = 0
    mul = pow(base, len(tup) - 1)
    for i in tup:
        num = num + i * mul
        mul = mul / base

    return num


#Will make changes to the original image and not to a copy! You have been
warned!
import Image

def encode(img, text):
    x = 0
    y = 0
    height, width = img.size

    text = text + "~~"

    if len(text) > height * width:
        return false

    pix = img.load()

    for c in text:
        li = separate(ord(c), 10)

        if(len(li) == 1):
            li.insert(0, 0)
            li.insert(0, 0)
        elif(len(li) == 2):
            li.insert(0, 0)

        r, g, b = pix[x, y]

        r = r - (r % 10) + li[0]
        if (r > 255):
            r = r - 10

        g = g - (g % 10) + li[1]
        if (g > 255):
            g = g - 10

        b = b - (b % 10) + li[2]
        if (b > 255):
            b = b - 10

        pix[x,y] = (r,g,b)

        if y == width - 1:
            y = 0
            x = x + 1
        else:
            y = y + 1

    img.save(img.filename)

def decode(img):
    x = 0
    y = 0
    text = ""
    c = ""
    height, width = img.size
    pix = img.load()
    while 1:
        r, g, b = pix[x, y]
        if (c == '~') and chr(combine([r % 10, g % 10, b % 10], 10)) == '~':
            return text[:len(text) - 1]

        c = chr(combineUnits([r % 10, g % 10, b % 10], 10))
        text = text + c

        if y == width - 1:
            y = 0
            x = x + 1
        else:
            y = y + 1

Pretty good for your first Python code. Clearly you have some experience in other language(s). Comments follow, no particular order.

Import should be at begin of module, unless it's just plain not possible. Functions should have doc-strings, explaining what they do, and what the arguments look like. For example, first two functions wouldn't work with floats, only ints. There also should be comments stating what ranges of values are legal. For example, num is a positive integer, and base is an integer between one and 250 or so.

separate() could be simplified and optimized, some depending on the range of expected parameters, but below we'll see that it should be replaced entirely. So don't optimize prematurely.

separate() and combine() both naturally want to do the digits in the oppposite order, and nothing else cares. But again, it's premature optimization.

Looking at encode(), we see that the only call to separate() is with 10 as a base. And it immediately takes the nicely formatted number, and pads it to 3 digits. Sounds like a standard formatting operation to me, so why not just use the format method with a 3 for width? Then loop over the string, or even use a list comprehension on it.

encode() refers to }false", but the built-in value is called "False"

encode() uses repetitive code to combine two tuple/lists. Ideal for a separate function, perhaps using zip(). Then we could replace 14 lines with:

       pix[x, y] =pixel(pix[x, y], li)

in encode(), row/column incrementing could be simplified, and common case sped up:

       y += 1
       if y == width:  y, x = 0, x+1

Incidentally, aren't you processing the pixels in column-major order? Doesn't matter much for tiny images, but for symmetric designs, it's usually better to keep memory accesses localized, to reduce swapping. I could be all wet here, though, just going on memory.

decode() refers to combineUnits() in one of its two calls, but it's actually called combine(). Further, there's no need to call it twice. Better would be to have two "c" variables, maybe calling one of them 'lastc'

       lastc = c
       c = chr(combineUnits([r % 10, g % 10, b % 10], 10))
       if (c == '~') and lstc == '~':
           return text[:len(text) - 1]

In decode(),  while 1    is usually spelled:    while True

Hope these help.
DaveA

_______________________________________________
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor

Reply via email to