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))

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

Read More...