Decreasing size of a photo taken via media.capturePhoto

I’ve currently got a app in which you need to take a picture and send it off to our server and the client complained about slow sending speed (because of his slow connection but he didn’t say that) compared to a different app with roughly the same concept. My only option is to decrease the size of the photo’s taken but I can’t find this anywhere on Google or in the Corona Documentation…

How can I scale down the image to make the file a lot smaller (currently ~7Mb on my iPhone 5)

+1

Yeah i’d be interested in this as well - would be useful for a future app idea ( when i improve my code ability ).

A pertinent question would be 

  1. is your photo full screen. So you want to take a full screen photo then scale it!

(i don’t know if this is actually possible)

or 2) you create a small insert window that shows the camera view - takes photo and result is photo is a smaller size???(than full)

i guess it all depends on the type of content that is needed to be photographed - small area or full screen

T.

A typical approach is to do the display.capture, then modify the returned image objects xScale and yScale before the display.save call (passing in the object of course). This will affect the pixel dimensions (and file size) of your final image.

However, it will still produce different dimension images on different devices. Retina devices will produce images approx 4x the dimensions of non-retina, for example.

This is due to display.save saving in the devices native resolution (see display.save docs).  If you want virtually identical pixel dimensions on all devices, then you will also need to factor in display.scaleX/display.scaleY as well.

You can try my function.:

[lua]

@param photo displayObject Such as a captured image from a camera

@param newFileName String Name of new image file in documents directory. Ending in .png or .jpg

@param width int The px width of final image in documents directory

@param height int The px height of final image in documents directory

local function saveImage(photo, newFileName, width, height)

    

    local endWidth = width * display.contentScaleX

    local endHeight = height * display.contentScaleY

    

    local tempGroup = display.newGroup()

    

    photo.anchorX   = 0.5

    photo.anchorY   = 0.5

    photo.x         = display.contentCenterX

    photo.y         = display.contentCenterY

    tempGroup:insert(photo)

    

    – Find the bigger scale out of widht or height so it will fill in the crop

    local scale = math.max(endWidth / photo.contentWidth, endHeight / photo.contentHeight)

    photo.width = photo.width * scale

    photo.height = photo.height * scale

    

    – This object will be used as screen capture boundaries object

    local cropArea   = display.newRect(display.contentCenterX, display.contentCenterY, endWidth, endHeight)

    cropArea.anchorX = 0.5

    cropArea.anchorY = 0.5

    cropArea.x       = display.contentCenterX

    cropArea.y       = display.contentCenterY

    cropArea.alpha   = 0

    tempGroup:insert(cropArea)

    

    – Now capture the crop area which the user image will be underneith

    local myCaptureImage = display.captureBounds(cropArea.contentBounds)

    

    myCaptureImage.anchorX  = 0.5

    myCaptureImage.anchorY  = 0.5

    myCaptureImage.x        = display.contentCenterX

    myCaptureImage.y        = display.contentCenterY

    

    display.save(myCaptureImage, newFileName) 

    myCaptureImage:removeSelf() – Remove captured image since we have the image already visible

    myCaptureImage = nil

    

    tempGroup:removeSelf()

    tempGroup = nil

end

local someImage = display.newCircle(200, 200, 150)

saveImage(someImage, “test.jpg”, 512, 512)[/lua]

I thought then when you call display.save it would create a PNG file which is mostly used for flat colors instead of pictures (which need more precise data). If I can just load in the picture, scale it down and save it again as a JPG (without much quality loss due to file the file type) then that would fix my problem. I’ll take a look, oh and jonjonsson I’m assuming your code works with any display object right? Not just stuff created by something like display.newCircle or newLine or so.

@allen

I believe if you just use ,jpg for the file extension when calling it, display.save() will save in jpg format.

If you save it as a png, It is pretty lossless in the SDK I believe – best for photos.  jpg does some heavier compression in the SDK, so much so that on android the filesize is almost a 10-1 ratio.   It’s is jpg where they will look worse in Corona – but much much smaller.

And yes, just changing the extension when you display.save will switch between the formats.

I think the jpg are way too compressed and almost unusable, while the PNG are too big. Really wish we had something in between, configurable would be best and logical.

 jonjonsson I’m assuming your code works with any display object right? Not just stuff created by something like display.newCircle or newLine or so.

I use it with images from camera for profile photos as well as game screenshot sharing. 

But I’m not in production, so this code has not gotten wide device testing. I sometimes see artifacts in the captured images. Better give it a good testing if you use it.

+1

Yeah i’d be interested in this as well - would be useful for a future app idea ( when i improve my code ability ).

A pertinent question would be 

  1. is your photo full screen. So you want to take a full screen photo then scale it!

(i don’t know if this is actually possible)

or 2) you create a small insert window that shows the camera view - takes photo and result is photo is a smaller size???(than full)

i guess it all depends on the type of content that is needed to be photographed - small area or full screen

T.

A typical approach is to do the display.capture, then modify the returned image objects xScale and yScale before the display.save call (passing in the object of course). This will affect the pixel dimensions (and file size) of your final image.

However, it will still produce different dimension images on different devices. Retina devices will produce images approx 4x the dimensions of non-retina, for example.

This is due to display.save saving in the devices native resolution (see display.save docs).  If you want virtually identical pixel dimensions on all devices, then you will also need to factor in display.scaleX/display.scaleY as well.

You can try my function.:

[lua]

@param photo displayObject Such as a captured image from a camera

@param newFileName String Name of new image file in documents directory. Ending in .png or .jpg

@param width int The px width of final image in documents directory

@param height int The px height of final image in documents directory

local function saveImage(photo, newFileName, width, height)

    

    local endWidth = width * display.contentScaleX

    local endHeight = height * display.contentScaleY

    

    local tempGroup = display.newGroup()

    

    photo.anchorX   = 0.5

    photo.anchorY   = 0.5

    photo.x         = display.contentCenterX

    photo.y         = display.contentCenterY

    tempGroup:insert(photo)

    

    – Find the bigger scale out of widht or height so it will fill in the crop

    local scale = math.max(endWidth / photo.contentWidth, endHeight / photo.contentHeight)

    photo.width = photo.width * scale

    photo.height = photo.height * scale

    

    – This object will be used as screen capture boundaries object

    local cropArea   = display.newRect(display.contentCenterX, display.contentCenterY, endWidth, endHeight)

    cropArea.anchorX = 0.5

    cropArea.anchorY = 0.5

    cropArea.x       = display.contentCenterX

    cropArea.y       = display.contentCenterY

    cropArea.alpha   = 0

    tempGroup:insert(cropArea)

    

    – Now capture the crop area which the user image will be underneith

    local myCaptureImage = display.captureBounds(cropArea.contentBounds)

    

    myCaptureImage.anchorX  = 0.5

    myCaptureImage.anchorY  = 0.5

    myCaptureImage.x        = display.contentCenterX

    myCaptureImage.y        = display.contentCenterY

    

    display.save(myCaptureImage, newFileName) 

    myCaptureImage:removeSelf() – Remove captured image since we have the image already visible

    myCaptureImage = nil

    

    tempGroup:removeSelf()

    tempGroup = nil

end

local someImage = display.newCircle(200, 200, 150)

saveImage(someImage, “test.jpg”, 512, 512)[/lua]

I thought then when you call display.save it would create a PNG file which is mostly used for flat colors instead of pictures (which need more precise data). If I can just load in the picture, scale it down and save it again as a JPG (without much quality loss due to file the file type) then that would fix my problem. I’ll take a look, oh and jonjonsson I’m assuming your code works with any display object right? Not just stuff created by something like display.newCircle or newLine or so.

@allen

I believe if you just use ,jpg for the file extension when calling it, display.save() will save in jpg format.

If you save it as a png, It is pretty lossless in the SDK I believe – best for photos.  jpg does some heavier compression in the SDK, so much so that on android the filesize is almost a 10-1 ratio.   It’s is jpg where they will look worse in Corona – but much much smaller.

And yes, just changing the extension when you display.save will switch between the formats.

I think the jpg are way too compressed and almost unusable, while the PNG are too big. Really wish we had something in between, configurable would be best and logical.

 jonjonsson I’m assuming your code works with any display object right? Not just stuff created by something like display.newCircle or newLine or so.

I use it with images from camera for profile photos as well as game screenshot sharing. 

But I’m not in production, so this code has not gotten wide device testing. I sometimes see artifacts in the captured images. Better give it a good testing if you use it.