Testing Camera Quality Settings and PhoneGap/Cordova

This post is more than 2 years old.

As you know, when using the Camera plugin with PhoneGap/Cordova, you have an optional quality setting. It accepts values from 0 to 100 with 50 being the default. I was curious as to how much of an impact this setting had on the final result. Obviously quality can be subjective, but I thought it would be interesting to build a simple tool that would let me test the settings and compare the results.

I began by building a simple Node.js application. Its sole purpose was to simply listen for a form POST and save attached files to a time stamped directory. While not really important to the discussion, I wanted to share the code because this is the first time I've done uploads in Node and Formidable made it freaking easy as heck. Like, I went from thinking I'd need 3-4 hours to figure it out to having the entire server done in about 30 minutes. Anyway, here's the code:

var express = require('express');
var app = express();

var fs = require('fs');

app.set('port', process.env.PORT || 3000);

var formidable = require('formidable');

app.post('/process', function(req, res) {

	console.log('Attempting process.');
	//data directory
	var time = new Date();

	var dir = __dirname + '/output_'+time.getFullYear()+"_"+(time.getMonth()+1)+"_"+time.getDate() + "_"+time.getHours() + "_" + time.getMinutes()+"_"+time.getSeconds();
	fs.existsSync(dir) || fs.mkdirSync(dir);
	console.log("Dir to save is "+dir);
	var updir = __dirname + '/uploads';
	fs.existsSync(updir) || fs.mkdirSync(updir);

	var form = new formidable.IncomingForm();
	form.uploadDir = updir;

	form.parse(req, function(err, fields, files) {
		if(err) {
			res.send("i shit my pants");
		console.log("fields", fields);
		console.log("files", files);
		for(var file in files) {
			if(files[file].name.length) {
				var source = files[file].path;
				var dest = dir + '/' + files[file].name;
				console.log(source, fs.existsSync(source));
				console.log('copied '+files[file].name);


app.listen(app.get('port'), function() {
	console.log('App started on port '+app.get('port'));

Again - don't spend too much time looking this over. It really isn't the important part. Now let's discuss the app. I created a quick Ionic app with one button. The idea would be that you would click the button, and the Camera API will take over. Each iteration of clicking would change to a new quality setting. I decided 20%, 40%, 60%, 80%, and 100% would be good testing points. I also decided to do the same tests for pictures selected from the device. My gut told me that the quality setting would have no impact there, but I wasn't sure. When I tested, I confirmed that quality does not impact existing pictures, so in the code below you will see some parts that are no longer applicable. (And I should stress, this code is an absolute mess. I was going for quick and dirty here, nothing more.)

Another interesting aspect of the code is that I decided to use XHR2 instead of Cordova's File-Transfer plugin. Why? Because the plugin doesn't support multiple uploads at once. With XHR2, I could create a FormData structure with all my file data and send them in one request.

So - the code - and again - this is a mess so don't consider this suitable production code. I'm only including the JavaScript as the HTML is a header and a button.

angular.module('starter', ['ionic'])

.controller("mainController", function($scope) {
	var images = [];
	var idx = 0;
	$scope.currentLabel = {value:""};
	$scope.doingUpload = {value:false};
	var doLabel = function() {
		var label = "";
		switch(idx) {
				case 0: label="Camera 20%"; break;
				case 1: label="Camera 40%"; break;
				case 2: label="Camera 60%"; break;
				case 3: label="Camera 80%"; break;
				case 4: label="Camera 100%"; break;
				case 5: label="Gallery 20%"; break;
				case 6: label="Gallery 40%"; break;
				case 7: label="Gallery 60%"; break;
				case 8: label="Gallery 80%"; break;
				case 9: label="Gallery 100%"; break;
				case 10: label="Upload"; break;
        case 5: label="Upload"; break;
		$scope.currentLabel.value = label;
		if(!$scope.$$phase) {
	//Technically I do pics *and* uploads
	$scope.doPic = function() {
		if(idx <= 4) {
			var options = {destinationType:Camera.DestinationType.NATIVE_URI};
			options.sourceType = (idx<=4)?Camera.PictureSourceType.CAMERA:Camera.PictureSourceType.PHOTOLIBRARY;

			if(idx === 0 || idx === 5) options.quality = 20;
			if(idx === 1 || idx === 6) options.quality = 40;
			if(idx === 2 || idx === 7) options.quality = 60;
			if(idx === 3 || idx === 8) options.quality = 80;
			if(idx === 4 || idx === 9) options.quality = 100;

			navigator.camera.getPicture(function(u) {
				var result = {index:idx, uri:u};
			}, function(e) {
				//if we get an error, might as well die now
			}, options);
		} else {
			$scope.doingUpload.value = true;
			if(!$scope.$$phase) {
			console.log('ok, do upload');
      var complete = 0;
			var formData = new FormData();
			images.forEach(function(i) {
				console.log("processing image "+JSON.stringify(i));
				//console.log("test "+decodeURI(i.uri));
				window.resolveLocalFileSystemURL(i.uri, function(fileEntry) {
					fileEntry.file(function(file) {
						var reader = new FileReader();
						reader.onloadend = function(frResult) {
							var data = new Uint8Array(frResult.target.result);
              //using a hard coded name since gallery pics were 'content'
              var fileName = i.index + ".jpg";
							formData.append("index"+i.index, new Blob([data], {type:file.type}), fileName);	
              if(complete === images.length) doUpload(formData);
				   },function(e) {
						console.log("failed to get a file ob");
				}, function(e) {
					console.log("something went wrong w/ resolveLocalFileSystemURL");	
  var doUpload = function(data) {
    console.log('doUpload', data);
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '', true);
    xhr.onload = function(e) {
      $scope.doingUpload.value = true;
      if(!$scope.$$phase) {
    xhr.onerror = function(e) {
      console.log('onerror fire');

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
    if(window.StatusBar) {

Ok - so what were the results like? In the following screen shot, 0.jpg represents the first image, which is at 20%, whereas 4.jpg represents the last one, at 100%. Note the file size differences:


How about the quality? I'll include all the images as an attachment to this blog post, but let's focus on the 20% and 100%. I'm resizing these too of course. First, the 20%:


And now the 100%:


There seems to be a huge difference in the details of the wallpaper and the colors in the flowers.

Interestingly enough - the 80% is over one meg smaller despite being just 20% less quality. Obviously theres a lot of loss going on - but I think if you look at the 80% - it looks really good:


Certainly this isn't an incredibly scientific test. I left my default camera settings on and each click was a new picture. Many things could have impacted the result - how I held the camera - small changes in light - ghosts, etc. For folks curious, I tested this with an HTC M8. If folks want, I can give the iPhone a try as well.

I've uploaded the zip of images here: https://dl.dropboxusercontent.com/u/88185/output_2015_4_27_12_26_22.zip.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Marek Wawrzynczyk posted on 4/30/2015 at 8:28 AM

Very usefull.

Comment 2 by Munish Kumar posted on 1/11/2016 at 6:01 AM

I needed to balance between quality and size and your post helped me to get there ! Thanks

Comment 3 by Akash Pal posted on 9/2/2016 at 2:52 PM

What about videos? Does the video size differ when quality is changed?

Comment 4 (In reply to #3) by Raymond Camden posted on 9/2/2016 at 3:03 PM

I don't know - test it and see. :)