×

INDI Library v2.0.6 is Released (02 Feb 2024)

Bi-monthly release with minor bug fixes and improvements

How to convert a fits file into good quality jpg?

  • Posts: 14
  • Thank you received: 0
Hi,

maybe a few know this fantastic project from Thomas about his allsky cam.

github.com/thomasjacquin/allsky

Sadly it linked to Zwo cams only, so I'm thinking about replacing his capture.cpp file with some python script that does same but using indilib.

As most of the other - not hardware related scripts - use png or jpg files, I'm wondering how to convert fits into jpg or png files.

I tried something like that, using a color cam from QHY, so there is only one HDU object in.

##########################

#!/usr/bin/python3

import sys
import getopt
import PyIndi
import time
import sys
import threading
import io
import numpy as np
from PIL import Image
from astropy.io import fits


def main(argv):
inputfile = ''
outputfile = ''
try:
opts, args = getopt.getopt(argv,"hi:o:")
except getopt.GetoptError:
print ('fitsTojpg.py -i <inputfile> -o <outputfile>')
sys.exit(2)

for opt, arg in opts:
if opt == '-h':
print ('fitsTojpg.py -i <inputfile> -o <outputfile>')
sys.exit()
elif opt in ("-i"):
inputfile = arg
elif opt in ("-o"):
outputfile = arg

print ('Input file is "', inputfile)
print ('Output file is "', outputfile)

image_data = fits.open(inputfile)
print (image_data.info())
dataImage = image_data[0].data
im = Image.fromarray(dataImage)
if im.mode != 'RGB':
im = im.convert('RGB')
im.save(outputfile)
im.close()


if __name__ == "__main__":
main(sys.argv[1:])

##########################

But the quality is not good: so how to make a nice jpg out a one shot color flits file? In Python if possible.

Thanks!
Joerg

pic1.fits (too large, so its within pic1.zip): original one, got by another python script via BLOB. It has colors by looking at that in KStars FITS Viewer.

File Attachment:

File Name: pic1.zip
File Size:8,224 KB


pic1_astconvertt.jpg: better but still no color (done by astconvertt, part of GNU Astronomy Utilities)


pic1_myscript.jpg: done by the script above
Last edit: 3 years 6 months ago by Joerg Koenig.
3 years 6 months ago #59309
Attachments:

Please Log in or Create an account to join the conversation.

  • Posts: 14
  • Thank you received: 0
Maybe somebody have a nice way to debayering those fits coming from indilib one color short cams. I have tested a few - so far I have not found something that would produce good results.

Any idea?
3 years 6 months ago #59372

Please Log in or Create an account to join the conversation.

  • Posts: 1957
  • Thank you received: 420
There are many algorithms to debayer OSC images and the only software I know of that perform this well are expensive, like AstroPixelProcessor and PixInsight.

I’d be interested in a Python library that can do this as well by the way.


Wouter
3 years 6 months ago #59373

Please Log in or Create an account to join the conversation.

  • Posts: 14
  • Thank you received: 0
Yes... seems not that easy. The KStars FITS Viewer gives best results so far. Any way that there is a command line version of that?
3 years 6 months ago #59386

Please Log in or Create an account to join the conversation.

  • Posts: 180
  • Thank you received: 30
OpenCV has debayering functions. I do not know if they can be used for those FITS
3 years 6 months ago #59406

Please Log in or Create an account to join the conversation.

  • Posts: 910
  • Thank you received: 86
Fitswork 4 works for me (on Windows). It has a batch mode.
For debayering/converting to TIFF's.
Fitswork is not very straightforward but I figured it out.
Here's how to batch convert to TIFF (tiffs then can be converted to jpg elsewhere).

-- open Fitswork.
-- go to Settings | Save and make sure Scale TIFF Images is unchecked.
-- open one of your fits files in Fitswork.
-- go to Processing | Bayer Interpolation | Demosaic Camera Raw Image
-- select Linear Interpolation or Bayer Astro (didn't investigate the difference)
-- set your Bayer pattern (RGGB etc)
-- click on the Save button to save your Debayer settings to a *.fcm file. Then Cancel.
-- go to File | Batch Processing.
-- Start File - select the first file of your sequence. Fitswork will pick up the rest.
-- Destination File - anyname.tif - you need to specify the extension! *.tif will be saved as tif. *.jpg willl be saved as jpg.
-- All Files in Folder - unticked. Skip absent - ticked.
-- Click the arrow on the right (next step)
-- Choose Processing Function | Debayer Raw Images | Open the *.fcm file you saved before
-- START and you're done.

I just tried to convert to JPG and it didn't work (got white images) - maybe Fitswork settings can be adjusted to work with JPG.
-- Max S
ZWO AM5. RST-135. AZ-GTI. HEQ5. iOptron SkyTracker.
TPO RC6. FRA400. Rokinon 135 and other lenses.
ZWO ASI2600MC. D5500 modified with UVIR clip-in filter.
ZWO ASI120MM Mini x 2. ZWO 30F4 guider. Orion 50mm guider.
ZWO EAF x 2.
Last edit: 3 years 6 months ago by maxthebuilder.
3 years 6 months ago #59605

Please Log in or Create an account to join the conversation.

  • Posts: 61
  • Thank you received: 8
Have you tried this:
pypi.org/project/colour-demosaicing/

I used it a while ago when I experimented with processing FITS files with Python scripts.

CS
Guido
3 years 6 months ago #60695

Please Log in or Create an account to join the conversation.

  • Posts: 14
  • Thank you received: 0
Thanks for all the feedback. I think I have made good progress - I will share current status tomorrow.

Looks promising...
3 years 6 months ago #60696

Please Log in or Create an account to join the conversation.

  • Posts: 14
  • Thank you received: 0
Python script gets the fits file from indilibserver, tze cam model (in my case a QGYIII178C, is hard codded for now). you can set a few parameters:

capture.py -b binding -g gain -e exposure -f filename

#########################################
#!/usr/bin/python3

import PyIndi
import time
import sys
import getopt
import threading
import io
import numpy as np
from PIL import Image
from astropy.io import fits



class IndiClient(PyIndi.BaseClient):
def __init__(self):
super(IndiClient, self).__init__()
def newDevice(self, d):
pass
def newProperty(self, p):
pass
def removeProperty(self, p):
pass
def newBLOB(self, bp):
global blobEvent
print("new BLOB ", bp.name)
blobEvent.set()
pass
def newSwitch(self, svp):
pass
def newNumber(self, nvp):
pass
def newText(self, tvp):
pass
def newLight(self, lvp):
pass
def newMessage(self, d, m):
pass
def serverConnected(self):
pass
def serverDisconnected(self, code):
pass

def main(argv):
global blobEvent
try:
opts, args = getopt.getopt(argv,"hb:g:e:f:")
except getopt.GetoptError:
print ('capture.py -b binding -g gain -e exposure -f filename')
sys.exit(2)

if len(opts) == 0:
print ('capture.py -b binding -g gain -e exposure -f filename')
sys.exit(2)

for opt, arg in opts:
if opt == '-h':
print ('capture.py -b binding -g gain -e exposure -f filename')
sys.exit()
elif opt in ("-b"):
binding = arg
elif opt in ("-g"):
gain = arg
elif opt in ("-e"):
exposure = arg
elif opt in ("-f"):
filename = arg

print ('binding is ', binding)
print ('gain is ', gain)
print ('exposure is ', exposure)
print ('filename is ', filename)

# connect the server
indiclient=IndiClient()
indiclient.setServer("localhost",7624)

if (not(indiclient.connectServer())):
print("No indiserver running on "+indiclient.getHost()+":"+str(indiclient.getPort())+" - Try to run")
print(" indiserver indi_simulator_telescope indi_simulator_ccd")
sys.exit(1)

# Let's take some pictures
ccd="QHY CCD QHY5III178C-62f"
device_ccd=indiclient.getDevice(ccd)
while not(device_ccd):
time.sleep(0.5)
device_ccd=indiclient.getDevice(ccd)

ccd_connect=device_ccd.getSwitch("CONNECTION")
while not(ccd_connect):
time.sleep(0.5)
ccd_connect=device_ccd.getSwitch("CONNECTION")
if not(device_ccd.isConnected()):
ccd_connect[0].s=PyIndi.ISS_ON # the "CONNECT" switch
ccd_connect[1].s=PyIndi.ISS_OFF # the "DISCONNECT" switch
indiclient.sendNewSwitch(ccd_connect)

ccd_exposure=device_ccd.getNumber("CCD_EXPOSURE")
while not(ccd_exposure):
time.sleep(0.5)
ccd_exposure=device_ccd.getNumber("CCD_EXPOSURE")

ccd_gain=device_ccd.getNumber("CCD_GAIN")
while not(ccd_gain):
time.sleep(0.5)
ccd_gain=device_ccd.getNumber("CCD_GAIN")

#ccd_temperature=device_ccd.getNumber("CCD_TEMPERATURE")
#while not(ccd_temperature):
# time.sleep(0.5)
# ccd_temperature=device_ccd.getNumber("CCD_TEMPERATURE")

ccd_binding=device_ccd.getNumber("CCD_BINNING")
while not(ccd_binding):
time.sleep(0.5)
ccd_binding=device_ccd.getNumber("CCD_BINNING")

print("Current Values:")
print("Exposure: ", ccd_exposure[0].value)
print("Gain: ", ccd_gain[0].value)
#print("Temperature: ", ccd_temperature[0].value)
print("Binding: ", ccd_binding[0].value)


# Ensure the CCD simulator snoops the telescope simulator
# otherwise you may not have a picture of vega
ccd_active_devices=device_ccd.getText("ACTIVE_DEVICES")
while not(ccd_active_devices):
time.sleep(0.5)
ccd_active_devices=device_ccd.getText("ACTIVE_DEVICES")
ccd_active_devices[0].text="Telescope Simulator"
indiclient.sendNewText(ccd_active_devices)

# we should inform the indi server that we want to receive the
# "CCD1" blob from this device
indiclient.setBLOBMode(PyIndi.B_ALSO, ccd, "CCD1")

ccd_ccd1=device_ccd.getBLOB("CCD1")
while not(ccd_ccd1):
time.sleep(0.5)
ccd_ccd1=device_ccd.getBLOB("CCD1")

# a list of our exposure times
#exposure = 10
#gain = 0
#binding = 2

# we use here the threading.Event facility of Python
# we define an event for newBlob event
blobEvent=threading.Event()
blobEvent.clear()

ccd_exposure[0].value=float(exposure)
indiclient.sendNewNumber(ccd_exposure)

ccd_gain[0].value=float(gain)
indiclient.sendNewNumber(ccd_gain)

ccd_binding[0].value=float(binding)
indiclient.sendNewNumber(ccd_binding)

ccd_exposure=device_ccd.getNumber("CCD_EXPOSURE")
while not(ccd_exposure):
time.sleep(0.5)
ccd_exposure=device_ccd.getNumber("CCD_EXPOSURE")

ccd_gain=device_ccd.getNumber("CCD_GAIN")
while not(ccd_gain):
time.sleep(0.5)
ccd_gain=device_ccd.getNumber("CCD_GAIN")

#ccd_temperature=device_ccd.getNumber("CCD_TEMPERATURE")
#while not(ccd_temperature):
# time.sleep(0.5)
# ccd_temperature=device_ccd.getNumber("CCD_TEMPERATURE")

ccd_binding=device_ccd.getNumber("CCD_BINNING")
while not(ccd_binding):
time.sleep(0.5)
ccd_binding=device_ccd.getNumber("CCD_BINNING")

print("Set values to:")
print("Exposure: ", ccd_exposure[0].value)
print("Gain: ", ccd_gain[0].value)
#print("Temperature: ", ccd_temperature[0].value)
print("Binding: ", ccd_binding[0].value)

blobEvent.wait()
for blob in ccd_ccd1:
print("name: ", blob.name," size: ", blob.size," format: ", blob.format)
# pyindi-client adds a getblobdata() method to IBLOB item
# for accessing the contents of the blob, which is a bytearray in Python
fits=blob.getblobdata()
# print("fits data type: ", type(fits))

# write image data to StringIO buffer
blobfile = io.BytesIO(fits)
# open a file and save buffer to disk
with open(filename, "wb") as f:
f.write(blobfile.getvalue())


if __name__ == "__main__":
main(sys.argv[1:])

########################


For the fits -> png conversion I use this script below - still work in progress, i generate lots of files to find the best one. Seems that the little auto stretching, white balancing function does its job. Its looks very similar as the KStars Fits debayering / preview or as in ZWOs ASIFitsView.
Best one seems to be the combination of Menon2007 debayering and white balancing - capture_image_int8_Menon2007_wb.png.

########################

import os
import cv2
import numpy as np
import astropy.io.fits as pyfits

from colour_demosaicing import (
EXAMPLES_RESOURCES_DIRECTORY,
demosaicing_CFA_Bayer_bilinear,
demosaicing_CFA_Bayer_Malvar2004,
demosaicing_CFA_Bayer_Menon2007,
mosaicing_CFA_Bayer)

def white_balance(img):
stack = []
for i in cv2.split(img):
hist, bins = np.histogram(i, 256, (0, 256))
# remove colors at each end of the histogram which are used by only by .05%
tmp = np.where(hist > hist.sum() * 0.0005)[0]
i_min = tmp.min()
i_max = tmp.max()
# stretch history a bit
tmp = (i.astype(np.int32) - i_min) / (i_max - i_min) * 255
tmp = np.clip(tmp, 0, 255)
stack.append(tmp.astype(np.uint8))
return np.dstack(stack)

hdul = pyfits.open('/home/fritz/python/test.fits')
print (hdul.info)
image_uint16=hdul[0].data
print("Raw fits file:")
print(image_uint16)
print("######################################################")

image_int8 = cv2.normalize(image_uint16, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)

print("Normalized data:")
print(image_int8)
print("######################################################")


image_int8_eq = cv2.equalizeHist(image_int8)

print("EqualizeHist data:")
print(image_int8_eq)
print("######################################################")


balanced_img = np.zeros_like(image_int8) #Initialize final image

image_rgb1 = cv2.cvtColor(image_uint16, cv2.COLOR_BayerGB2RGB)
image_rgb2 = cv2.cvtColor(image_int8, cv2.COLOR_BayerGB2RGB)
image_rgb3 = cv2.cvtColor(image_int8_eq, cv2.COLOR_BayerGB2RGB)
debayerImage1 = demosaicing_CFA_Bayer_Malvar2004(image_int8, pattern='GRBG')
debayerImage2 = demosaicing_CFA_Bayer_bilinear(image_int8)
debayerImage3 = demosaicing_CFA_Bayer_Menon2007(image_int8, pattern='GRBG', refining_step=True)

print("RGB image_uint16 data:")
print(image_rgb1)
print("######################################################")

print("RGB image_int8 data:")
print(image_rgb2)
print("######################################################")

print("RGB image_int8_eq data:")
print(image_rgb3)
print("######################################################")

cv2.imwrite('/home/fritz/python/capture_image_uint16.png', image_rgb1)
cv2.imwrite('/home/fritz/python/capture_image_int8.png', image_rgb2)
cv2.imwrite('/home/fritz/python/capture_image_int8_eq.png', image_rgb3)
cv2.imwrite('/home/fritz/python/capture_image_int8_Malvar2004.png', debayerImage1)
cv2.imwrite('/home/fritz/python/capture_image_int8_bilinear.png', debayerImage2)
cv2.imwrite('/home/fritz/python/capture_image_int8_Menon2007.png', debayerImage3)
cv2.imwrite('/home/fritz/python/capture_image_int8_wb.png', white_balance(image_rgb2))
cv2.imwrite('/home/fritz/python/capture_image_int8_Malvar2004_wb.png', white_balance(debayerImage1))
cv2.imwrite('/home/fritz/python/capture_image_int8_Menon2007_wb.png', white_balance(debayerImage3))

########################
3 years 6 months ago #60713

Please Log in or Create an account to join the conversation.

  • Posts: 14
  • Thank you received: 0
A 1st version of capture.py is located here:

github.com/jkoenig72/indiCapture

usage: capture.py -c cam -b binding -g gain -e exposure -f fits_filename -p png_filename
Last edit: 3 years 6 months ago by Joerg Koenig.
3 years 6 months ago #60714

Please Log in or Create an account to join the conversation.

Time to create page: 1.699 seconds