<jupyter_start><jupyter_text>findPart.ipynbFinds largest contour by one of:- floodFill- HSV color space filter- Canny edgeUseful for finding a part as the first step in an industrial inspection pipeline.  Allows for a tweaking and running modes.  If tweak is set, pipeline is displayed and values can be modified and changed.  Conversely, the function returns an image with the backround removed.<jupyter_code># Load packages
import cv2
import os
import platform
import sys
import numpy as np
import json
import pprint<jupyter_output><empty_output><jupyter_text>Tested on:- platform    Linux-4.15.0-38-generic-x86_64-with-debian-buster-sid    41-Ubuntu SMP Wed Oct 10 10:59:38 UTC 2018    - python    3.6.6 |Anaconda, Inc.| (default, Oct  9 2018, 12:34:16) [GCC 7.3.0]    - packages    cv2: 3.4.3    numpy: 1.14.4    json: 2.0.9    platform: 1.0.8<jupyter_code># Print os, python and package informaiton.  
# If different from previous, errors may occur
print("platform")
print(platform.platform())
print(platform.version())
print("\npython")
print(sys.version)
print("\npackages:")
print(f"cv2: {cv2.__version__}")
print(f"numpy: {np.__version__}")
print(f"json: {json.__version__}")
print(f"platform: {platform.__version__}")<jupyter_output>platform
Linux-4.15.0-38-generic-x86_64-with-debian-buster-sid
#41-Ubuntu SMP Wed Oct 10 10:59:38 UTC 2018

python
3.6.6 |Anaconda, Inc.| (default, Oct  9 2018, 12:34:16) 
[GCC 7.3.0]

packages:
cv2: 3.4.3
numpy: 1.14.4
json: 2.0.9
platform: 1.0.8<jupyter_text>File StructureRecommeded structure is to place this notebook in a folder.In the same folder, place a folder called data containing any input imagesIn the same folder, place a folder called output where images showing the pipeline can be saved**"params.json" must be in the same directory as the notebook.**<jupyter_code>dataDir = str(os.getcwd()) + "/data"
outputDir = str(os.getcwd())+"/output"
paramsFile = "params.json"
print(f"dataDir   = {dataDir}")
print(f"outputDir = {outputDir}")
print(paramsFile)
def saveParams(params):
    with open(paramsFile, "w") as write_file:
        json.dump(params, write_file, indent=4)
    return
        
def loadParams(f):
    with open(f, "r") as read_file:
        params = json.load(read_file)
    return params
params=loadParams(paramsFile)
pprint.pprint(params)
def maskPart(inpImg, paramsFile, tweak = True, f = "input image", outputDir = ""):
    # takes an opencv image (BGR or grayscale) and returns a masked color image
    # allows use of sliders to asjust image if tweak = true
    # f is the name of the image, often the input filename
    # outputDir is the directory used to save files and is only used if tweak is True
    
    if (type(inpImg) != np.ndarray):
        print("Image is not of type numpy.ndarray")
        return -1
    if (len(inpImg.shape) !=3 and len(inpImg.shape) != 2):
        print("Image is not a two or three dimensional array")
        return -1
    if(len(inpImg.shape) == 3 and inpImg.shape[2] !=3):
        print("Image is a three dimensional array, but does not have three channels")
        return -1
    if (len(inpImg.shape)==2):
        inpImg = cv2.cvtColor(inpImg, cv2.COLOR_GRAY2BGR)
    
    if(tweak is not True):
        tweak = False
    f = str(f)
    
    if(tweak is True):
        cv2.namedWindow("input",cv2.WINDOW_NORMAL)
        cv2.namedWindow("ff",cv2.WINDOW_NORMAL)
        cv2.namedWindow("hsv",cv2.WINDOW_NORMAL)
        cv2.namedWindow('edges', cv2.WINDOW_NORMAL)
        cv2.namedWindow("contourInput",cv2.WINDOW_NORMAL)
        cv2.namedWindow('contour',cv2.WINDOW_NORMAL)
        cv2.namedWindow('maskImg',cv2.WINDOW_NORMAL)
        cv2.namedWindow('comboWin',cv2.WINDOW_NORMAL)

    max_value = 255
    max_value_H = 360//2
    
    with open(paramsFile, "r") as read_file:
        params = json.load(read_file)
    if(len(params) == 0):
        print(f"{paramsFile} not found.")
        return -1
    
    ffw = params["ffw"]
    ffh = params["ffh"]
    ffbLow = params["ffbLow"]
    ffgLow = params["ffgLow"]
    ffrLow = params["ffrLow"]
    ffbHigh = params["ffbHigh"]
    ffgHigh = params["ffgHigh"]
    ffrHigh = params["ffrHigh"]
    lowH = params["lowH"]
    lowS = params["lowS"]
    lowV = params["lowV"]
    highH = params["highH"]
    highS = params["highS"]
    highV = params["highV"]  
    blurSlider = params["blurSlider"]
    edgeSlider = params["edgeSlider"]
    CannyLowPct = params["CannyLowPct"]
    CannyHigh = params["CannyHigh"]
    closeSlider = params["closeSlider"]    
    openSlider = params["openSlider"]
    selectedMethod = params["selectedMethod"]
    
    height, width, channels = inpImg.shape
    if height < 2:
        print("Image height needs to be at least 2")
        return -1
    if width < 2:
        print("Image width needs to be at least 2")
        return -1
    
    ffwMax = width - 1
    ffhMax = height - 1
    ffdiffMax = 127;

    def nothing(x):
        pass
    
    if(tweak is True):
        cv2.createTrackbar("w location", "ff", ffw, ffwMax, nothing)
        cv2.createTrackbar("h location", "ff", ffh, ffhMax, nothing)
        cv2.createTrackbar("blu Ldiff", "ff", ffbLow, ffdiffMax, nothing)
        cv2.createTrackbar("gre Ldiff", "ff", ffgLow, ffdiffMax, nothing)
        cv2.createTrackbar("red Ldiff", "ff", ffrLow, ffdiffMax, nothing)
        cv2.createTrackbar("blu Hdiff", "ff", ffbHigh, ffdiffMax, nothing)
        cv2.createTrackbar("gre Hdiff", "ff", ffgHigh, ffdiffMax, nothing)
        cv2.createTrackbar("red Hdiff", "ff", ffrHigh, ffdiffMax, nothing)

        cv2.createTrackbar('low_H',     "hsv" ,  lowH,      max_value_H, nothing)
        cv2.createTrackbar('high_H',    "hsv" ,  highH,     max_value_H, nothing)
        cv2.createTrackbar('low_S',     "hsv" ,  lowS,      max_value,   nothing)
        cv2.createTrackbar('low_S',     "hsv" ,  lowS,      max_value,   nothing)
        cv2.createTrackbar('high_S',    "hsv" ,  highS,     max_value,   nothing)
        cv2.createTrackbar('low_V',     "hsv" ,  lowV,      max_value,   nothing)
        cv2.createTrackbar('high_V',    "hsv" ,  highV,     max_value,   nothing)

        cv2.createTrackbar('blur (sz-1)/2','edges', blurSlider,   10, nothing)
        cv2.createTrackbar('edge (sz-1)/2','edges', edgeSlider,   10, nothing)
        cv2.createTrackbar('LowPctCanny',  'edges', CannyLowPct, 100, nothing)
        cv2.createTrackbar('HighCanny',    'edges', CannyHigh,   800, nothing)
        cv2.createTrackbar('close (sz-1)/2','edges',closeSlider,   3, nothing)
        
        cv2.createTrackbar('open (sz-1)/2','maskImg', params["openSlider"], 3, nothing)

        cv2.createTrackbar('0: ff  1: hsv  2: edge ', 'contourInput', params["selectedMethod"], 2, nothing)


    inpImgOut = inpImg.copy()
    inpStr = f
    inpCol = (0,0,0)
    cv2.putText(inpImgOut, inpStr,(20,30), cv2.FONT_HERSHEY_SIMPLEX, 1, inpCol, 2) 
    if(tweak is True):
        cv2.imshow("input",inpImgOut)
    cont = True
    
    if(tweak is True):
        print("Enter 'c' to continue, 's' to save image, 'p' to save parameters.")
        
    while(cont):
        if(tweak is True):
            ffw = cv2.getTrackbarPos("w location", "ff")
            ffh = cv2.getTrackbarPos("h location", "ff")
            ffbLow = cv2.getTrackbarPos("blu Ldiff", "ff")
            ffgLow = cv2.getTrackbarPos("gre Ldiff", "ff")
            ffrLow = cv2.getTrackbarPos("red Ldiff", "ff")
            ffbHigh = cv2.getTrackbarPos("blu Hdiff", "ff")
            ffgHigh = cv2.getTrackbarPos("gre Hdiff", "ff")
            ffrHigh = cv2.getTrackbarPos("red Hdiff", "ff")
        
        if(tweak is True or selectedMethod == 0):
            ffImg = inpImg.copy()
            seed = (ffw, ffh)
            connectivity = 8
            newMaskVal = (0, 0, 0)
            dcL = (ffbLow,  ffgLow,  ffrLow)
            dcH = (ffbHigh, ffgHigh, ffrHigh)
        
            cv2.floodFill(ffImg, None, seed, newMaskVal, dcL, dcH, connectivity)
        
            outFFimg = ffImg.copy()
            imgStr1 = f"seed.x = {ffw} seed.y = {ffh}"
            imgStr2 = f"blueLow = {ffbLow} greenLow = {ffgLow} redLow = {ffrLow}"
            imgStr3 = f"blueHigh = {ffbHigh} greenHigh = {ffgHigh} redHigh = {ffrHigh}"
            ffCol = (255,255,255)
            retval, ffImgThresh = cv2.threshold(cv2.cvtColor(ffImg, cv2.COLOR_BGR2GRAY), 1, 255, cv2.THRESH_BINARY)
            if(tweak is True):
                ffImgThreshColor = cv2.cvtColor(ffImgThresh, cv2.COLOR_GRAY2BGR)
                ffImgThreshColor[np.where((ffImgThreshColor == [255,255,255]).all(axis=2))]=[255,0,0]
                outFFimg = cv2.addWeighted(inpImg.copy(), 0.5, ffImgThreshColor, 0.5, 0)
                cv2.circle(outFFimg, (ffw, ffh), 5, color=(0,0,255), thickness=2)
                cv2.putText(outFFimg, imgStr1, (20,35), cv2.FONT_HERSHEY_SIMPLEX, 1, ffCol, 2)  
                cv2.putText(outFFimg, imgStr2, (20,70), cv2.FONT_HERSHEY_SIMPLEX, 1, ffCol, 2)  
                cv2.putText(outFFimg, imgStr3,(20,105), cv2.FONT_HERSHEY_SIMPLEX, 1, ffCol, 2)
                cv2.imshow("ff",outFFimg)
        
        if(tweak is True or selectedMethod == 1):
            imgHSV = cv2.cvtColor(inpImg, cv2.COLOR_BGR2HSV)
            if(tweak is True):
                lowH  = cv2.getTrackbarPos('low_H', "hsv")
                lowS  = cv2.getTrackbarPos('low_S', "hsv")
                lowV  = cv2.getTrackbarPos('low_V', "hsv")
                highH = cv2.getTrackbarPos('high_H',"hsv")
                highS = cv2.getTrackbarPos('high_S',"hsv")
                highV = cv2.getTrackbarPos('high_V',"hsv")
            
            thresholdImg = cv2.inRange(imgHSV, (lowH, lowS, lowV), (highH, highS, highV))
            if(tweak is True):
                threshColorImg = cv2.cvtColor(thresholdImg, cv2.COLOR_GRAY2BGR)
                threshColorImg[np.where((threshColorImg == [255,255,255]).all(axis=2))]=[255,0,0]
                outHSVimg = cv2.addWeighted(inpImg.copy(), 0.5, threshColorImg, 0.5,0)
                imgStr1 = f"lowH = {lowH:.0f} highH = {highH:.0f}"
                imgStr2 = f"lowS = {lowS:.0f} highS = {highS:.0f}"
                imgStr3 = f"lowV = {lowV:.0f} highV = {highV:.0f}"
                hsvCol = (0,0,0)
                cv2.putText(outHSVimg, imgStr1, (20,35), cv2.FONT_HERSHEY_SIMPLEX, 1, hsvCol, 2)  
                cv2.putText(outHSVimg, imgStr2, (20,70), cv2.FONT_HERSHEY_SIMPLEX, 1, hsvCol, 2)  
                cv2.putText(outHSVimg, imgStr3,(20,105), cv2.FONT_HERSHEY_SIMPLEX, 1, hsvCol, 2)  
                cv2.imshow("hsv", outHSVimg)
        
        if(tweak is True):
            blurSlider = cv2.getTrackbarPos("blur (sz-1)/2","edges")
            edgeSlider = cv2.getTrackbarPos("edge (sz-1)/2","edges")
            CannyLowPct = cv2.getTrackbarPos("LowPctCanny", "edges")
            CannyHigh = cv2.getTrackbarPos("HighCanny", "edges")
            closeSlider=cv2.getTrackbarPos("close (sz-1)/2","edges")
            
        if(tweak is True or selectedMethod == 2):    
            CannyLow = CannyHigh*CannyLowPct/100
            blurKSize = blurSlider*2 + 1
            edgeKSize = edgeSlider*2 + 1
            closeKSize = closeSlider*2 + 1
            blurImg = cv2.GaussianBlur(inpImg.copy(), (blurKSize,blurKSize),0)
            edgesImg = cv2.Canny(blurImg, CannyLow, CannyHigh, edgeKSize)
            if(closeKSize > 1):
                kernel = np.ones((closeKSize,closeKSize),np.uint8)
                edgesImg = cv2.morphologyEx(edgesImg, cv2.MORPH_CLOSE, kernel)
            if(tweak is True):
                edgesColorImg = cv2.cvtColor(edgesImg, cv2.COLOR_GRAY2BGR)
                edgesColorImg[np.where((edgesColorImg == [255,255,255]).all(axis=2))]=[255,0,0]
                outEdgeImg = cv2.addWeighted(blurImg, 0.5, edgesColorImg, 0.5,0)
        
                imgStr1 = f"blurKernel = {blurKSize:.0f} edgeKernel = {edgeKSize:.0f}"
                imgStr2 = f"CannyLow = {CannyLow:.0f} CannyHigh = {CannyHigh:.0f}"
                imgStr3 = f"close = {closeKSize: .0f}"
                edgeCol = (0,0,0)
                cv2.putText(outEdgeImg, imgStr1,(20,35), cv2.FONT_HERSHEY_SIMPLEX, 1, edgeCol, 2)    
                cv2.putText(outEdgeImg, imgStr2,(20,70), cv2.FONT_HERSHEY_SIMPLEX, 1, edgeCol, 2) 
                cv2.putText(outEdgeImg, imgStr3,(20,105),cv2.FONT_HERSHEY_SIMPLEX, 1, edgeCol, 2) 
                cv2.imshow("edges", outEdgeImg)     
                
        if(tweak is True):        
            selectedMethod = cv2.getTrackbarPos('0: ff  1: hsv  2: edge ', 'contourInput')
        
        if selectedMethod == 0:
            contInp = ffImgThresh
            if(tweak is True):
                inpTransOut = outFFimg
                imgStr = "using floodfill"
        if selectedMethod == 1:
            contInp = thresholdImg
            if(tweak is True):
                inpTransOut = outHSVimg
                imgStr = "using HSV"
        if selectedMethod == 2:
            contInp = edgesImg
            if(tweak is True):
                inpTransOut = outEdgeImg
                imgStr = "using edges"
            
        selectCol = (255)
        if(tweak is True):
            contInpDis = contInp.copy()
            cv2.putText(contInpDis, imgStr,(20,35), cv2.FONT_HERSHEY_SIMPLEX, 1, selectCol, 2) 
            cv2.imshow("contourInput",contInpDis)
            
        if(tweak is True):
            contImg = contInp.copy()
            contImg = cv2.cvtColor(contImg, cv2.COLOR_GRAY2BGR)
            contInpOut = contInpDis.copy()
            contInpOut = cv2.cvtColor(contInpOut, cv2.COLOR_GRAY2BGR)
        else:
            contImg = contInp
            
        imCont, contours, hierarchy = cv2.findContours(contInp, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

        if len(contours) !=0:
            cv2.drawContours(contImg, contours, -1, (255, 255, 255), 1)
            cnt = max(contours, key = cv2.contourArea)
            area = cv2.contourArea(cnt)
            perimeter = cv2.arcLength(cnt, True)
            cv2.drawContours(contImg, [cnt], 0, (255,0,0), 2)
            
            mask = np.zeros(inpImg.shape, np.uint8)
            cv2.fillPoly(mask, pts =[cnt], color=(1,1,1))
            
            if(tweak is True):
                openSlider=cv2.getTrackbarPos("open (sz-1)/2","maskImg")
                openKSize = openSlider*2 + 1
                if(openKSize > 1):
                    kernel = np.ones((openKSize,openKSize),np.uint8)
                    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
                
            maskImg = inpImg * mask
            if(tweak is True):
                cv2.imshow("maskImg",maskImg)
            
                M = cv2.moments(cnt)
                cX = M["m10"] / M["m00"]
                cY = M["m01"] / M["m00"]
            
                col = (255,255,255)
            
                cv2.circle(contImg, (int(cX), int(cY)), 5, color=(0,0,255), thickness=2)    
                imgStr1 = f"center = ({cX:.1f}, {cY:.1f})  area = {area:,.0f} perimeter = {perimeter:,.0f}"
                cv2.putText(contImg, imgStr1,(20,35), cv2.FONT_HERSHEY_SIMPLEX, 1, col, 2)
                
                cv2.imshow("contour",contImg)
                maskImgOut = maskImg.copy()
                imgStr1 = f"open = {openKSize}"
                cv2.putText(maskImgOut, imgStr1,(20,35), cv2.FONT_HERSHEY_SIMPLEX, 1, col, 2)
                imgTop = np.concatenate((inpImgOut, inpTransOut), axis = 1)
                imgBtm = np.concatenate((contImg, maskImgOut), axis = 1)
                imgSave =  np.concatenate((imgTop, imgBtm), axis = 0)
                cv2.imshow('comboWin', imgSave)
                
                key = cv2.waitKey(200)
                if  key == ord('c'):
                    cont=False
                if  key == ord('s'):
                    if(os.path.isdir(outputDir)):
                        i = 0
                        fname = outputDir + "/out_"+str(i)+"_"+f
                        while os.path.isfile(fname):
                            i = i+1
                            fname = outputDir + "/out_"+str(i)+"_"+f
                        cv2.imwrite(fname,imgSave)
                    else:
                        print(f"{outputDir} is not a directory.  Image not saved.")
                if key == ord('p'):
                        params["ffw"] = ffw
                        params["ffh"] = ffh
                        params["ffbLow"] = ffbLow
                        params["ffgLow"] = ffgLow
                        params["ffrLow"] = ffrLow
                        params["ffbHigh"] = ffbHigh
                        params["ffgHigh"] = ffgHigh
                        params["ffrHigh"] = ffrHigh
                        params["lowH"] = lowH
                        params["lowS"] = lowS
                        params["lowV"] = lowV
                        params["highH"] = highH
                        params["highS"] = highS
                        params["highV"] = highV
                        params["blurSlider"] = blurSlider
                        params["edgeSlider"] = edgeSlider
                        params["CannyLowPct"] = CannyLowPct
                        params["CannyHigh"] = CannyHigh
                        params["closeSlider"] = closeSlider    
                        params["openSlider"] = openSlider
                        params["selectedMethod"] = selectedMethod
                        saveParams(params)
            else:
                cont=False

    if(tweak is True):
        cv2.destroyAllWindows()
    
    return maskImg;
tweak = True
if(tweak is False):
    cv2.namedWindow("maskedImage",cv2.WINDOW_NORMAL)
for f in os.listdir(dataDir):
    inpImg = cv2.imread(dataDir + "/" + f)
    maskImg = maskPart(inpImg, paramsFile, tweak, f, outputDir)
    if(tweak is False):
        cv2.imshow("maskedImage", maskImg)
        print("Press any key to continue")
        cv2.waitKey()
        
cv2.destroyAllWindows()
# to reset params, uncommment last line and run cell
params = { 
    "ffw" : 1,
    "ffh" : 1,
    "ffbLow" : 5,
    "ffgLow" : 5,
    "ffrLow" : 5,
    "ffbHigh" : 5,
    "ffgHigh" : 5,
    "ffrHigh" : 5,
    "lowH" : 0,
    "lowS" : 0,
    "lowV" : 0,
    "highH" : 180,
    "highS" : 255,
    "highV" : 150,
    "blurSlider" : 1,
    "edgeSlider" : 1,
    "CannyLowPct" : 50,
    "CannyHigh" : 100,
    "closeSlider" : 2,   
    "openSlider" : 1,
    "selectedMethod" : 0
}
#saveParams(params)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1273090634114724, 'fraction_numerical': 0.03510856649022361, 'mean_word_length': 2.0745599468615077, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '440188', 'license': 'MIT', 'n_tokens_mistral': 6781, 'n_tokens_neox': 6342, 'n_words': 1327, 'path': 'findPart.ipynb', 'repo_name': 'bjmeier/findPart', 'size': '18514'}