]> AND Private Git Repository - myo-class.git/blobdiff - topng.py
Logo AND Algorithmique Numérique Distribuée

Private GIT Repository
integrate distinction_word, for deployment
[myo-class.git] / topng.py
index 8722d98c785f756ab87452b1574fb0d97966fa3c..fde569ba6509bdf19efb0a28c282d4ec4b8b1dfb 100644 (file)
--- a/topng.py
+++ b/topng.py
@@ -2,36 +2,52 @@ import cv2
 import os
 from os.path import isdir, join
 from os import mkdir
 import os
 from os.path import isdir, join
 from os import mkdir
+from timeit import timeit
 import pydicom
 from pprint import pprint
 import numpy as np
 import pathlib
 import json
 
 import pydicom
 from pprint import pprint
 import numpy as np
 import pathlib
 import json
 
+from scipy import ndimage
+from scipy import misc
+
+
 from decimal import Decimal as d, ROUND_HALF_UP as rhu
 
 from decimal import Decimal as d, ROUND_HALF_UP as rhu
 
+# locals
+from helpers import drawcontours
+
+np.set_printoptions(edgeitems=372, linewidth=1200)# hey numpy, take advantage of my large screen
+
 PNG16_MAX = pow(2, 16) - 1 # here if thinks the heaviest weight bit is for transparency or something not in use with dicom imgs
 PNG8_MAX = pow(2, 8+1) - 1 # heaviest weight bit is 8 => 2**8, but dont forget the others: the reason of +1
 
 INPUT_DIR = '../../Data/Images_anonymous/Case_0002/'
 OUT_DIR = './generated/'
 PNG16_MAX = pow(2, 16) - 1 # here if thinks the heaviest weight bit is for transparency or something not in use with dicom imgs
 PNG8_MAX = pow(2, 8+1) - 1 # heaviest weight bit is 8 => 2**8, but dont forget the others: the reason of +1
 
 INPUT_DIR = '../../Data/Images_anonymous/Case_0002/'
 OUT_DIR = './generated/'
-#os.mkdir(outdir)
+
+CROP_SIZE = (45, 45) # (width, height)
+RED_COLOR = 100
 
 def roundall(*l):
 
 def roundall(*l):
-       return (round(e) for e in l)
+       return (int(round(e)) for e in l)
 
 def ftrim(Mat):
 
 def ftrim(Mat):
+       # ---------- FTRIM ----------
        # private func to trim the Matrix, but in one direction, vertically | horizontally
        # return the slice, don't affect the Matrix
        # private func to trim the Matrix, but in one direction, vertically | horizontally
        # return the slice, don't affect the Matrix
-       # y | : for column, x -> : for rows
+       # y | : for (top to bottom), x -> : for (left to right)
        #   v
        # not my fault, numpy architecture
        #   v
        # not my fault, numpy architecture
+
        y1 = y2 = i = 0
        y1 = y2 = i = 0
+
        while i < len(Mat):# search by top
                if Mat[i].max() > 0:
                        y1 = i
                        break
                i += 1
        while i < len(Mat):# search by top
                if Mat[i].max() > 0:
                        y1 = i
                        break
                i += 1
+
        i = len(Mat) - 1
        while i >= 0:# search by bottom
                if Mat[i].max() > 0:
        i = len(Mat) - 1
        while i >= 0:# search by bottom
                if Mat[i].max() > 0:
@@ -48,29 +64,8 @@ def trim(Mat):
        # print('horizontal:vertical', horizontal, vertical)
        return Mat[horizontal, vertical]
 
        # print('horizontal:vertical', horizontal, vertical)
        return Mat[horizontal, vertical]
 
-def mask(Mat, maskfile):
-       # return
-       xmin, ymin, xmax, ymax = minmax(Mat, maskfile)
-
-       y1 = y2 = i = 0
-       while i < len(Mat):# search by top
-               if i < ymin: 
-                       Mat[i].fill(0)# paint the row in black
-               else: break
-               i += 1
-       
-       i = len(Mat) - 1
-       while i >= 0:# search by bottom
-               if i > ymax:
-                       Mat[i].fill(0)# paint the row in black
-               else: break
-               i -= 1
-       # print('y1, y2', y1, y2)
-       # return slice(y1, y2+1)# +1 to stop at y2
-
-
 
 
-def minmax(Mat, file):
+def getxy(file):
        """
                {
                        'Image00001': [{
        """
                {
                        'Image00001': [{
@@ -93,35 +88,170 @@ def minmax(Mat, file):
                                ]
                        }]
                }
                                ]
                        }]
                }
-               return xmin, ymin, xmax, ymax
+               return [(94.377, 137.39), ...]
        """
        with open(file) as jsonfile:
                data = json.load(jsonfile)
        """
        with open(file) as jsonfile:
                data = json.load(jsonfile)
-               pprint(data)
-               
-               for imgXXX in data:pass # get the value of key ~= "Image00001", cause it's dynamic
-               
-               for obj in data[imgXXX]:pass # get the object that contains the points, cause it's a list
-               points = obj['points']
+               # pprint(data)
+
+               for imgXXX in data: pass  # get the value of key ~= "Image00001", cause it's dynamic
 
 
+               for obj in data[imgXXX]: pass  # get the object that contains the points, cause it's a list
+               points = obj['points']
 
                # print("print, ", data)
                # print("imgXXX, ", imgXXX)
                # print("points, ", points)
                # print("imgXXX, ", obj['points'])
 
                # print("print, ", data)
                # print("imgXXX, ", imgXXX)
                # print("points, ", points)
                # print("imgXXX, ", obj['points'])
-               tmp = [(pt['x'], pt['y']) for pt in points ] # extract x,y. {'x': 94.377, 'y': 137.39} => (94.377, 137.39)
-               r = np.array(tmp)
-
-               print(r) # log
-
+               tmp = [np.array( (round(pt['x']), round(pt['y'])) ).astype(np.int32) for pt in
+                          points]  # extract x,y. {'x': 94.377, 'y': 137.39} => (94.377, 137.39)
+               return np.array(tmp, dtype=np.int32)
+               # return tmp
+
+def minmax(file):
+       r = getxy(file)
+       if r is not None:
+               # print(r) # log
                xmin, ymin = np.min(r, axis=0)
                xmax, ymax = np.max(r, axis=0)
 
                xmin, ymin = np.min(r, axis=0)
                xmax, ymax = np.max(r, axis=0)
 
-               print('xmax, ymax', xmax, ymax)
-               print('xmin, ymin', xmin, ymin)
+               print('xmax, ymax', xmax, ymax)
+               print('xmin, ymin', xmin, ymin)
 
                return roundall(xmin, ymin, xmax, ymax)
 
 
                return roundall(xmin, ymin, xmax, ymax)
 
+def crop(Mat, maskfile, size=None):
+       """
+               size : if filled with a (45, 45), it will search the center of maskfile then crop by center Mat
+       """
+       xmin, ymin, xmax, ymax = minmax(maskfile)
+       if size:
+               xcenter = (xmin + xmax) / 2
+               ycenter = (ymin + ymax) / 2
+
+               # crop coords
+               ymin, xmin, ymax, xmax = roundall(xcenter - size[0], ycenter - size[0],
+                                                                 xcenter + size[1], ycenter + size[1])
+
+       return Mat[xmin:xmax, ymin:ymax]
+
+def contour(Mat, maskfiles, color, thickness=1):
+       # ---------- CONTOUR POLYGON ----------
+       # thickness = -1 => fill it
+       #                       = 1 => just draw the contour with color
+       #
+       # return new Mat
+       contours = [getxy(maskfile) for maskfile in maskfiles] # list of list of coords, 
+       # [ 
+       #       [ (3,43), (3,4) ],
+       #       [ (33,43) ]
+       # ]
+       # print("log: contours", contours)
+       if contours is not None:
+               # print('type contours', type(contours))
+               # print('contours', contours)
+               msk = np.zeros(Mat.shape, dtype=np.int32) # init all the mask with True... 
+               cv2.drawContours(msk, contours, -1, True, thickness) # affect Mat, fill the polygone with False
+               msk = msk.astype(np.bool)
+               # print('msk')
+               # print(msk)
+               # print("bool", timeit(lambda:msk, number=1000))
+               # print("normal", timeit(lambda:msk.astype(np.bool), number=1000))
+               # Mat = cv2.cvtColor(Mat, cv2.COLOR_RGB2GRAY) # apply gray
+
+               # Mat = drawcontours(Mat, contours)
+               # pass
+               Mat[msk] = color # each True in msk means 0 in Mat. msk is like a selector
+       return Mat
+
+def mask(Mat, maskfile, color=0, thickness=-1):
+       # ---------- MASK POLYGON ----------
+       # thickness = -1 => fill it
+       #                       = 1 => just draw the contour with color
+       #
+       # return new Mat
+       contours = getxy(maskfile)
+       # print("log: contours", contours)
+       if contours is not None:
+               # print('type contours', type(contours))
+               # print('contours', contours)
+               msk = np.ones(Mat.shape, dtype=np.int32) # init all the mask with True... 
+               cv2.drawContours(msk, [contours], -1, False, thickness) # affect msk, fill the polygone with False, => further, don't touch where is False
+               msk = msk.astype(np.bool)
+               # print('msk')
+               # print(msk)
+               # print("bool", timeit(lambda:msk, number=1000))
+               # print("normal", timeit(lambda:msk.astype(np.bool), number=1000))
+               # Mat = cv2.cvtColor(Mat, cv2.COLOR_RGB2GRAY) # apply gray
+
+               # Mat = drawcontours(Mat, contours)
+               # pass
+               Mat[msk] = color # each True in msk means 0 in Mat. msk is like a selector
+       return Mat
+
+
+def hollowmask(Mat, epifile, endofile, color=0, thickness=-1):
+       # ---------- MASK POLYGON ----------
+       # thickness = -1 => fill it
+       #                       = 1 => just draw the contour with color
+       #
+       # return new Mat
+       epicontours = getxy(epifile)
+       endocontours = getxy(endofile)
+       if epicontours is not None and endocontours is not None:
+               msk = np.ones(Mat.shape, dtype=np.int32) # init all the mask with True... 
+               cv2.drawContours(msk, [epicontours], -1, False, thickness) # affect msk, fill the polygone with False, => further, don't touch where is False
+               cv2.drawContours(msk, [endocontours], -1, True, thickness) #                     fill the intern polygone with True, (the holow in the larger polygon), => further, color where is True with black for example
+               msk = msk.astype(np.bool)
+               
+               Mat[msk] = color # each True in msk means 0 in Mat. msk is like a selector
+       return Mat
+
+
+def sqrmask1(Mat, maskfile):
+       # ---------- SQUARE MASK 1st approach ----------
+       # print( timeit( lambda:sqrmask1(img, epimask), number=1000 ) ) # 0.48110522600000005
+       # return new Mat
+       xmin, ymin, xmax, ymax = minmax(maskfile)
+       # print("xmin, ymin, xmax, ymax", xmin, ymin, xmax, ymax)
+
+       i = 0
+       while i < ymin:# search by top
+               Mat[i].fill(0)# paint the row in black
+               i += 1
+       
+       i = len(Mat) - 1
+       while i > ymax:# search by bottom
+               Mat[i].fill(0)# paint the row in black
+               i -= 1
+
+       i = 0
+       while i < xmin:# search by top
+               Mat.T[i, ymin:ymax+1].fill(0) # paint the column (row of the Transpose) in black, ymin and ymax to optimize, cause, I previously painted a part
+               i += 1
+
+       i = len(Mat.T) - 1
+       while i > xmax:# search by bottom
+               Mat.T[i, ymin:ymax+1].fill(0) # paint the column (row of the Transpose) in black, ymin and ymax to optimize, cause, I previously painted a part
+               i -= 1
+
+       return Mat
+
+def sqrmask2(Mat, maskfile):
+       # ---------- SQUARE MASK 2nd and best approach ----------
+       # print( timeit( lambda:sqrmask2(img, epimask), number=1000 ) ) # 0.3097705270000001
+       # return new Mat
+       xmin, ymin, xmax, ymax = minmax(maskfile)
+       # print("xmin, ymin, xmax, ymax", xmin, ymin, xmax, ymax)
+
+       msk = np.ones(Mat.shape, dtype=np.int32) # init all the mask with True... 
+       msk = msk.astype(np.bool)
+
+       msk[ymin:ymax, xmin:xmax] = False # for after, don't touch between min and max region
+       Mat[msk] = 0
+
+       return Mat
+
 def map16(array):# can be useful in future
        return array * 16
 
 def map16(array):# can be useful in future
        return array * 16
 
@@ -145,17 +275,31 @@ def affine(Mat, ab, cd):
 def getdir(filepath):
        return '/'.join(filepath.split('/')[:-1]) + '/'
 
 def getdir(filepath):
        return '/'.join(filepath.split('/')[:-1]) + '/'
 
-def topng(inputfile, outfile=None, overwrite=True, verbose=False):
+def readpng(inputfile):# just a specific func to preview a "shape = (X,Y,3)" image
+       image = misc.imread(inputfile)
+
+       print("image")
+       print("image.shape", image.shape)
+
+       for tab in image:
+               for i, row in enumerate(tab):
+                       print(row[0]*65536 + row[0]*256 + row[0], end=" " if i % image.shape[0] != 0 else "\n")
+
+def topng(inputfile, outfile=None, overwrite=True, verbose=False, epimask='', endomask='', centercrop=None, blackmask=False, square=False, redcontour=False):
        """
                (verbose) return (64, 64) : the width and height
                (not verbose) return (img, dicimg) : the image and the dicimg objects
        """
                (verbose) return (64, 64) : the width and height
                (not verbose) return (img, dicimg) : the image and the dicimg objects
+               centercrop : it's a size (45, 45), to mention I want to crop by center of epimask and by size.
+               blackmask : draw the outside with black
+
        """
        try:
                dicimg = pydicom.read_file(inputfile) # read dicom image
        except pydicom.errors.InvalidDicomError as e:
                # @TODO: log, i can't read this file
                return
        """
        try:
                dicimg = pydicom.read_file(inputfile) # read dicom image
        except pydicom.errors.InvalidDicomError as e:
                # @TODO: log, i can't read this file
                return
-       img = trim(dicimg.pixel_array)# get image array (12bits)
+       # img = trim(dicimg.pixel_array)# get image array (12bits), Don't trim FOR THE MOMENT
+       img = dicimg.pixel_array# get image array (12bits)
 
        # test <<
        # return img.shape # $$ COMMENT OR REMOVE THIS LINE 
 
        # test <<
        # return img.shape # $$ COMMENT OR REMOVE THIS LINE 
@@ -170,14 +314,52 @@ def topng(inputfile, outfile=None, overwrite=True, verbose=False):
 
        # affine transfo to png 16 bits, func affects img variable
        maxdepth = pow(2, dicimg.BitsStored) - 1
 
        # affine transfo to png 16 bits, func affects img variable
        maxdepth = pow(2, dicimg.BitsStored) - 1
-       print('dicimg.BitsStored, (img.min(), img.max())', dicimg.BitsStored, (img.min(), img.max())) # testing..
+       # print('dicimg.BitsStored, (img.min(), img.max())', dicimg.BitsStored, (img.min(), img.max())) # testing..
+       if int(dicimg.BitsStored) < 12:
+               print("\n\n\n-----{} Bits-----\n\n\n\n".format(dicimg.BitsStored))
        affine(img, 
                (0, maxdepth), # img.min() replace 0 may be, but not sure it would be good choice
                (0, PNG16_MAX if maxdepth > PNG8_MAX else PNG8_MAX)
        )
        
        affine(img, 
                (0, maxdepth), # img.min() replace 0 may be, but not sure it would be good choice
                (0, PNG16_MAX if maxdepth > PNG8_MAX else PNG8_MAX)
        )
        
-       mask(img, '/Users/user/Desktop/Master/Stage/Data/json/json_GT/0_Case_0002/contours/1.2.3.4.5.6/31/-85.9968/Epicardic.json')
+       # test <<
+       # imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
+       # ret,thresh = cv2.threshold(img,127,255,0)
+       # im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
+       # print("im2, contours, hierarchy", im2, contours, hierarchy)
+       # test >>
+
+       # return img
+
+       # print("log: epimask", epimask)
+
+       if epimask:
+               if redcontour:
+                       
+                       contours = [epimask] # list of list of coords 
+                       if endomask:contours.append(endomask)
 
 
+                       img = contour(img, contours, color=RED_COLOR, thickness=1)
+
+               if blackmask:# if is there a mask => apply
+                       if square:
+                               img = sqrmask2(img, epimask)
+                       else:
+                               if endomask:
+                                       img = hollowmask(img, epimask, endomask)
+                               else:
+                                       img = mask(img, epimask)
+
+               if centercrop:
+                       img = crop(img, epimask, centercrop)
+
+
+       # return
+       # test
+       # if verbose:
+       #       return img, dicimg, minmax(epimask)
+       # else:
+       #       return img.shape
 
        savepath = (outfile or inputfile) + '.png'
        savedir = getdir(savepath)
 
        savepath = (outfile or inputfile) + '.png'
        savedir = getdir(savepath)
@@ -194,11 +376,17 @@ def topng(inputfile, outfile=None, overwrite=True, verbose=False):
        # np.savetxt(savepath + '.npy', img)
        # test >>
 
        # np.savetxt(savepath + '.npy', img)
        # test >>
 
-       cv2.imwrite(savepath, img, [cv2.IMWRITE_PNG_COMPRESSION, 0]) # write png image
+       if np.count_nonzero(img) > 0: # matrix not full of zero
+               cv2.imwrite(savepath, img, [cv2.IMWRITE_PNG_COMPRESSION, 0]) # write png image
+
+       # print("ndimage.imread(savepath)", ndimage.imread(savepath).shape)
+       # print( ndimage.imread(savepath) )
+       # print("np.expand_dims(ndimage.imread(savepath), 0)", np.expand_dims(ndimage.imread(savepath), 0).shape)
+       # print(np.expand_dims(ndimage.imread(savepath), 0))
 
        # test
        if verbose:
 
        # test
        if verbose:
-               return img, dicimg
+               return img, dicimg, minmax(epimask)
        else:
                return img.shape
 
        else:
                return img.shape
 
@@ -213,4 +401,5 @@ def topngs(inputdir, outdir):
 
 if __name__ == '__main__':
        # topngs( INPUT_DIR, join(OUT_DIR, INPUT_DIR.split('/')[-2]) )
 
 if __name__ == '__main__':
        # topngs( INPUT_DIR, join(OUT_DIR, INPUT_DIR.split('/')[-2]) )
-       topng(INPUT_DIR+'Image00003', OUT_DIR + INPUT_DIR.split('/')[-2] +'-Image00003')
+       readpng(OUT_DIR+'aug_circle.png')
+       # topng(INPUT_DIR+'Image00003', OUT_DIR + INPUT_DIR.split('/')[-2] +'-Image00003', epimask="/Users/user/Desktop/Master/Stage/Data/json/json_GT/0_Case_0002/contours/1.2.3.4.5.6/31/-85.9968/Epicardic.json")