def debugPlot(imagePath, fitsWcsPath, tleFolder, photoTimeDelta=None, altitude=0, minElevation=10): """ Plot the georeferenced image on a map. This allows to play with the image timestamp and mapping altitude. :param imagePath: :param fitsWcsPath: :param tleFolder: :param datetime.timedelta photoTimeDelta: amount to shift the image time (affects calculated camera position and therefore the geodetic coordinates) :param altitude: in km :param minElevation: in degrees """ mapping = auromat.mapping.spacecraft.getMapping(imagePath, fitsWcsPath, photoTimeDelta, tleFolder=tleFolder, altitude=altitude) mapping = mapping.maskedByElevation(minElevation) mapping = auromat.resample.resample(mapping) tmpPath = tempfile.mktemp(suffix='.png') saveFig(tmpPath, auromat.draw.drawPlot(mapping)) _openFile(tmpPath)
def _testMLatMLTPolarMapNorthPole(self): m = _getMappingNorth() m.checkGuarantees() #m = m.maskedByElevation(10) #m.checkGuarantees() m = resample(m, arcsecPerPx=100, method='mean') m.checkGuarantees() saveFig('test_mlatmlt_polar_north.png', drawMLatMLTPolar(m))
def _testConstellationPlot(self): imagePath = getResourcePath('ISS030-E-102170_dc.jpg') wcsPath = getResourcePath('ISS030-E-102170_dc.wcs') m = getMapping(imagePath, wcsPath) figax = loadFigImage(imagePath) drawConstellations(figax, wcsPath, clipPoly=outline(~m.intersectsEarth)) saveFig('test_constellations.jpg', figax)
def testParallelsMeridiansPlot(self): imagePath = getResourcePath('ISS030-E-102170_dc.jpg') wcsPath = getResourcePath('ISS030-E-102170_dc.wcs') m = getMapping(imagePath, wcsPath, altitude=0, fastCenterCalculation=True) figax = loadFigImage(imagePath) drawParallelsAndMeridians(m, figax=figax) drawConstellations(figax, wcsPath, clipPoly=outline(~m.intersectsEarth)) saveFig('test_parallelsmeridians.jpg', figax)
def _testMLatMLTPolarMapBothHemispheres(self): m = _getMappingSouth() m.checkGuarantees() #m = m.maskedByElevation(10) #m.checkGuarantees() m = resample(m, arcsecPerPx=100, method='mean') m.checkGuarantees() seqbb = BoundingBox(latSouth=-61.150846231, lonWest=142.622725698, latNorth=6.84984918353, lonEast=-116.820615123) saveFig('test_mlatmlt_polar_seqbb.png', drawMLatMLTPolar(m, boundingBox=seqbb))
def _testMiracle(self): m = _getMiracleMapping() saveFig('heat_intersection.png', drawHeatmap(m.intersectionInflatedCorner[...,0])) drawHeatmaps(m) m.checkGuarantees() m = m.maskedByElevation(10) m.checkGuarantees() m = resample(m, arcsecPerPx=100, method='mean') m.checkGuarantees() saveFig('test_stereo.png', drawStereographic(m))
def _testColors(self): m = _getMappingNorth() m = resample(m, arcsecPerPx=100, method='mean') saveFig('test_white.png', drawStereographic(m, bottomTitle='foo'), bgcolor='white') saveFig('test_black.png', drawStereographic(m, bottomTitle='foo'), bgcolor='black') saveFig('test_polar_white.png', drawMLatMLTPolar(m), bgcolor='white') saveFig('test_polar_black.png', drawMLatMLTPolar(m), bgcolor='black')
def debugHorizon(imagePath, fitsWcsPath, tleFolder, photoTimeDelta=None, altitude=110): """ Visualizes the horizon of the modelled earth and inflated earth (=aurora) for the given parameters. :param datetime.timedelta photoTimeDelta: amount to shift the image time (affects calculated camera position and therefore the geodetic coordinates) """ mapping = auromat.mapping.spacecraft.getMapping(imagePath, fitsWcsPath, photoTimeDelta, tleFolder=tleFolder, altitude=altitude) tmpPath = tempfile.mktemp(suffix='.jpg') saveFig(tmpPath, auromat.draw.drawHorizon(mapping)) _openFile(tmpPath)
def _testHoleBug(self): """ Triggers a (now fixed) bug that occured when 'mean' resampling led to single-polygon holes where surrounding polygons were well-defined. A hole is a polygon such that all corners are defined and just the polygon center data was missing (color etc.). The issue was that those holes were not filtered and led to subsequent errors. The fix was to filter not by whether all corners are defined but whether the color is defined as this also guarantees that the corners are defined. """ m = _getMappingNorth() m = resample(m, arcsecPerPx=200, method='mean') # saving as svg without rasterization will lead to errors when trying to use # polygons with NaN colors; saving as png only leads to black areas! saveFig('test_stereo.svg', drawStereographic(m, rasterized=False))
def _testParallelsMeridiansPlotOptimized(self): imagePath = getResourcePath('ISS030-E-102170_dc.jpg') wcsPath = getResourcePath('ISS030-E-102170_dc.wcs') # As we precalculated the bounding box and the lat/lon label position # we only need to access the 'latCenter', 'lonCenter' and 'intersectsEarth' # attributes of the spacecraft mapping. This means that we don't need # any sanitization applied on the corner coordinate arrays ('lats','lons'). # For this reason, and because we don't use masking, we set nosanitize=True # and can cut the required time in half. m = getMapping(imagePath, wcsPath, altitude=0, fastCenterCalculation=False, nosanitize=True) # the following values must be precalculated to use the optimization # the bounding box at altitude=0 without any masking bb = BoundingBox(latSouth=49.3401757697, lonWest=-116.368770925, latNorth=64.288454984, lonEast=-91.8890098192) # center of bounding box at altitude=0 when masked below 10deg elevation labelLat, labelLon = 53.133, -98.684 figax = loadFigImage(imagePath) drawParallelsAndMeridians(figax, m, boundingBox=bb, labelLat=labelLat, labelLon=labelLon) drawConstellations(figax, wcsPath, clipPoly=outline(~m.intersectsEarth)) saveFig('test_parallelsmeridians_optimized.jpg', figax)
def maskStarfield(imagePath, channel=None, blackenLowerPart=True, ignoreVeryDark=True, debugPathPrefix=None, debugJpegQuality=80): """ Automatic masking of the starfield in the given image using a combination of image processing and object detection steps. :param str channel: the channel to use for analysis 'R','G','B', or None for combining all channels into a grayscale image :param bool blackenLowerPart: If the earth is in the lower part of the image then this should be set to True as it will broadly mask parts of the earth not detected otherwise. :param bool ignoreVeryDark: If True, then areas (block) which are almost totally black are not considered as starfield. This is sometimes useful to ignore very dark spacecraft structures which would later on be detected as stars. :param str debugPathPrefix: if given, the folder in which to store debug images illustrating the different processing stages :param int debugJpegQuality: JPEG quality from 0 to 100 used for storing debug images; only contour images are currently saved as JPEG :rtype: tuple (mask, sigma) """ if debugPathPrefix: red = (0,0,255) green = (0,255,0) orange = (0,106,255) debugHistogramPath = debugPathPrefix + 'hist.svg' debugThresholdedImagePath = debugPathPrefix + 'thresh.png' debugContoursImagePath = debugPathPrefix + 'cont.jpg' debugContoursMaskImagePath = debugPathPrefix + 'cont_mask.jpg' debugAdaptiveThresholdedImagePath = debugPathPrefix + 'thresh_adapt.png' debugCutoffImagePath = debugPathPrefix + 'cutoff.jpg' if isinstance(imagePath, np.ndarray): # assume RGB image array im = np.require(imagePath, np.uint8, 'C') im = cv.cvtColor(im, cv.COLOR_RGB2BGR) imagePath = '[array]' else: im = cv.imread(imagePath) if channel is None: imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY) elif channel.lower() == 'r': imgray = im[:,:,2] elif channel.lower() == 'g': imgray = im[:,:,1] elif channel.lower() == 'b': imgray = im[:,:,0] else: raise ValueError('channel is "{}" but must be R,G,B or None'.format(channel)) # opencv requires a contiguous array.. imgray = np.require(imgray, np.uint8, 'C') # Step 1: Find dark areas which might be starfield fudge = 20 binary, hist, threshold, firstSpike = _binarizeStarfieldImage(imgray, fudge=fudge) contours, area, isBigContour, isSmallLongContour, isSmallShortContour = _findAndCategorizeContours(binary) mask = _createStarfieldMask(im, contours, area, isBigContour, None, blackenLowerPart) starfieldAreaRatio = np.sum(mask) / (mask.shape[0]*mask.shape[1]) while starfieldAreaRatio < 0.1: # A part other then the starfield was probably picked as reference threshold because it was darker. # Remember that the first spike in the histogram (=darkest part) is used to determine the threshold. # To fix this situation, we just raise the threshold and hope for the best. print('Starfield area is only ' + "{0:.2f}".format(starfieldAreaRatio*100) +\ '% (< 10%). Trying a higher threshold. ' +\ '(' + os.path.basename(imagePath) + ')') fudge += 20 binary, hist, threshold, firstSpike = _binarizeStarfieldImage(imgray, fudge=fudge) contours, area, isBigContour, isSmallLongContour, isSmallShortContour = _findAndCategorizeContours(binary) # FIXME use small long contours as well for masking, why did we disable that? mask = _createStarfieldMask(im, contours, area, isBigContour, None, blackenLowerPart) starfieldAreaRatio = np.sum(mask) / (mask.shape[0]*mask.shape[1]) if starfieldAreaRatio >= 0.1: print('Starfield area is now ' + "{0:.2f}".format(starfieldAreaRatio*100) + '%') elif fudge > 100: print('giving up') break if debugPathPrefix and debugHistogramPath: vlines = [(firstSpike, 'red'), (threshold, 'blue')] try: saveFig(debugHistogramPath, drawHistogram(hist, vlines, xlabel='Intensity', ylabel='Pixel Count', linecolor='black')) except: ex = traceback.format_exc() with open(debugPathPrefix + 'matplotlib.EXCEPTION', 'w') as fp: fp.write(ex) if debugPathPrefix and debugThresholdedImagePath: cv.imwrite(debugThresholdedImagePath, binary, [IMWRITE_PNG_COMPRESSION, 9]) if debugPathPrefix and debugContoursImagePath: imContours = im.copy() cv.drawContours(imContours,contours[isBigContour],-1,red,2) cv.drawContours(imContours,contours[isSmallLongContour],-1,orange,2) cv.drawContours(imContours,contours[isSmallShortContour],-1,green,2) cv.imwrite(debugContoursImagePath, imContours, [IMWRITE_JPEG_QUALITY, debugJpegQuality]) del imContours if debugPathPrefix and debugContoursMaskImagePath: # draw the borders of the current mask onto the original image imContoursMask = im.copy() res = _findAndCategorizeContours(mask) contours = res[0] cv.drawContours(imContoursMask,contours,-1,(255,255,255),3) cv.imwrite(debugContoursMaskImagePath, imContoursMask, [IMWRITE_JPEG_QUALITY, debugJpegQuality]) del imContoursMask imgray[~mask] = 0 # Step 2: Filter out dark areas which are probably not starfield # Step 2a: try to find lines and mask blocks containing them binary = _masked_adaptive_threshold(imgray, mask, 255, 89, -1) if debugPathPrefix and debugAdaptiveThresholdedImagePath: binaryC = cv.cvtColor(binary, cv.COLOR_GRAY2BGR) binary = cv.medianBlur(binary, 3) lines = cv.HoughLinesP(binary.copy(), 1, pi/180, 200, minLineLength=100, maxLineGap=4) blockShape = _getBlockShape(im) blockH, blockW = blockShape blockViewMask = view_as_blocks(mask, blockShape) if lines is not None: # draw all lines on a blank binary image # then for each block check if it contains part of a line imFilledOffenders = np.zeros(mask.shape, np.uint8) for line in lines[0,:]: cv.line(imFilledOffenders, (line[0], line[1]), (line[2], line[3]), 255) if debugPathPrefix and debugAdaptiveThresholdedImagePath: cv.line(binaryC, (line[0], line[1]), (line[2], line[3]), red, 5) blockViewOffenders = view_as_blocks(imFilledOffenders, blockShape) isBlockContainingOffenders = (blockViewOffenders==255).any(axis=-1).any(axis=-1) blockViewMask[isBlockContainingOffenders] = False if debugPathPrefix and debugAdaptiveThresholdedImagePath: cv.imwrite(debugAdaptiveThresholdedImagePath, binaryC, [IMWRITE_PNG_COMPRESSION, 9]) # Step 2b: make very dark pixels black and mask all-black blocks if ignoreVeryDark: if debugPathPrefix and debugCutoffImagePath: wasStarfieldBlock = blockViewMask.all(axis=-1).all(axis=-1) imgrayCutoff = imgray.copy() # blurring will wash out very tiny artifacts (which could also be faint small stars) # this helps here because we require that absolutely all of a block must be black imgrayCutoff = cv.blur(imgrayCutoff, (3,3)) cutoffThreshold = max(30, firstSpike + 20) print('cutoffThreshold:', cutoffThreshold, os.path.basename(imagePath)) imgrayCutoff[imgrayCutoff<cutoffThreshold] = 0 blockViewCutoff = view_as_blocks(imgrayCutoff, blockShape) isBlockPureBlack = (blockViewCutoff==0).all(axis=-1).all(axis=-1) if debugPathPrefix and debugCutoffImagePath: imCutoff = im.copy() imCutoff[~mask] = 0 blockViewCutoffC = view_as_blocks(imCutoff, (blockH,blockW,3)) blockGotMasked = np.logical_and(wasStarfieldBlock, isBlockPureBlack) # draw rectangle around each block that was masked blockViewCutoffC[blockGotMasked,0,:4,:] = red blockViewCutoffC[blockGotMasked,0,-4:,:] = red blockViewCutoffC[blockGotMasked,0,:,:4] = red blockViewCutoffC[blockGotMasked,0,:,-4:] = red cv.imwrite(debugCutoffImagePath, imCutoff, [IMWRITE_JPEG_QUALITY, debugJpegQuality]) del imCutoff blockViewMask[isBlockPureBlack] = False # Step 3: Filter out lonely starfield blocks (surrounded by non-starfield blocks) isStarfieldBlock = blockViewMask.all(axis=-1).all(axis=-1) neighborCountKernel = np.ones((3,3), dtype=int) neighborCountKernel[1,1] = 0 neighbors = convolve2d(isStarfieldBlock.astype(int), neighborCountKernel, mode='same') isLonelyBlock = np.logical_and(isStarfieldBlock, neighbors == 0) blockViewMask[isLonelyBlock] = False # estimate noise level using the biggest starfield rectangle we got (rectY,rectX), (rectH,rectW) = _max_size_rectangle(isStarfieldBlock, value=True) rectY, rectH = rectY*blockH, rectH*blockH rectX, rectW = rectX*blockW, rectW*blockW imgrayBiggestRect = imgray[rectY:rectY+rectH, rectX:rectX+rectW] sigma = _estimateNoiseLevel(imgrayBiggestRect) print('Sigma:', sigma) if debugPathPrefix: with open(os.path.join(debugPathPrefix + '.sigma'), 'w') as fp: fp.write(str(sigma)) return mask, sigma
def _testStereographicMap(self): m = _getMappingSouth() m = m.maskedByElevation(10) m = resample(m, arcsecPerPx=100, method='mean') saveFig('test_stereo.svg', drawStereographic(m), dpi=200)
def _testBBPole2(self): bb = BoundingBox(latSouth=35.3446724767, lonWest=-180.0, latNorth=90.0, lonEast=180.0) m = _getMappingNorth() m = resample(m, arcsecPerPx=100, method='mean') saveFig('test_bb.png', drawStereographic(m, boundingBox=bb))
lats = masked.lats lons = masked.lons # plt.scatter(lats, lons, np.array(masked.img).flatten()) # plt.show() # for i in range(0, len(lats)): # #print(lats[i]) # if lats[i] != lats[i] or lons[i] != lons[i]: # del lats[i] # del lons[i] # del img[i:i+3] # # else: # # print(lats[i]) # print(len(img)) # print(len(lats)) # print(len(lons)) # f = open('/home/lianqiang/data/img'+number, 'a', 1000) # f.write(str(img)) # f.close() # f = open('/home/lianqiang/data/lats'+number, 'a', 1000) # f.write(str(lats)) # f.close() # f = open('/home/lianqiang/data/lons'+number, 'a', 1000) # f.write(str(lons)) # f.close() saveFig('/home/lianqiang/data/map_test.png', draw.drawPlot(masked)) #saveFig('/home/lianqiang/data/map.png', draw.drawStereographic(masked, lineDelta=2, width=1600, height=1000,)) #saveFig('/home/lianqiang/data/astrometry.jpg', draw.drawReferenceStars(mapping, scale=2))
def _testSingle(self): provider = ISSMappingProvider(url, cacheFolder, altitude=110) mapping = provider.get(datetime(2012,1,25,9,26,57)) mapping = resample(mapping, arcsecPerPx=100) saveFig('test.png', drawStereographicMLatMLT(mapping))
def maskStarfield(imagePath, channel=None, blackenLowerPart=True, ignoreVeryDark=True, debugPathPrefix=None, debugJpegQuality=80): """ Automatic masking of the starfield in the given image using a combination of image processing and object detection steps. :param str channel: the channel to use for analysis 'R','G','B', or None for combining all channels into a grayscale image :param bool blackenLowerPart: If the earth is in the lower part of the image then this should be set to True as it will broadly mask parts of the earth not detected otherwise. :param bool ignoreVeryDark: If True, then areas (block) which are almost totally black are not considered as starfield. This is sometimes useful to ignore very dark spacecraft structures which would later on be detected as stars. :param str debugPathPrefix: if given, the folder in which to store debug images illustrating the different processing stages :param int debugJpegQuality: JPEG quality from 0 to 100 used for storing debug images; only contour images are currently saved as JPEG :rtype: tuple (mask, sigma) """ if debugPathPrefix: red = (0, 0, 255) green = (0, 255, 0) orange = (0, 106, 255) debugHistogramPath = debugPathPrefix + 'hist.svg' debugThresholdedImagePath = debugPathPrefix + 'thresh.png' debugContoursImagePath = debugPathPrefix + 'cont.jpg' debugContoursMaskImagePath = debugPathPrefix + 'cont_mask.jpg' debugAdaptiveThresholdedImagePath = debugPathPrefix + 'thresh_adapt.png' debugCutoffImagePath = debugPathPrefix + 'cutoff.jpg' if isinstance(imagePath, np.ndarray): # assume RGB image array im = np.require(imagePath, np.uint8, 'C') im = cv.cvtColor(im, cv.COLOR_RGB2BGR) imagePath = '[array]' else: im = cv.imread(imagePath) if channel is None: imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY) elif channel.lower() == 'r': imgray = im[:, :, 2] elif channel.lower() == 'g': imgray = im[:, :, 1] elif channel.lower() == 'b': imgray = im[:, :, 0] else: raise ValueError( 'channel is "{}" but must be R,G,B or None'.format(channel)) # opencv requires a contiguous array.. imgray = np.require(imgray, np.uint8, 'C') # Step 1: Find dark areas which might be starfield fudge = 20 binary, hist, threshold, firstSpike = _binarizeStarfieldImage(imgray, fudge=fudge) contours, area, isBigContour, isSmallLongContour, isSmallShortContour = _findAndCategorizeContours( binary) mask = _createStarfieldMask(im, contours, area, isBigContour, None, blackenLowerPart) starfieldAreaRatio = np.sum(mask) / (mask.shape[0] * mask.shape[1]) while starfieldAreaRatio < 0.1: # A part other then the starfield was probably picked as reference threshold because it was darker. # Remember that the first spike in the histogram (=darkest part) is used to determine the threshold. # To fix this situation, we just raise the threshold and hope for the best. print('Starfield area is only ' + "{0:.2f}".format(starfieldAreaRatio*100) +\ '% (< 10%). Trying a higher threshold. ' +\ '(' + os.path.basename(imagePath) + ')') fudge += 20 binary, hist, threshold, firstSpike = _binarizeStarfieldImage( imgray, fudge=fudge) contours, area, isBigContour, isSmallLongContour, isSmallShortContour = _findAndCategorizeContours( binary) # FIXME use small long contours as well for masking, why did we disable that? mask = _createStarfieldMask(im, contours, area, isBigContour, None, blackenLowerPart) starfieldAreaRatio = np.sum(mask) / (mask.shape[0] * mask.shape[1]) if starfieldAreaRatio >= 0.1: print('Starfield area is now ' + "{0:.2f}".format(starfieldAreaRatio * 100) + '%') elif fudge > 100: print('giving up') break if debugPathPrefix and debugHistogramPath: vlines = [(firstSpike, 'red'), (threshold, 'blue')] try: saveFig( debugHistogramPath, drawHistogram(hist, vlines, xlabel='Intensity', ylabel='Pixel Count', linecolor='black')) except: ex = traceback.format_exc() with open(debugPathPrefix + 'matplotlib.EXCEPTION', 'w') as fp: fp.write(ex) if debugPathPrefix and debugThresholdedImagePath: cv.imwrite(debugThresholdedImagePath, binary, [IMWRITE_PNG_COMPRESSION, 9]) if debugPathPrefix and debugContoursImagePath: imContours = im.copy() cv.drawContours(imContours, contours[isBigContour], -1, red, 2) cv.drawContours(imContours, contours[isSmallLongContour], -1, orange, 2) cv.drawContours(imContours, contours[isSmallShortContour], -1, green, 2) cv.imwrite(debugContoursImagePath, imContours, [IMWRITE_JPEG_QUALITY, debugJpegQuality]) del imContours if debugPathPrefix and debugContoursMaskImagePath: # draw the borders of the current mask onto the original image imContoursMask = im.copy() res = _findAndCategorizeContours(mask) contours = res[0] cv.drawContours(imContoursMask, contours, -1, (255, 255, 255), 3) cv.imwrite(debugContoursMaskImagePath, imContoursMask, [IMWRITE_JPEG_QUALITY, debugJpegQuality]) del imContoursMask imgray[~mask] = 0 # Step 2: Filter out dark areas which are probably not starfield # Step 2a: try to find lines and mask blocks containing them binary = _masked_adaptive_threshold(imgray, mask, 255, 89, -1) if debugPathPrefix and debugAdaptiveThresholdedImagePath: binaryC = cv.cvtColor(binary, cv.COLOR_GRAY2BGR) binary = cv.medianBlur(binary, 3) lines = cv.HoughLinesP(binary.copy(), 1, pi / 180, 200, minLineLength=100, maxLineGap=4) blockShape = _getBlockShape(im) blockH, blockW = blockShape blockViewMask = view_as_blocks(mask, blockShape) if lines is not None: # draw all lines on a blank binary image # then for each block check if it contains part of a line imFilledOffenders = np.zeros(mask.shape, np.uint8) for line in lines[0, :]: cv.line(imFilledOffenders, (line[0], line[1]), (line[2], line[3]), 255) if debugPathPrefix and debugAdaptiveThresholdedImagePath: cv.line(binaryC, (line[0], line[1]), (line[2], line[3]), red, 5) blockViewOffenders = view_as_blocks(imFilledOffenders, blockShape) isBlockContainingOffenders = (blockViewOffenders == 255).any( axis=-1).any(axis=-1) blockViewMask[isBlockContainingOffenders] = False if debugPathPrefix and debugAdaptiveThresholdedImagePath: cv.imwrite(debugAdaptiveThresholdedImagePath, binaryC, [IMWRITE_PNG_COMPRESSION, 9]) # Step 2b: make very dark pixels black and mask all-black blocks if ignoreVeryDark: if debugPathPrefix and debugCutoffImagePath: wasStarfieldBlock = blockViewMask.all(axis=-1).all(axis=-1) imgrayCutoff = imgray.copy() # blurring will wash out very tiny artifacts (which could also be faint small stars) # this helps here because we require that absolutely all of a block must be black imgrayCutoff = cv.blur(imgrayCutoff, (3, 3)) cutoffThreshold = max(30, firstSpike + 20) print('cutoffThreshold:', cutoffThreshold, os.path.basename(imagePath)) imgrayCutoff[imgrayCutoff < cutoffThreshold] = 0 blockViewCutoff = view_as_blocks(imgrayCutoff, blockShape) isBlockPureBlack = (blockViewCutoff == 0).all(axis=-1).all(axis=-1) if debugPathPrefix and debugCutoffImagePath: imCutoff = im.copy() imCutoff[~mask] = 0 blockViewCutoffC = view_as_blocks(imCutoff, (blockH, blockW, 3)) blockGotMasked = np.logical_and(wasStarfieldBlock, isBlockPureBlack) # draw rectangle around each block that was masked blockViewCutoffC[blockGotMasked, 0, :4, :] = red blockViewCutoffC[blockGotMasked, 0, -4:, :] = red blockViewCutoffC[blockGotMasked, 0, :, :4] = red blockViewCutoffC[blockGotMasked, 0, :, -4:] = red cv.imwrite(debugCutoffImagePath, imCutoff, [IMWRITE_JPEG_QUALITY, debugJpegQuality]) del imCutoff blockViewMask[isBlockPureBlack] = False # Step 3: Filter out lonely starfield blocks (surrounded by non-starfield blocks) isStarfieldBlock = blockViewMask.all(axis=-1).all(axis=-1) neighborCountKernel = np.ones((3, 3), dtype=int) neighborCountKernel[1, 1] = 0 neighbors = convolve2d(isStarfieldBlock.astype(int), neighborCountKernel, mode='same') isLonelyBlock = np.logical_and(isStarfieldBlock, neighbors == 0) blockViewMask[isLonelyBlock] = False # estimate noise level using the biggest starfield rectangle we got (rectY, rectX), (rectH, rectW) = _max_size_rectangle(isStarfieldBlock, value=True) rectY, rectH = rectY * blockH, rectH * blockH rectX, rectW = rectX * blockW, rectW * blockW imgrayBiggestRect = imgray[rectY:rectY + rectH, rectX:rectX + rectW] sigma = _estimateNoiseLevel(imgrayBiggestRect) print('Sigma:', sigma) if debugPathPrefix: with open(os.path.join(debugPathPrefix + '.sigma'), 'w') as fp: fp.write(str(sigma)) return mask, sigma
def testIndxPlot(self): imagePath = getResourcePath('ISS030-E-102170_dc.jpg') wcsPath = getResourcePath('ISS030-E-102170_dc.wcs') figax = drawIndxPlot(imagePath, wcsPath=wcsPath, useWebCatalog=True, webCatalogLimit=200) saveFig('test_indx.png', figax)
def _testHorizonImage(self): m = _getMappingNorth() saveFig('horizon.jpg', drawHorizon(m))