dd is not the most appropriate tool for this task because it has no way of determining which blocks are unused. To do that, you would need something else that can analyze the file system and create a list of blocks to save. I don't know how to do that, but I would like to find out.
However, if you insist on using dd, one simple approach would begin by filling the unused space with zeroes. Example:
dd if=/dev/zero of=bigfile
Then create the dd image, and finally compress it with gzip or any compressor you like. The zero-filled blocks will be highly compressible.
If you execute dd and gzip as two consecutive commands, the destination would have to be large enough to temporarily hold both the uncompressed image and the compressed image. If you pipe the output of dd into gzip, you eliminate the need for temporary storage of the uncompressed image. Example:
dd if=/dev/sda1 | gzip - > dd_image_sda1.gz # to make the dd image and compress it
gzip -cd dd_image_sda1.gz | dd of=/dev/sda1 # to uncompress and restore the dd image
Please note, I have not actually saved or restored a dd image in this way. I've only read the manual and worked out a plan. You will have to test and debug it yourself. Before trying it on an important hard drive, I would test it on a spare drive or a small flash drive and see how that works.
3) What limitations regarding files system type (ext, nfts, fat, etc)
The method outlined above should work for any of those file systems, provided it is not encrypted or compressed.