def __init__(self, layer=None, force=False, mode='amplitude', *args, **kwargs): super(GenPyramid, self).__init__(*args, **kwargs) if layer is None: self.layer = pyrat.data.active else: self.layer = layer self.hdfgroup = pyrat.data.layers[self.layer].group query = pyrat.data.queryLayer(self.layer) self.force = force # force recalculation self.mode = mode # rescaling method self.scale = 0 self.lshape = (np.prod(query['lshape']), ) self.dshape = query['dshape'] self.cut = [dim // 2 * 2 for dim in self.dshape] self.shp = [dim // 2 * 2 for dim in self.dshape] self.dset = [] self.nblock = 0 self.progress = ProgressBar(' Updating View', 3 * self.dshape[-2] / 128)
def run(self, *args, **kwargs): l_cov = pyrat.data.active outsize = pyrat.data.shape[-2:] # STEP0: Random initialisation l_init = self.layer_fromfunc(self.init_random, size=outsize, nclass=self.nclass) P = ProgressBar(' ' + self.name, self.niter) P.update(0) for iter in range(self.niter): # STEP1: Calculate cluster centres (and their frequency) pyrat.activate([l_cov, l_init], silent=True) cc, nc = self.layer_accumulate(self.calc_centers, nclass=self.nclass, combine=self.combine_mean) pyrat.delete(l_init, silent=True) # STEP2: Eliminate empty classes for k, n in enumerate(nc): if n == 0: del cc[k] del nc[k] # STEP3: Calculate class memberships pyrat.activate(l_cov, silent=True) l_init = self.layer_process(self.assign_classes, centers=cc) P.update(iter+1) del P pyrat.activate(l_init) return l_init
def layer_fromfunc(self, func, size=(1, 1), silent=True, **kwargs): """ Generates a new layer from the return of its method 'func', called with **kwargs (and possible args stored in in the keyword 'args' as tuple). The size of the produced layer must be passed in the 'size' keyword. Returns the name of the new layer(s) """ if self.vblock: self.initBP(size[-1]) kwargs["size"] = (size[-2], self.blocksize) else: self.initBP(size[-2]) kwargs["size"] = (self.blocksize, size[-1]) if len(self.blocks) > 1 and self.nthreads > 1: idx = [self.blocks[i:i + self.nthreads] for i in range(0, len(self.blocks), self.nthreads)] else: idx = [[block] for block in self.blocks] kwargs["meta"] = {} nb1 = 0 # input block number nb2 = 0 # output block number if silent is False: P = ProgressBar(' ' + self.name, len(self.blocks)) P.update(0) for bidx in idx: inputs = [] for ix in bidx: kwargs_copy = copy.deepcopy(kwargs) if self.vblock: kwargs_copy['block'] = (0, size[-2]) + tuple(self.blocks[nb1]) else: kwargs_copy['block'] = tuple(self.blocks[nb1])+(0, size[-1]) kwargs_copy['valid'] = tuple(self.valid[nb1]) inputs.append((self, func.__name__, kwargs_copy)) nb1 += 1 if self.nthreads > 1: result = pyrat.pool.imap(exec_out, inputs) else: result = map(exec_out, inputs) for res in result: if nb2 == 0: if isinstance(res[0], list) or isinstance(res[0], tuple): self.output = [] for n, re in enumerate(res[0]): self.output.append(pyrat.data.addLayer(dtype=re.dtype, shape=size)) else: self.output = pyrat.data.addLayer(dtype=res[0].dtype, shape=size) self.save_block(res[0], nb2) nb2 += 1 if silent is False: P.update(nb2) if silent is False: del P return self.output
def layer_extract(self, func, silent=True, **kwargs): if 'layer' in kwargs: self.input = kwargs['layer'] else: self.input = pyrat.data.active query = pyrat.data.queryLayer(self.input) if isinstance(query, list): dshape = query[0]['shape'] else: dshape = query['shape'] if self.vblock: self.initBP(dshape[-1]) else: self.initBP(dshape[-2]) if len(self.blocks) > 1 and self.nthreads > 1: idx = [self.blocks[i:i + self.nthreads] for i in range(0, len(self.blocks), self.nthreads)] else: idx = [[block] for block in self.blocks] out = [] nb = 0 metain = pyrat.data.getAnnotation(layer=self.input) if silent is False: P = ProgressBar(' ' + self.name, len(self.blocks)) P.update(0) for bidx in idx: meta = copy.deepcopy(metain) inputs = [] for ix in bidx: data = self.read_block(nb) kwargs_copy = copy.deepcopy(kwargs) kwargs_copy["args"] = data kwargs_copy["meta"] = meta if self.vblock: kwargs_copy['block'] = (0, dshape[-2]) + tuple(self.blocks[nb]) else: kwargs_copy['block'] = tuple(self.blocks[nb])+(0, dshape[-1]) kwargs_copy['valid'] = tuple(self.valid[nb]) inputs.append((self, func.__name__, kwargs_copy)) nb += 1 if silent is False: P.update(nb) if self.nthreads > 1: result = pyrat.pool.imap(exec_out, inputs) else: result = map(exec_out, inputs) for res in result: out.append(res[0]) if silent is False: del P return self.input
def run(self, *args, **kwargs): P = ProgressBar(' ' + self.name, 10) bounds = opt.fmin(self.optf, [0.5, 2.0], args=(self.looks, self.sigma), disp=False) # calc sigma bounds newsig = self.newsig(bounds[0], bounds[1], sigrng=self.sigma, looks=self.looks) # calc new stddev P.update(0) perc = 100.0 - self.perc * 100.0 # point target theshold pthreshold = np.mean( self.layer_accumulate(self.estimate_percentile, type=self.type, perc=perc)) P.update(2) layer = self.layer_process(self.leeimproved, bounds=bounds, newsig=newsig, thres=pthreshold, looks=self.looks, win=self.win, type=self.type) P.update(10) del P pyrat.activate(layer) return layer
def run(self, *args, **kwargs): l_cov = pyrat.data.active outsize = pyrat.data.shape[-2:] # STEP0: Random initialisation l_init = self.layer_fromfunc(self.init_random, size=outsize, nclass=self.nclass) P = ProgressBar(' ' + self.name, self.niter) P.update(0) for iter in range(self.niter): # STEP1: Calculate cluster centres (and their frequency) pyrat.activate([l_cov, l_init], silent=True) cc, nc = self.layer_accumulate(self.calc_centers, nclass=self.nclass, combine=self.combine_mean) pyrat.delete(l_init, silent=True) # STEP2: Eliminate empty classes for k, n in enumerate(nc): if n == 0: del cc[k] del nc[k] # STEP3: Calculate class memberships pyrat.activate(l_cov, silent=True) l_init = self.layer_process(self.assign_classes, centers=cc) P.update(iter + 1) del P pyrat.activate(l_init) return l_init
def layer_extract(self, func, silent=True, **kwargs): if 'layer' in kwargs: self.input = kwargs['layer'] else: self.input = pyrat.data.active query = pyrat.data.queryLayer(self.input) if isinstance(query, list): dshape = query[0]['shape'] else: dshape = query['shape'] if self.vblock: self.initBP(dshape[-1]) else: self.initBP(dshape[-2]) if len(self.blocks) > 1 and self.nthreads > 1: idx = [ self.blocks[i:i + self.nthreads] for i in range(0, len(self.blocks), self.nthreads) ] else: idx = [[block] for block in self.blocks] out = [] nb = 0 metain = pyrat.data.getAnnotation(layer=self.input) if silent is False: P = ProgressBar(' ' + self.name, len(self.blocks)) P.update(0) for bidx in idx: meta = copy.deepcopy(metain) inputs = [] for ix in bidx: data = self.read_block(nb) kwargs_copy = copy.deepcopy(kwargs) kwargs_copy["args"] = data kwargs_copy["meta"] = meta if self.vblock: kwargs_copy['block'] = (0, dshape[-2]) + tuple( self.blocks[nb]) else: kwargs_copy['block'] = tuple( self.blocks[nb]) + (0, dshape[-1]) kwargs_copy['valid'] = tuple(self.valid[nb]) inputs.append((self, func.__name__, kwargs_copy)) nb += 1 if silent is False: P.update(nb) if self.nthreads > 1: result = multimap(inputs) else: result = map(exec_out, inputs) for res in result: out.append(res[0]) if silent is False: del P return self.input
def layer_fromfunc(self, func, size=(1, 1), silent=True, **kwargs): """ Generates a new layer from the return of its method 'func', called with **kwargs (and possible args stored in in the keyword 'args' as tuple). The size of the produced layer must be passed in the 'size' keyword. Returns the name of the new layer(s) """ if self.vblock: self.initBP(size[-1]) kwargs["size"] = (size[-2], self.blocksize) else: self.initBP(size[-2]) kwargs["size"] = (self.blocksize, size[-1]) if len(self.blocks) > 1 and self.nthreads > 1: idx = [self.blocks[i:i + self.nthreads] for i in range(0, len(self.blocks), self.nthreads)] else: idx = [[block] for block in self.blocks] kwargs["meta"] = {} nb1 = 0 # input block number nb2 = 0 # output block number if silent is False: P = ProgressBar(' ' + self.name, len(self.blocks)) P.update(0) for bidx in idx: inputs = [] for ix in bidx: kwargs_copy = copy.deepcopy(kwargs) if self.vblock: kwargs_copy['block'] = (0, size[-2]) + tuple(self.blocks[nb1]) else: kwargs_copy['block'] = tuple(self.blocks[nb1]) + (0, size[-1]) kwargs_copy['valid'] = tuple(self.valid[nb1]) inputs.append((self, func.__name__, kwargs_copy)) nb1 += 1 if self.nthreads > 1: result = multimap(inputs) else: result = map(exec_out, inputs) for res in result: if nb2 == 0: if isinstance(res[0], list) or isinstance(res[0], tuple): self.output = [] for n, re in enumerate(res[0]): self.output.append(pyrat.data.addLayer(dtype=re.dtype, shape=size)) else: self.output = pyrat.data.addLayer(dtype=res[0].dtype, shape=size) self.save_block(res[0], nb2) nb2 += 1 if silent is False: P.update(nb2) if silent is False: del P pyrat.data.setAnnotation(res[1], layer=self.output) # add meta data to output layer return self.output
def __init__(self, layer=None, force=False, mode='amplitude', *args, **kwargs): super(GenPyramid, self).__init__(*args, **kwargs) if layer is None: self.layer = pyrat.data.active else: self.layer = layer self.hdfgroup = pyrat.data.layers[self.layer].group query = pyrat.data.queryLayer(self.layer) self.force = force # force recalculation self.mode = mode # rescaling method self.scale = 0 self.lshape = (np.prod(query['lshape']),) self.dshape = query['dshape'] self.cut = [dim // 2 * 2 for dim in self.dshape] self.shp = [dim // 2 * 2 for dim in self.dshape] self.dset = [] self.nblock = 0 self.progress = ProgressBar(' Updating View', 3 * self.dshape[-2] / 128)
def run(self, *args, **kwargs): P = ProgressBar(' ' + self.name, self.iter) P.update(0) for k in range(self.iter): if k != 0: oldlayer = newlayer newlayer = self.layer_process(self.srad, looks=self.looks, step=self.step, iter=k, scale=self.scale, type=self.type) if k != 0: pyrat.delete(oldlayer, silent=True) pyrat.activate(newlayer, silent=True) P.update(k + 1) del P pyrat.activate(newlayer) return newlayer
def run(self, *args, **kwargs): li = pyrat.query(layer=self.layer) odim = li.shape nry = odim[-2] odim[-2] //= self.suby odim[-1] //= self.subx outlayer = pyrat.data.addLayer(dtype=li.dtype, shape=odim) blockdim = odim.copy() blockdim[-2] = 1 P = ProgressBar(' ' + self.name, odim[-2]) P.update(0) for k in range(odim[-2]): arr = pyrat.getdata(block=(k*self.suby, (k+1)*self.suby, 0, odim[-1] * self.subx), layer=self.layer) arr = rebin(arr, tuple(blockdim)) pyrat.data.setData(arr, block=(k, k+1, 0, 0), layer=outlayer) P.update(k + 1) del P pyrat.activate(outlayer) return outlayer
class GenPyramid(): """ Generation of the presumming pyramid :author: Andreas Reigber """ def __init__(self, layer=None, force=False, mode='amplitude', *args, **kwargs): super(GenPyramid, self).__init__(*args, **kwargs) if layer is None: self.layer = pyrat.data.active else: self.layer = layer self.hdfgroup = pyrat.data.layers[self.layer].group query = pyrat.data.queryLayer(self.layer) self.force = force # force recalculation self.mode = mode # rescaling method self.scale = 0 self.lshape = (np.prod(query['lshape']), ) self.dshape = query['dshape'] self.cut = [dim // 2 * 2 for dim in self.dshape] self.shp = [dim // 2 * 2 for dim in self.dshape] self.dset = [] self.nblock = 0 self.progress = ProgressBar(' Updating View', 3 * self.dshape[-2] / 128) def __del__(self): del self.progress def run(self, *args, **kwargs): # print("pyramid level", self.scale) if self.scale == 0: ilay = 'D' olay = 'P/0' ishp = [dim // 2 * 2 for dim in self.dshape] oshp = [dim // 2 * 2 for dim in self.dshape] else: ilay = 'P/' + str(self.scale - 1) olay = 'P/' + str(self.scale) ishp = [dim // 2 * 2 for dim in self.dshape] oshp = [dim // 2 for dim in self.dshape] idat = self.hdfgroup[ilay] if olay in self.hdfgroup and self.force is False: self.dset.append(self.hdfgroup[olay]) self.scale += 1 self.dshape = oshp self.progress.update(self.scale * 100) self.run() else: odat = self.hdfgroup.require_dataset(olay, self.lshape + tuple(oshp), 'float32') self.dset.append(odat) if min(oshp) > 1: idx, ivalid, ibs = self.calc_blocks(ishp[-2], pack=True, blocksize=128) odx, ovalid, obs = self.calc_blocks(oshp[-2], pack=False, blocksize=ibs // (ishp[-2] // oshp[-2])) nb = 0 for bidx in idx: inputs = [] for ix in bidx: data = idat[..., ix[0]:ix[1], 0:ishp[-1]] inputs.append( (data, self.lshape + (obs, oshp[1]), self.mode)) result = pyrat.pool.imap(subsample, inputs) # result = map(absrebin, inputs) for res in result: odat[..., odx[nb][0] + ovalid[nb][0]:odx[nb][1], :] = res[ ..., ovalid[nb][0]:ovalid[nb][1], :] nb += 1 self.nblock += 1 self.progress.update(self.nblock) self.scale += 1 self.dshape = oshp self.run() return self.dset def calc_blocks(self, size, blocksize=128, pack=False): nthreads = pyrat.pool._processes if blocksize > size: # maximum equal image size blocksize = size blocks = [[0, blocksize]] while blocks[-1][1] < size: blocks.append([blocks[-1][1], blocks[-1][1] + blocksize]) offset = blocks[-1][1] - size # last block starts earlier blocks[-1][0] -= offset # with increased overlap blocks[-1][1] -= offset valid = [0] * len( blocks) # calculate the valid part of each block (start, end) for k, block in enumerate(blocks): if k == len(blocks) - 1: # last block if k == 0: valid[k] = block # only one block else: valid[k] = [blocks[-2][1] - block[0], block[1] - block[0]] else: # middle block valid[k] = [0, block[1] - block[0]] if pack is True: if len(blocks) > 1 and nthreads > 1: blocks = [ blocks[i:i + nthreads] for i in range(0, len(blocks), nthreads) ] else: blocks = [[block] for block in blocks] return blocks, valid, blocksize
def reader(self, *args, **kwargs): fN = len(self.filenames) meta = {} if 'ppfiles' in self.__dict__: pp = Xml2Py(self.ppfiles[0]) meta['sensor'] = 'DLR F-SAR' meta['band'] = band meta['prf'] = pp.prf meta['c0'] = pp.c0 meta['rd'] = pp.rd meta['rs'] = pp.rsf/subx meta['lam'] = pp.__dict__['lam'] meta['band'] = pp.band meta['antdir'] = pp.antdir meta['v0'] = pp.v0 meta['h0'] = pp.h0 meta['terrain'] = pp.terrain meta['bw'] = pp.cbw if (len(self.rat_block) == 4): meta['rd'] += self.rat_block[0] / meta['rs'] meta['CH_pol'] = [' ']*(fN**2) pind = 0 for f1 in self.ppfiles: pp1 = Xml2Py(f1) for f2 in self.ppfiles: pp2 = Xml2Py(f2) meta['CH_pol'][pind] = pp1.polarisation+pp2.polarisation ppind += 1 file = RatFile(self.filenames[0]) if len(self.rat_block) != 4: imDim = file.dim[0:2][::-1] else: imDim = self.rat_block[2:4][::-1] imDim = np.asarray(imDim,dtype='i4') blenOut = 100 blenIn = blenOut*self.suby nBlocks = int(np.ceil(imDim[0]/float(blenIn))) sub = np.asarray([self.suby,self.subx],dtype='i4') oDim = imDim / sub meta['nrg'] = oDim[0] meta['naz'] = oDim[1] P = ProgressBar(' ' + self.name, nBlocks) P.update(0) for n in range(nBlocks): rInd = np.minimum(np.asarray([n*blenIn,(n+1)*blenIn]),imDim[0]) wInd = rInd/sub if wInd[0] >= wInd[1]: continue binInd = [(wInd[1]-wInd[0])*sub[0],oDim[1]*sub[1]] bDim = (wInd[1]-wInd[0],oDim[1]) for u in range(fN): blk = [0,rInd[0],imDim[1],rInd[1]-rInd[0]] if len(self.rat_block) == 4: blk[0] = self.rat_block[0] blk[1] += self.rat_block[1] blk[2] = self.rat_block[2] arr1 = rrat(self.filenames[u],block=blk) arr1 = arr1[0:binInd[0],0:binInd[1]] if (u == 0) and (n == 0): covar = np.empty((fN,fN)+tuple(oDim),dtype=arr1.dtype) covar[u,u,wInd[0]:wInd[1],:] = rebin(np.abs(arr1)**2,bDim) for v in range(u+1,fN): arr2 = rrat(self.filenames[v],block=blk) covar[u,v,wInd[0]:wInd[1],:] = rebin(arr1*np.conj(arr2),bDim) covar[v,u,wInd[0]:wInd[1],:] = np.conj(covar[u,v,wInd[0]:wInd[1],:]) P.update(n+1) return covar, meta
def layer_process(self, func, silent=True, **kwargs): """ Generates a new layer from the return of its method 'func', called with **kwargs (and possible args stored in in the keyword 'args' as tuple). The size of the produced layer must be passed in the 'size' keyword. Returns the name of the new layer(s) """ if 'layer' in kwargs: self.input = kwargs['layer'] else: self.input = pyrat.data.active if any([isinstance(foo, list) for foo in self.input]): layshp = self.input self.input = flattenlist(self.input) nested = True else: nested = False query = pyrat.data.queryLayer(self.input) if isinstance(query, list): dshape = query[0]['shape'] else: dshape = query['shape'] if self.vblock: # init block processing self.initBP(dshape[-1]) else: self.initBP(dshape[-2]) if len(self.blocks) > 1 and self.nthreads > 1: # group chunks of blocks idx = [self.blocks[i:i + self.nthreads] for i in range(0, len(self.blocks), self.nthreads)] else: idx = [[block] for block in self.blocks] metain = pyrat.data.getAnnotation(layer=self.input) nb1 = 0 # input block number nb2 = 0 # output block number if silent is False: P = ProgressBar(' ' + self.name, len(self.blocks)) P.update(0) for bidx in idx: # loop over chunks of blocks meta = copy.deepcopy(metain) inputs = [] for ix in bidx: # loop over blocks in chunk data = self.read_block(nb1) if nested is True: data = unflattenlist(data, layshp) kwargs_copy = copy.deepcopy(kwargs) kwargs_copy["args"] = data kwargs_copy["meta"] = meta if self.vblock: kwargs_copy['block'] = (0, dshape[-2]) + tuple(self.blocks[nb1]) else: kwargs_copy['block'] = tuple(self.blocks[nb1])+(0, dshape[-1]) kwargs_copy['valid'] = tuple(self.valid[nb1]) inputs.append((self, func.__name__, kwargs_copy)) # accumulate inputs nb1 += 1 if self.nthreads > 1: result = pyrat.pool.imap(exec_out, inputs) # do the multiprocessing else: result = map(exec_out, inputs) # or avoid it... for res in result: # loop over output blocks (in chunk) metaout = res[1] # meta data (possibly modified) if nb2 == 0: # first block -> generate new layer(s) if isinstance(res[0], list) or isinstance(res[0], tuple): self.output = [] for n, re in enumerate(res[0]): lshape = re.shape[0:-2] # layer geometry if self.vblock: dshape = (re.shape[-2], dshape[-1]) else: dshape = (dshape[-2], re.shape[-1]) if self.blockprocess is False: # no blockprocessing lshape = () # -> entire image dshape = re.shape self.output.append(pyrat.data.addLayer(dtype=re.dtype, shape=lshape+dshape)) else: lshape = res[0].shape[0:-2] # layer geometry if self.vblock: dshape = (res[0].shape[-2], dshape[-1]) else: dshape = (dshape[-2], res[0].shape[-1]) if self.blockprocess is False: # no blockprocessing lshape = () # -> entire image dshape = res[0].shape self.output = pyrat.data.addLayer(dtype=res[0].dtype, shape=lshape+dshape) self.save_block(res[0], nb2) nb2 += 1 if silent is False: P.update(nb2) if silent is False: del P pyrat.data.setAnnotation(metaout, layer=self.output) # add meta data to output layer return self.output # return output layer
class GenPyramid(): """ Generation of the presumming pyramid :author: Andreas Reigber """ def __init__(self, layer=None, force=False, mode='amplitude', *args, **kwargs): super(GenPyramid, self).__init__(*args, **kwargs) if layer is None: self.layer = pyrat.data.active else: self.layer = layer self.hdfgroup = pyrat.data.layers[self.layer].group query = pyrat.data.queryLayer(self.layer) self.force = force # force recalculation self.mode = mode # rescaling method self.scale = 0 self.lshape = (np.prod(query['lshape']),) self.dshape = query['dshape'] self.cut = [dim // 2 * 2 for dim in self.dshape] self.shp = [dim // 2 * 2 for dim in self.dshape] self.dset = [] self.nblock = 0 self.progress = ProgressBar(' Updating View', 3 * self.dshape[-2] / 128) def __del__(self): del self.progress def run(self, *args, **kwargs): # print("pyramid level", self.scale) if self.scale == 0: ilay = 'D' olay = 'P/0' ishp = [dim // 2 * 2 for dim in self.dshape] oshp = [dim // 2 * 2 for dim in self.dshape] else: ilay = 'P/' + str(self.scale - 1) olay = 'P/' + str(self.scale) ishp = [dim // 2 * 2 for dim in self.dshape] oshp = [dim // 2 for dim in self.dshape] idat = self.hdfgroup[ilay] if olay in self.hdfgroup and self.force is False: self.dset.append(self.hdfgroup[olay]) self.scale += 1 self.dshape = oshp self.progress.update(self.scale * 100) self.run() else: odat = self.hdfgroup.require_dataset(olay, self.lshape + tuple(oshp), 'float32') self.dset.append(odat) if min(oshp) > 1: idx, ivalid, ibs = self.calc_blocks(ishp[-2], pack=True, blocksize=128) odx, ovalid, obs = self.calc_blocks(oshp[-2], pack=False, blocksize=ibs // (ishp[-2] // oshp[-2])) nb = 0 for bidx in idx: inputs = [] for ix in bidx: data = idat[..., ix[0]:ix[1], 0:ishp[-1]] inputs.append((data, self.lshape + (obs, oshp[1]), self.mode)) result = pyrat.pool.imap(subsample, inputs) # result = map(absrebin, inputs) for res in result: odat[..., odx[nb][0] + ovalid[nb][0]:odx[nb][1], :] = res[..., ovalid[nb][0]:ovalid[nb][1], :] nb += 1 self.nblock += 1 self.progress.update(self.nblock) self.scale += 1 self.dshape = oshp self.run() return self.dset def calc_blocks(self, size, blocksize=128, pack=False): nthreads = pyrat.pool._processes if blocksize > size: # maximum equal image size blocksize = size blocks = [[0, blocksize]] while blocks[-1][1] < size: blocks.append([blocks[-1][1], blocks[-1][1] + blocksize]) offset = blocks[-1][1] - size # last block starts earlier blocks[-1][0] -= offset # with increased overlap blocks[-1][1] -= offset valid = [0] * len(blocks) # calculate the valid part of each block (start, end) for k, block in enumerate(blocks): if k == len(blocks) - 1: # last block if k == 0: valid[k] = block # only one block else: valid[k] = [blocks[-2][1] - block[0], block[1] - block[0]] else: # middle block valid[k] = [0, block[1] - block[0]] if pack is True: if len(blocks) > 1 and nthreads > 1: blocks = [blocks[i:i + nthreads] for i in range(0, len(blocks), nthreads)] else: blocks = [[block] for block in blocks] return blocks, valid, blocksize