[Gimp-developer] McCann Retinex plugin with python

2007-09-15 Thread John Fremlin
Yesterday I decided to implement the retinex algorithm described by
John McCann in 1999 as a gimp plugin.

I am using Python (in particular numpy for the main calculations) and
consequently chose to put in some modifications to the algorithm to
make it more efficient, but it makes generally the same effect as that
described in

   Brian Funt, Florian Ciurea, and John McCann Retinex in Matlab, Proceedings 
of the IST/SID Eighth Color Imaging Conference: Color Science, Systems and 
Applications, 2000, pp 112-121.

   http://www.cs.sfu.ca/~colour/publications/IST-2000/index.html

As this is my first foray into gimp plugging in, I'd appreciate if
someone could look over the code. Is there a more efficient way of
getting out one colour channel of the image at a time? At the moment I
read in the whole image which takes a lot of memory.

Searching the archives today I notice that Pedro Paf was suggesting
implementing it as a Summer of Code project - as the algorithm is very
simple (a day to make even starting no numpy or Gimp python
knowledge), what enhancements where being contemplated?

#! /usr/bin/env python

#   Implementation of a retinex algorithm similar to that described by
#   John McCann in 1999

#   For more information about the algorithm see http://www.cs.sfu.ca/~colour/publications/IST-2000/index.html
#   Brian Funt, Florian Ciurea, and John McCann Retinex in Matlab, Proceedings of the IST/SID Eighth Color Imaging Conference: Color Science, Systems and Applications, 2000, pp 112-121.

#   Copyright (C) 2007 John Fremlin
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import gimp
from gimpfu import *

import numpy
import scipy
import scipy.ndimage
import struct
import Image

gettext.install(gimp20-python, gimp.locale_directory, unicode=True)

small_amount = 1/1024.0
difference_from_neighbours_kernel = numpy.array([ [-1, -1, -1], [-1,8,-1], [-1,-1,-1]],d)
global_logscale = True

def shrink(chan,scale):
return scipy.ndimage.zoom(chan,1/float(scale),prefilter=False,order=5)

def image_clip(chan):
if global_logscale:
return chan.clip(-numpy.inf,0.0)
else:
return chan.clip(0.0,1.0)

def retinex_at_scale(retinex,orig,scale):
assert(orig.size == retinex.size)
working = orig
diff = scipy.ndimage.convolve(working,difference_from_neighbours_kernel)
result = (retinex + diff)/2
working = (retinex + image_clip(result))/2

return working

def resize(chan,new_size):
orig = chan.shape
zoom = [((new+0.9)/float(old)) for old, new in zip(orig, new_size)]
ret = scipy.ndimage.zoom(chan,zoom,prefilter=False,order=5)
assert(new_size == ret.shape)
return ret

def process_one_channel(chan):
retinex = numpy.array([[chan.mean()]],d)

for logscale in range(int(numpy.log2(min(*chan.shape))),-1,-1):
scale = 1  logscale
orig = shrink(chan,scale)
retinex = retinex_at_scale(resize(retinex,orig.shape),orig,scale)
return retinex
#return numpy.abs(chan-retinex)

def retinex(image):
if global_logscale:
image = numpy.log(image+small_amount)


[ width, height, channels ] = image.shape
for c in range(channels):
gimp.progress_update(c/channels)
image[:,:,c] = process_one_channel(image[:,:,c])

if global_logscale:
image = numpy.exp(image)-small_amount

return image

def progress_update(stage,proportion_done):
gimp.progress_update(proportion_done*0.10)

def read_in(drawable):
width = drawable.width
height = drawable.height
bpp = drawable.bpp
pr = drawable.get_pixel_rgn(0, 0, width, height, False)
# image = numpy.zeros((height,width,3),d)
# for y in range(height):
# for x in range(width):
# image[y,x,:] = struct.unpack('BBB',pr[x,y][0:3])

# progress_update(read-in, (y / float(height)))

a = numpy.fromstring(pr[:,:],B)
assert(a.size == width * height * bpp)
image = numpy.array(a.reshape(height,width,bpp),d)[:,:,0:min(bpp,3)]
return image/256.0



def write_out(drawable,image):
byte_image = numpy.array((image*256).round(0),B)
width = drawable.width
height = drawable.height
bpp = drawable.bpp

pr = drawable.get_pixel_rgn(0, 0, width, height, True)
assert(byte_image.size == width * height * bpp)
pr[:,:] = 

Re: [Gimp-developer] McCann Retinex plugin with python

2007-09-15 Thread David Gowers
On 9/15/07, John Fremlin [EMAIL PROTECTED] wrote:
 Yesterday I decided to implement the retinex algorithm described by
 John McCann in 1999 as a gimp plugin.

 I am using Python (in particular numpy for the main calculations) and
 consequently chose to put in some modifications to the algorithm to
 make it more efficient, but it makes generally the same effect as that
 described in

Brian Funt, Florian Ciurea, and John McCann Retinex in Matlab, 
 Proceedings of the IST/SID Eighth Color Imaging Conference: Color Science, 
 Systems and Applications, 2000, pp 112-121.

http://www.cs.sfu.ca/~colour/publications/IST-2000/index.html

 As this is my first foray into gimp plugging in, I'd appreciate if
 someone could look over the code. Is there a more efficient way of
 getting out one colour channel of the image at a time? At the moment I
 read in the whole image which takes a lot of memory.

plug_in_decompose decomposes the image into a layer per channel.


 Searching the archives today I notice that Pedro Paf was suggesting
 implementing it as a Summer of Code project - as the algorithm is very
 simple (a day to make even starting no numpy or Gimp python
 knowledge), what enhancements where being contemplated?

I find this odd -- your whole email odd, in fact, because -- there is
already a retinex plugin (found at Colours-Retinex;
plug-ins/common/retinex.c in the GIMP source tree.).  If you want to
add some enhancements, perhaps you could check that out first.
___
Gimp-developer mailing list
Gimp-developer@lists.XCF.Berkeley.EDU
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gimp-developer


Re: [Gimp-developer] McCann Retinex plugin with python

2007-09-15 Thread John Fremlin
David Gowers [EMAIL PROTECTED] writes:
[...]
 plug_in_decompose decomposes the image into a layer per channel.

Thanks

 Searching the archives today I notice that Pedro Paf was suggesting
 implementing it as a Summer of Code project - as the algorithm is very
 simple (a day to make even starting no numpy or Gimp python
 knowledge), what enhancements where being contemplated?

 I find this odd -- your whole email odd, in fact, because -- there is
 already a retinex plugin (found at Colours-Retinex;
 plug-ins/common/retinex.c in the GIMP source tree.).  If you want to
 add some enhancements, perhaps you could check that out first.

The retinex plugin is not documented. (As far as I can see?) What do the
different parameters to it mean? Even after looking at the paper it is
supposedly based on (multsclrtx.ps) I don't understand the
parameters. There are many different retinex algorithms based on
widely different principles; it is true that this one and the McCann
one are similar in some respects.

Obviously, I experimented with it a few times, but I couldn't get it
to work before I gave up.

The objective of my implementation of is to brighten dark areas of the
image while preserving detail, and it has a very noticeable effect.

Compare the outputs. Probably the best way to use my code is to select
the non-flatten option and adjust the opacity of the retinex layer
afterwards.
___
Gimp-developer mailing list
Gimp-developer@lists.XCF.Berkeley.EDU
https://lists.XCF.Berkeley.EDU/mailman/listinfo/gimp-developer