views:

299

answers:

2

Hi all,

I have written the following IronPython code:

import clr
clr.AddReference("System.Drawing")
from System import *
from System.Drawing import *
from System.Drawing.Imaging import *

def NoWorko(bitmap):
    bmData = bitmap.LockBits(Rectangle(0, 0, bitmap.Width, bitmap.Height), 
        ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb)

    total_bytes = (bmData.Stride) * bmData.Height
    rgbValues = Array.CreateInstance(Byte, total_bytes)
    Runtime.InteropServices.Marshal.Copy(bmData.Scan0, rgbValues, 0, total_bytes)
    for i in rgbValues:
        i = 255 - i

    #The following line doesn't appear to actually copy the bits back
    Runtime.InteropServices.Marshal.Copy(rgbValues, 0, bmData.Scan0, total_bytes)
    bitmap.UnlockBits(bmData)

originalImage = Bitmap("Test.bmp")
newPic = NoWorko(originalImage)
newPic.Save("New.bmp")

Which is my interpretation of this MSDN code sample: http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx except that I am saving the altered bitmap instead of displaying it in a Form.

The code runs, however the newly saved bitmap is an exact copy of the original image with no sign of any changes having occurred (it's supposed to create a red tint). Could anyone advise what's wrong with my code?

The image I'm using is simply a 24bpp bitmap I created in Paint (it's just a big white rectangle!), using IronPython 2.6 and on Windows 7 (x64) with .Net Framework 3.5 SP1 installed.

Update

My foolishness has been pointed out in trying to add a red tint to a white image - so now the code simply inverts the colours. I've tried this on a number of images, but it just doesn't seem to have any effect.

However, the following (very similar) C# program:

public static void Main(string[] args)
        {
            Bitmap bitmap = new Bitmap("nTest.jpg");
            BitmapData bmData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), 
                    ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int total_bytes = (bmData.Stride) * bmData.Height;
            byte[] rgbValues = new byte[total_bytes];

            Marshal.Copy(bmData.Scan0, rgbValues, 0, total_bytes);

            for(int i = 0; i < total_bytes; i++)
            {
                rgbValues[i] = (byte)(255 - rgbValues[i]);
            }

            Marshal.Copy(rgbValues, 0, bmData.Scan0, total_bytes);
            bitmap.UnlockBits(bmData);

            bitmap.Save("nNew.jpg");

        }

Worked on all the images I've tried.

I'm not sure, but it seems to be something to do with the call to:

Runtime.InteropServices.Marshal.Copy(rgbValues, 0, bmData.Scan0, bytes)

in IPY that is causing the problem.

+1  A: 

I don't know Python, so I have no idea how

for i in rgbValues[::3]: 
        i = 255 

is supposed to work. But provided that it actually sets each third byte to 255 you have two problems:

  1. You cannot give the color white a red tint by setting it's red component to 255, since it already has that value. You have to decrease the blue and green components instead.
  2. You cannot write code that tries to alter all the pixels of a bitmap at once, when your format is 24bpp (it would work with 32bpp). You must do it scan line by scan line.
danbystrom
1 - Excellent point, idiocy on my part viz the red component. However, I've tried it on other images and it doesn't work on those either. I'll update my post.2 - Not sure about this - the same code does work in C# against the same images...
Leonard H Martin
1. Have you made sure that your code actually alters the vector? Provided that you didn't notice that that you just tried to change 255 to 255 I guess that you haven't... :-) 2. I guess it will work if the image has a width that is a multiple of four. Ckeck out the docs for the .Stride property.
danbystrom
Think of it more of an idiotic example :) What I'm not quite understanding is why this works in C# and not IPY. Very odd. Thanks for the suggestions though.
Leonard H Martin
You cracked it! I'm an idiot and had my for loop syntax all mixed up - I'm only just learning this stuff. Cheers!
Leonard H Martin
A: 

Thanks to danbystrom for making me go and check this - it was my for loop syntax that was all mixed up, when I was certain I'd got it right. The correct syntax should have been:

for y in range(bmData.Height):
    for x in range(bmData.Width):
        rgbValues[(bmData.Stride * y) + (3 * x) +2] = 255

D'oh! But it just goes to show the benefit of having other people question what you're doing :)

Leonard H Martin
Well, I *did* start out by saying that I didn't understand how the loop was supposed to work, didn't I? :-) But I repeat: that C# code is wrong. You shouldn't do that with a 24bpp bitmap. Try it on a one pixel wide bitmap and see what happens!
danbystrom
Updated with what I *think* is the correct method for accessing bits in an image. Interestingly, windows stores the data in BGR format.
Leonard H Martin