├── .gitignore ├── LICENSE ├── README.md ├── footage.mp4 ├── imgseq └── footage │ └── README.md ├── main.py └── out └── footageInfo.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | #testfiles 107 | t1.py 108 | t2.py -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 P.os 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyChromakey 2 | pyChromakey is a simple python app/script that can remove greenscreen backgrounds from a given video footage. 3 | 4 | - Output will be a sequence of PNG Images 5 | - currently tested only with MP4 6 | 7 | # Why! 8 | I know there are better softwares that can do this kind a thing,but i wanted to try it for fun.and it worked! 9 | 10 | ### prerequisites 11 | - python 3 12 | - opencv 13 | - PIL 14 | 15 | ### usage 16 | 17 | >Simply copy the footage you want to key to pyChromakey directory, 18 | >then run ```main.py``` 19 | 20 | ### Todos 21 | 22 | - Create a UI 23 | - Implement a export file type selector 24 | 25 | ##### Sources: 26 | - [Reddit][rdtl1] 27 | - [Stackoverflow][stkof1] 28 | 29 | License 30 | ---- 31 | 32 | MIT 33 | 34 | [rdtl1]: https://www.reddit.com/r/Python/comments/m68js/trying_to_write_a_very_simple_green_screen_code/c2yk6wx 35 | [stkof1]: https://stackoverflow.com/a/34325723 36 | -------------------------------------------------------------------------------- /footage.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmcx/pyChromakey/0a1c46b2ac2ff4ccbcbf5ef563cf2f7445ce6853/footage.mp4 -------------------------------------------------------------------------------- /imgseq/footage/README.md: -------------------------------------------------------------------------------- 1 | > This directory is used to create the initial image sequence 2 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from PIL import Image,ImageChops 3 | import numpy 4 | import math 5 | import os 6 | import sys 7 | 8 | def removeGreenScreen(infile,outfile, keyColor = None, tolerance = None): 9 | #open files 10 | inDataFG = Image.open(infile).convert('YCbCr') 11 | BG = Image.new('RGBA', inDataFG.size, (0, 0, 0, 0)) 12 | #make sure values are set 13 | if keyColor == None:keyColor = inDataFG.getpixel((1,1)) 14 | if tolerance == None: tolerance = [50,130] 15 | [Y_key, Cb_key, Cr_key] = keyColor 16 | [tola, tolb]= tolerance 17 | 18 | (x,y) = inDataFG.size #get dimensions 19 | foreground = numpy.array(inDataFG.getdata()) #make array from image 20 | maskgen = numpy.vectorize(colorClose) #vectorize masking function 21 | 22 | 23 | alphaMask = maskgen(foreground[:,1],foreground[:,2] ,Cb_key, Cr_key, tola, tolb) #generate mask 24 | alphaMask.shape = (y,x) #make mask dimensions of original image 25 | imMask = Image.fromarray(numpy.uint8(alphaMask))#convert array to image 26 | invertMask = Image.fromarray(numpy.uint8(255-255*(alphaMask/255))) #create inverted mask with extremes 27 | 28 | #create images for color mask 29 | colorMask = Image.new('RGB',(x,y),tuple([0,0,0])) 30 | allgreen = Image.new('YCbCr',(x,y),tuple(keyColor)) 31 | 32 | colorMask.paste(allgreen,invertMask) #make color mask green in green values on image 33 | inDataFG = inDataFG.convert('RGB') #convert input image to RGB for ease of working with 34 | cleaned = ImageChops.subtract(inDataFG,colorMask) #subtract greens from input 35 | BG.paste(cleaned,(0,0),imMask)#paste masked foreground over background 36 | BG.save(outfile, "PNG") #save cleaned image 37 | 38 | # Print iterations progress 39 | def print_progress(iteration, total, prefix='', suffix='', decimals=1, bar_length=100): 40 | """ 41 | Call in a loop to create terminal progress bar 42 | @params: 43 | iteration - Required : current iteration (Int) 44 | total - Required : total iterations (Int) 45 | prefix - Optional : prefix string (Str) 46 | suffix - Optional : suffix string (Str) 47 | decimals - Optional : positive number of decimals in percent complete (Int) 48 | bar_length - Optional : character length of bar (Int) 49 | """ 50 | str_format = "{0:." + str(decimals) + "f}" 51 | percents = str_format.format(100 * (iteration / float(total))) 52 | filled_length = int(round(bar_length * iteration / float(total))) 53 | bar = '█' * filled_length + '-' * (bar_length - filled_length) 54 | 55 | sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percents, '%', suffix)), 56 | 57 | if iteration == total: 58 | sys.stdout.write('\n') 59 | sys.stdout.flush() 60 | 61 | def colorClose(Cb_p,Cr_p, Cb_key, Cr_key, tola, tolb): 62 | temp = math.sqrt((Cb_key-Cb_p)**2+(Cr_key-Cr_p)**2) 63 | if temp < tola: 64 | z= 0.0 65 | elif temp < tolb: 66 | z= ((temp-tola)/(tolb-tola)) 67 | else: 68 | z= 1.0 69 | return 255.0*z 70 | 71 | def writeLog(data,filename): 72 | #print(var) 73 | with open(filename, 'w') as out: 74 | out.write(str(data) + '\n') 75 | 76 | # extract frames 77 | def frameCapture(path,type): 78 | # Path to video file 79 | vidObj = cv2.VideoCapture(path) #cv2 v3 or above 80 | # Used as counter variable 81 | count = 0 82 | # checks whether frames were extracted 83 | success = 1 84 | global length 85 | if vidObj.isOpened(): 86 | length = int(vidObj.get(cv2.CAP_PROP_FRAME_COUNT)) 87 | fps = vidObj.get(cv2.CAP_PROP_FPS) 88 | width = vidObj.get(cv2.CAP_PROP_FRAME_WIDTH) 89 | height = vidObj.get(cv2.CAP_PROP_FRAME_HEIGHT) 90 | writeLog("Frame Width : "+str(width)+"\nFrame Height : "+str(height)+"\nFrame Rate : "+str(fps),"out\\footageInfo.txt") 91 | print ("\n" * 100) 92 | while count < length: 93 | # vidObj object calls read 94 | # function extract frames 95 | success, image = vidObj.read() 96 | # Saves the frames with frame-count 97 | cv2.imwrite("imgseq\\"+ str(type) +"\\frame"+ str(count).zfill(6) +".png", image) 98 | print_progress(count, (length-1), prefix='Creating Image Sequence', suffix="[ Frame : "+ str(count).zfill(6) +" ]", decimals=1, bar_length=50) 99 | count += 1 100 | 101 | def genOutput(): 102 | global length 103 | count = 0 104 | print ("\n" * 100) 105 | for file in [file for file in os.listdir("imgseq\\footage\\") if file.endswith('.png')]: 106 | removeGreenScreen("imgseq\\footage\\"+str(file),"out\\"+str(file)) 107 | print_progress(count, (length-1), prefix='Generating Output Files', suffix="[ File : "+str(file)+" ]", decimals=1, bar_length=50) 108 | count += 1 109 | 110 | def grabInput(): 111 | print ("\n" * 100) 112 | print ("~ A simple GreenScreen Removing tool") 113 | print ("~ Output wil be a PNG Sequence") 114 | footage = input("Enter Filename with extention (Ex.Footage.mp4) \n: ") 115 | return footage 116 | 117 | def start(footage): 118 | global length 119 | files = [f for f in os.listdir('.') if os.path.isfile(f)] 120 | if footage in files: 121 | frameCapture(footage,"footage") 122 | genOutput() 123 | print ("\n" * 100) 124 | print("\n"+length+" Files Generated successfully! Check the 'out' directory.") 125 | print("\nPress Enter to Exit.") 126 | input("") 127 | quit() 128 | else: 129 | print ("\n" * 100) 130 | print("File not found!! Press Enter to Try Again.") 131 | print("Remember to copy the file into this folder.") 132 | input("") 133 | 134 | if __name__ == '__main__': 135 | done = False 136 | length = 0 137 | while not done: 138 | footage = grabInput() 139 | start(footage) -------------------------------------------------------------------------------- /out/footageInfo.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmcx/pyChromakey/0a1c46b2ac2ff4ccbcbf5ef563cf2f7445ce6853/out/footageInfo.txt --------------------------------------------------------------------------------