problem altering jpeg pixels then saving amended jpeg

mistermentality

Active Member
Licensed User
Longtime User
I have never tried using graphics and have hit a problem. I am trying to load a jpeg, alter some pixels argb values, then save the amended version.

I don't want the picture displayed, just to alter the values then save the new version. So I have tried the following code

B4X:
Dim pixelval As Int
' Test routine to alter some pixels values
  For n = 0 To originX -1
    pixelval = jpg.GetBmpPixel(n ,0)
    pixelval = pixelval + 75
  jpg.SetBmpPixel(n,0, pixelval) ' change the specified pixels value
 Next

out = File.OpenOutput(File.DirDefaultExternal, "NEW.Jpg", False)

bmp2 = jpg.GetBmpFromPixels ' make new picture based on amended data
bmp2.WriteToStream(out, 100, "JPEG")

in.Close
out.Flush

The problem is that I cannot tell if this is working because although I get no errors there is no new file created (defaultexternal is my sd card and is writeable by my other applications although they write text this is my first try with graphics)

As I understand it the code above should get the value of pixel n and alter it, then create a new picture in bmp2 (dim'd as a bitmap of course) and write that to the sd card.

The original point was to learn about the argb values and how changing them affects the picture as I noticed the argb values could be of quite different length and originally just wanted to edit the rgb parts themselves not the alpha value but I do need to be able to save the amended picture.

So is there an obvious reason why despite no error messages no file is created?

Thanks.

Dave

Edit: I forgot to point out that jpg is dim'd as Jpeg using the jpeg library and that originally I did have it save to the sd card folder the original pic was in with some letters added to the originals name but that would give me a file not exists error saying that permission was denied (but I know the sd card is writeable with text files as a number of my apps do save to the sd card).
 
Last edited:

mistermentality

Active Member
Licensed User
Longtime User
I thought it went to the root of the sd card but a search with root explorer found it right where you suggested, thanks.

It is just an empty file though so got to figure out why the pic isnt saving an amended version but maybe I have to draw the pic on a canvas first or something simila

Dave.
 
Upvote 0

mistermentality

Active Member
Licensed User
Longtime User
Before I post my question I have to say thank you to agraham for responding so quick not just with a reply but library changes, thank you :)

I apologise in advance for asking more questions but using the new jpeg library I have amended my code using the example included with the jpeg library v1.1.

The problem is that while my app saves the edited picture, it is not changed other than saved pictures often come out either blue or green (the green ones for some reason have two side by side copies of themselves in the one saved image).

This effect happens whether the routine I added to change some values is included or not, and whether the conversion to argb is done or not.

I think this may be because of something I am doing wrong with the code as the small scale version shown on screen does not have the problems some saved versions do.

I include the new code below, could someone point out where I may be going wrong?

Although I have included the whole subroutine which is long, I have marked the start of the problem area with asterisks to make that part easy to see and that problem area is toward the mid point of the code so if anyone can skip to that and give any advice I would be very greatful :)

As far as I can see it should display a scaled version of the chosen jpeg on screen and then read the pictures pixel data, edit some of its values, and save the edited jpeg to the default external directory. But for some reason it is not editing the pixel values to make the changes.

B4X:
Sub loadjpeg_button_Click
' choose a jpeg
   ret = fd.Show("Choose a file to load:", "Okay", "Cancel", "", Null)   
   If ret = -3 OR fd.ChosenName = "" Then 
        Return
   End If
   
       If File.Exists(fd.FilePath, fd.ChosenName) = False Then
       Msgbox(fd.ChosenName & " does not exist.", "")
       Return
       End If

       inputjpegname = fd.ChosenName ' this holds the name of the file chosen
jpegdir = fd.FilePath

   Dim in As InputStream
   Dim out As OutputStream
   Dim c As Canvas
   Dim r As Rect
   Dim w, h As Int
   ' as bmp is a local variable it will be GCed on exit reclaiming the memory as there will be no refence to it held
   Dim bmp, bmp2 As Bitmap   
   

       
        jpg.Initialize("jpg")
   c.Initialize(ImageView1)
   in = File.OpenInput(fd.FilePath, fd.ChosenName)
   Jpg.LoadJpegSizeOnly(in)
   in = File.OpenInput(fd.FilePath, fd.ChosenName)
   bmp = Jpg.LoadJpegSmaller(in, Jpg.JpegHeight/ImageView1.Height)
   'out = File.OpenOutput(File.DirRootExternal, "Bmp.Jpg", False)
   'bmp.WriteToStream(out, 100, "JPEG")
   'out.Flush
   'out.Close
   Dim pixels(), pixelval, rd, gn, bl As Int
   'originX = jpg.BmpWidth
   'originY = jpg.BmpHeight
   r.Initialize(0, 0, ImageView1.Width, ImageView1.Height)
   c.DrawBitmap(bmp, Null, r) ' draw the smaller preview on screen
   ImageView1.Invalidate
   
        ' *********
   ' From here on is the part that has the problem

   pixels = Jpg.BmpPixels ' get the argb values for the original bitmap
   Jpg.PixelsABGRtoARGB
   ' Test routine to alter some pixels values
   For n2 = 0 To 10
      For n = 0 To originX -1
      pixelval = jpg.GetBmpPixel(n ,n2) ' get the argb value of pixel n,n2
      pixelval = pixelval +75 ' add 75 to its value
      jpg.SetBmpPixel(n,0, pixelval) ' replace the previous value for the pixel with the new one
      Next
   Next
   'end test
   Jpg.SetPixelsFromBmp(bmp)
   bmp2 = jpg.GetBmpFromPixels
   out = File.OpenOutput(File.DirRootExternal, "NEW_" & fd.ChosenName, False)
   bmp2.WriteToStream(out, 100, "JPEG")   
   in.Close
   out.Flush
   out.Close
   
   
   pixelval = jpg.GetBmpPixel(100 ,0)
   Msgbox("Height = " & Jpg.JpegHeight & " Width = " & Jpg.JpegWidth & " Pixel argb = " & pixelval, "Jpeg details")
End Sub

I see in the example code with v1.1 it includes variables for red green blue values as Integers, but I'm not sure how to extract those values, is there a way to do that from the pixel data?

Dave
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
Jpg.SetPixelsFromBmp(bmp) is in the wrong place. It should be before Jpg.PixelsABGRtoARGB. I'm surprised that code isn't throwing an array bounds exception, I'll have to look why.

You don't need pixels = Jpg.BmpPixels if you are not going to access the pixel array directly nor Dim pixels(), pixelval, rd, gn, bl As Int - I left them in inadvertantly after testing.

Multiple images probably mean that you passed a 16bit RGB_565 bitmap instead of an ARGB_8888 one to the library.

You get the individual alpha and colours using Bit.And and Bit.Shift. Basic4android accepts 0xff like literals to make this easier.
 
Upvote 0

mistermentality

Active Member
Licensed User
Longtime User
I adapted the code accordingly but still have the problem of no alterations being made. I know you said I don't need to use jpg.BmpPixels but don't I need that if I want to read the current value of a pixel?

I just cannot get the pixel values to change and I don't know why.The following code snippet

B4X:
pixels = Jpg.BmpPixels ' get the argb values for the original bitmap
pixelval = jpg.GetBmpPixel(100,100) ' get the argb value of pixel 100,100
pixelval = pixelval +75 ' add 75 to its value
jpg.SetBmpPixel(100,100, pixelval) ' replace value for the pixel with new one
      
Jpg.SetPixelsFromBmp(bmp)
Jpg.PixelsABGRtoARGB
bmp2 = jpg.GetBmpFromPixels

should find the argb value of pixel 100,100 and add 75 to whatever its current argb value is, and then make a new bitmap from the amended data (assuming the code is as posted earlier with bmp as the currently worked on image)?

This is the bit I just cannot seem to figure out, it sounds so simple so I must be doing something wrong. I really hate wasting anyones time so I'll just ask one quick question....am I right in thinking that the above code snippet should alter a pixel of the current bitmap and then create an amended version?

Thank you

Dave
 
Upvote 0

mistermentality

Active Member
Licensed User
Longtime User
You haven't fixed the problem I already pointed out. Put Jpg.SetPixelsFromBmp(bmp) in the correct place.

I think there is a misunderstanding on my part.

You said
Jpg.SetPixelsFromBmp(bmp) is in the wrong place. It should be before Jpg.PixelsABGRtoARGB
which I took to mean that the SetPixels command must be called first and then after that call PixelsABGRtoARGB, and in the new code snippet that I posted in my previous post SetPixelsFromBmp is on the line before PixelsABGRtoARGB....

B4X:
Jpg.SetPixelsFromBmp(bmp)
Jpg.PixelsABGRtoARGB
bmp2 = jpg.GetBmpFromPixels

So unless I misunderstood you I have made the alteration you suggested as the SetPixelsFromBmp statement does come first as you said it should.

Dave
 
Upvote 0

mistermentality

Active Member
Licensed User
Longtime User
You moved the wrong line. I said it was Jpg.SetPixelsFromBmp(bmp) that was in the wrong place but you moved Jpg.PixelsABGRtoARGB!

Check out the new demo in the archive, it should be clearer there.

I am really confused now because in the demo you have the code....

B4X:
        Jpg.SetPixelsFromBmp(bmp)
   pixels = Jpg.BmpPixels
   Jpg.PixelsABGRtoARGB ' overcomes a longstanding bug in Android
   bmp2 = jpg.GetBmpFromPixels

and my code snippet of

B4X:
Jpg.SetPixelsFromBmp(bmp)
Jpg.PixelsABGRtoARGB
bmp2 = jpg.GetBmpFromPixels

is identical except that you have one additional line in between with pixels = Jpg.BmpPixels (which I have added to my test app in case needed).

I have the statements in the same order as you suggested, its just for some reason I seem unable to alter pixel values and I can't figure out why.

Dave
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
The first thing you need to do is load the pixel array from a bitmap (although as noted in the help it is not needed if you have used LoadJpegArea). Then do the manipulation. Then save it. Why is this so difficult?

I don't see the code you attribute to me in the new demo. The code in the demo, which I have just downloaded to check, looks like this.
B4X:
   Jpg.SetPixelsFromBmp(bmp)
   Jpg.PixelsABGRtoARGB ' overcomes a longstanding bug in Android
   
   ' play with pixel component values
   Dim pixelval, alpha, rd, gn, bl As Int 
   Jpg.SetBmpPixel(0, 0, Colors.ARGB(255, 192, 128, 64)) ' set a pixel
   pixelval = Jpg.GetBmpPixel(0, 0) ' get a pixel
   alpha = Bit.UnsignedShiftRight(Bit.And(pixelval, 0xff000000), 24) 
   rd = Bit.UnsignedShiftRight(Bit.And(pixelval, 0xff0000), 16) 
   gn = Bit.UnsignedShiftRight(Bit.And(pixelval, 0xff00), 8) 
   bl = Bit.And(pixelval, 0xff) 
   Log("ARGB = " & alpha & "," & rd & "," & gn  & "," & bl)

   ' draw line 
   Dim i As Int
   For i = 0 To 100
      Jpg.SetBmpPixel(i, i, Colors.Cyan)
   Next
   
   ' if you need to you can get the pixels as an array of Ints
   Dim pixels() As Int
   pixels = Jpg.BmpPixels
   ' draw line 
   Dim i As Int
   For i = 0 To 100
      pixels(i)= Colors.Yellow
   Next   
   
   'save the changed bitmap
   bmp2 = jpg.GetBmpFromPixels
   out = File.OpenOutput(File.DirRootExternal, "Bmp2.Jpg", False)
   bmp2.WriteToStream(out, 100, "JPEG")   
   in.Close
   out.Flush
   out.Close
 
Upvote 0

mistermentality

Active Member
Licensed User
Longtime User
The first thing you need to do is load the pixel array from a bitmap (although as noted in the help it is not needed if you have used LoadJpegArea). Then do the manipulation. Then save it. Why is this so difficult?

I don't see the code you attribute to me in the new demo.

My apologies, it is your code but must have been from before you added to the demo code as I just re downloaded and the 1.1 zip I had before did not have the additional code the new one has with the lines like

B4X:
Jpg.SetBmpPixel(0, 0, Colors.ARGB(255, 192, 128, 64)) ' set a pixel

I thought I had the latest demo, but I will study that as the additional code looks like what I have been trying to achieve unsucessfully all along. But I agree I have no idea why it is proving difficult for me, but thank you for your patience.

Dave
 
Upvote 0
Top