diff --git a/compat/msvc/winmake.py b/compat/msvc/winmake.py
index 2e6209f6ac058b4246916df05716dd8fa253d3d7..9e8123ee2e532ae8729130db8366149fe29480ed 100644
--- a/compat/msvc/winmake.py
+++ b/compat/msvc/winmake.py
@@ -30,6 +30,9 @@ daemon_msvc_build_local_dir = daemon_dir + r'\build-local'
 contrib_src_dir = daemon_dir + r'\contrib\src'
 contrib_build_dir = daemon_dir + r'\contrib\build'
 contrib_tmp_dir = daemon_dir + r'\contrib\tarballs'
+plugins_bin_dir = daemon_dir + r'\..\plugins\build'
+plugins_dir = daemon_dir + r'\..\plugins'
+pluginsList = ['GreenScreen']
 
 # SCM
 wget_args = [
@@ -82,7 +85,10 @@ def findVSLatestDir():
         '-property installationPath'
     ]
     cmd = [vs_where_path] + args
-    output = subprocess.check_output(' '.join(cmd)).decode('utf-8', errors='ignore')
+    output = subprocess.check_output(
+        ' '.join(cmd)).decode(
+        'utf-8',
+        errors='ignore')
     if output:
         return output.splitlines()[0]
     else:
@@ -126,8 +132,62 @@ def getVSEnvCmd(arch='x64', platform='', version=''):
     return vcEnvInit
 
 
+def make_plugin(pkg_info, force, sdk_version, toolset):
+    pkg_name = pkg_info.get('name')
+    version = pkg_info.get('version')
+    extractLibs = pkg_info.get('extractLibs')
+    plugin_path = (plugins_dir + "/" + pkg_name).replace("\\", "/")
+    if (extractLibs):
+        path = plugins_dir + r'\contrib\libs.tar.gz'
+        with tarfile.open(path, 'r', encoding="utf8", errors='ignore') as tarball:
+            tarball.extractall(plugins_dir + r'\contrib')
+    pkg_build_uptodate = False
+    pkg_ver_uptodate = False
+    # attempt to get the current built version
+    current_version = ''
+    # check build file for current version
+    build_file = plugins_bin_dir + r'\\.' + pkg_name
+    if os.path.exists(build_file):
+        if force:
+            os.remove(build_file)
+        else:
+            pkg_build_uptodate = is_build_uptodate(pkg_name, build_file)
+            with open(build_file, 'r+', encoding="utf8", errors='ignore') as f:
+                current_version = f.read()
+                if current_version == version:
+                    pkg_ver_uptodate = True
+    for dep in pkg_info.get('deps', []):
+        dep_build_dep = resolve(dep, False, sdk_version, toolset)
+        if dep_build_dep:
+            pkg_build_uptodate = False
+    pkg_up_to_date = pkg_build_uptodate & pkg_ver_uptodate
+    cmake_defines = ""
+    for define in pkg_info.get('defines', []):
+        cmake_defines += " -D" + define + " "
+    if not pkg_up_to_date or current_version is None or force:
+        root_logger.warning(
+            "Building plugin with preferred sdk version %s and toolset %s", sdk_version, toolset)
+        env_set = 'false' if pkg_info.get('with_env', '') == '' else 'true'
+        sdk_to_use = sdk_version if env_set == 'false' else pkg_info.get(
+            'with_env', '')
+        cmake_script = "cmake -G " + getCMakeGenerator(getLatestVSVersion(
+        )) + cmake_defines + "-S " + plugin_path + " -B " + plugin_path + "/msvc"
+        root_logger.warning("Cmake generating vcxproj files")
+        result = getSHrunner().exec_batch(cmake_script)
+        build(pkg_name,
+              plugin_path,
+              pkg_info.get('project_paths', []),
+              pkg_info.get('custom_scripts', {}),
+              env_set,
+              sdk_to_use,
+              toolset)
+        track_build(pkg_name, version)
+
+
 def make_daemon(pkg_info, force, sdk_version, toolset):
-    cmake_script = 'cmake -DCMAKE_CONFIGURATION_TYPES="ReleaseLib_win32" -DCMAKE_SYSTEM_VERSION=' + sdk_version + ' -DCMAKE_VS_PLATFORM_NAME="x64" -G ' + getCMakeGenerator(getLatestVSVersion()) + ' -T $(DefaultPlatformToolset) -S ../../ -B ../../build-local'
+    cmake_script = 'cmake -DCMAKE_CONFIGURATION_TYPES="ReleaseLib_win32" -DCMAKE_SYSTEM_VERSION=' + sdk_version + \
+        ' -DCMAKE_VS_PLATFORM_NAME="x64" -G ' + getCMakeGenerator(getLatestVSVersion(
+        )) + ' -T $(DefaultPlatformToolset) -S ../../ -B ../../build-local'
     root_logger.warning("Cmake generating vcxproj files")
     result = getSHrunner().exec_batch(cmake_script)
     if result[0] is not 0:
@@ -138,7 +198,8 @@ def make_daemon(pkg_info, force, sdk_version, toolset):
     root_logger.warning(
         "Building daemon with preferred sdk version %s and toolset %s", sdk_version, toolset)
     env_set = 'false' if pkg_info.get('with_env', '') == '' else 'true'
-    sdk_to_use = sdk_version if env_set == 'false' else pkg_info.get('with_env', '')
+    sdk_to_use = sdk_version if env_set == 'false' else pkg_info.get(
+        'with_env', '')
     build('daemon', daemon_msvc_build_local_dir,
           pkg_info.get('project_paths', []),
           pkg_info.get('custom_scripts', {}),
@@ -152,6 +213,8 @@ def make(pkg_info, force, sdk_version, toolset):
     pkg_name = pkg_info.get('name')
     if pkg_name == 'daemon':
         return make_daemon(pkg_info, force, sdk_version, toolset)
+    if pkg_name in pluginsList:
+        return make_plugin(pkg_info, force, sdk_version, toolset)
     version = pkg_info.get('version')
     pkg_build_uptodate = False
     pkg_ver_uptodate = False
@@ -184,11 +247,13 @@ def make(pkg_info, force, sdk_version, toolset):
         if not pkg_up_to_date or force:
             if not force and not current_version is None:
                 log.warning(pkg_name + ' is not up to date')
-            if (should_fetch or force) and fetch_pkg(pkg_name, version, pkg_info['url'], force):
+            if (should_fetch or force) and fetch_pkg(
+                    pkg_name, version, pkg_info['url'], force):
                 apply(pkg_name, pkg_info.get('patches', []),
                       pkg_info.get('win_patches', []))
         env_set = 'false' if pkg_info.get('with_env', '') == '' else 'true'
-        sdk_to_use = sdk_version if env_set == 'false' else pkg_info.get('with_env', '')
+        sdk_to_use = sdk_version if env_set == 'false' else pkg_info.get(
+            'with_env', '')
         if build(pkg_name,
                  contrib_build_dir + '\\' + pkg_name,
                  pkg_info.get('project_paths', []),
@@ -214,10 +279,10 @@ def fetch_pkg(pkg_name, version, url, force):
         log.error(pkg_name + ' missing url in package configuration')
         return False
     archive_name = full_url[full_url.rfind("/") + 1:]
-    archive_path = contrib_tmp_dir + '\\' + archive_name
+    archive_path = contrib_tmp_dir + '\\' + pkg_name + '_' + archive_name
     if not os.path.exists(archive_path):
         log.debug('Fetching ' + pkg_name + ' from: ' + full_url)
-        args = [full_url, '-P', contrib_tmp_dir]
+        args = [full_url, '-O', archive_path]
         args.extend(wget_args)
         dl_result = getSHrunner().exec_batch('wget', args)
         if dl_result[0] is not 0:
@@ -328,6 +393,8 @@ def apply(pkg_name, patches, win_patches):
 def get_pkg_file(pkg_name):
     if pkg_name == 'daemon':
         pkg_location = daemon_msvc_dir
+    elif (pkg_name in pluginsList):
+        pkg_location = plugins_dir + r'\\' + pkg_name
     else:
         pkg_location = daemon_dir + r'\contrib\src\\' + pkg_name
     pkg_json_file = pkg_location + r"\\package.json"
@@ -351,7 +418,10 @@ def resolve(pkg_name, force=False, sdk_version='', toolset=''):
 
 
 def track_build(pkg_name, version):
-    build_file = contrib_build_dir + '\\.' + pkg_name
+    if pkg_name in pluginsList:
+        build_file = plugins_bin_dir + '\\.' + pkg_name
+    else:
+        build_file = contrib_build_dir + '\\.' + pkg_name
     f = open(build_file, "w+", encoding="utf8", errors='ignore')
     f.write(version)
     f.close()
@@ -442,7 +512,7 @@ class SHrunner():
         if not os.environ.get('JENKINS_URL'):
             self.sh_path = full_sys_path + r'\bash.exe'
         else:
-            self.sh_path = '\"C:\Program Files\Git\git-bash.exe\"'
+            self.sh_path = r'\"C:\Program Files\Git\git-bash.exe\"'
 
         if not os.path.exists(self.sh_path):
             log.warning('Bash not found at ' + self.sh_path)
@@ -556,7 +626,7 @@ class MSbuilder:
         # force chosen toolset
         self.__class__.replace_vs_prop(proj_path,
                                        'PlatformToolset',
-                                        toolset)
+                                       toolset)
         args = []
         args.extend(self.default_msbuild_args)
         args.extend(self.extra_msbuild_args)
@@ -651,16 +721,31 @@ def main():
             pkg_json_file = get_pkg_file(parsed_args.clean)
             with open(pkg_json_file, encoding="utf8", errors='ignore') as json_file:
                 pkg_info = json.load(json_file)
-                dir_to_clean = contrib_build_dir + '\\' + pkg_info['name']
-                file_to_clean = contrib_build_dir + '\\.' + pkg_info['name']
-                if os.path.exists(dir_to_clean) or os.path.exists(file_to_clean):
-                    log.warning('Removing contrib build ' + dir_to_clean)
-                    getSHrunner().exec_batch(
-                        'rmdir', ['/s', '/q', dir_to_clean])
-                    getSHrunner().exec_batch(
-                        'del', ['/s', '/f', '/q', file_to_clean])
+                if (parsed_args.clean not in pluginsList):
+                    exclude_dirs = contrib_build_dir
+                    dir_to_clean = exclude_dirs + '\\' + pkg_info['name']
+                    file_to_clean = exclude_dirs + '\\.' + pkg_info['name']
+
+                    if os.path.exists(dir_to_clean) or os.path.exists(
+                            file_to_clean):
+                        log.warning('Removing build ' + dir_to_clean)
+                        getSHrunner().exec_batch(
+                            'rmdir', ['/s', '/q', dir_to_clean])
+                        getSHrunner().exec_batch(
+                            'del', ['/s', '/f', '/q', file_to_clean])
+                    else:
+                        log.warning('No builds to remove')
                 else:
-                    log.warning('No builds to remove')
+                    exclude_dirs = plugins_bin_dir
+                    files_to_clean = [exclude_dirs + '\\.' + pkg_info['name']]
+                    for name in glob.iglob(
+                            exclude_dirs + '\\**\\' + pkg_info['name'] + ".jpl", recursive=True):
+                        files_to_clean.append(name)
+                    for name in files_to_clean:
+                        getSHrunner().exec_batch(
+                            'del', ['/s', '/f', '/q', name])
+                    getSHrunner().exec_batch(
+                        'rmdir', ['/s', '/q', plugins_dir + "\\" + parsed_args.clean + "\\msvc"])
 
     if parsed_args.build:
         if not os.path.exists(contrib_build_dir):
@@ -720,7 +805,7 @@ class CustomAdapter(logging.LoggerAdapter):
         return indentation_level - 4 - 2  # Remove logging infrastructure frames
 
     def process(self, msg, kwargs):
-        return '{i}{m}'.format(i=' '*(self.indent()), m=msg), kwargs
+        return '{i}{m}'.format(i=' ' * (self.indent()), m=msg), kwargs
 
 
 def setup_logging(lvl=logging.DEBUG, verbose=False, do_indent=False):
diff --git a/contrib/src/opencv/package.json b/contrib/src/opencv/package.json
index 653c40d54ca51f35ec517800b94d11ddd397aeaa..77b033a3976b4046a9431238c8fb224f5fc32ff5 100644
--- a/contrib/src/opencv/package.json
+++ b/contrib/src/opencv/package.json
@@ -2,16 +2,19 @@
     "name": "opencv",
     "version": "4.1.1",
     "url": "https://github.com/opencv/opencv/archive/__VERSION__.tar.gz",
-    "deps": [
-	"opencv_contrib"
-    ],
+    "deps": ["opencv_contrib"],
     "patches": [],
     "win_patches": [],
     "project_paths": [],
     "with_env" : "",
     "custom_scripts": {
-        "pre_build": [],
-        "build": [],
+        "pre_build": [
+            "mkdir build"
+        ],
+        "build": [
+            "cmake -G %CMAKE_GENERATOR% -DBUILD_opencv_java=OFF -DBUILD_opencv_python=OFF -DCMAKE_INSTALL_LIBDIR=lib -DBUILD_SHARED_LIBS=no -DOPENCV_EXTRA_MODULES_PATH=./../opencv_contrib/modules -DOPENCV_FORCE_3RDPARTY_BUILD=OFF -DENABLE_PRECOMPILED_HEADERS=ON -DBUILD_ZLIB=OFF -DBUILD_TIFF=OFF -DBUILD_JASPER=OFF -DBUILD_JPEG=OFF -DBUILD_PNG=ON -DBUILD_OPENEXR=OFF -DBUILD_WEBP=OFF -DBUILD_TBB=OFF -DBUILD_IPP_IW=OFF -DBUILD_ITT=OFF -DBUILD_opencv_apps=OFF -DBUILD_opencv_js=OFF -DBUILD_ANDROID_PROJECTS=OFF -DBUILD_ANDROID_EXAMPLES=OFF -DBUILD_DOCS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_PACKAGE=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_TESTS=OFF -DBUILD_WITH_STATIC_CRT=ON -DBUILD_WITH_DYNAMIC_IPP=OFF -DWITH_JPEG=OFF -DWITH_JASPER=OFF -DWITH_WEBP=OFF -DWITH_PNG=ON -DWITH_TIFF=OFF -DWITH_GTK=OFF -DWITH_GSTREAMER=OFF -DWITH_VTK=OFF -DWITH_CAROTENE=OFF -DWITH_OPENEXR=OFF -DWITH_WIN32UI=OFF -DWITH_V4L=OFF -DWITH_DSHOW=OFF -DWITH_MSMF=OFF -DWITH_OPENCLAMDFFT=OFF -DWITH_OPENCLAMDBLAS=OFF -DWITH_PROTOBUF=OFF -DWITH_QUIRC=OFF -DWITH_IPP=OFF -S . -B ./build",
+            "cmake --build ./build --config Release --target install"
+            ],
         "post_build": []
     }
 }
diff --git a/contrib/src/opencv/rules.mak b/contrib/src/opencv/rules.mak
index a45aceeba7bd4250868c0afa57cbcec7be0baa4c..7041ad9562988439bde4baf44d2c4cd8658dfa23 100644
--- a/contrib/src/opencv/rules.mak
+++ b/contrib/src/opencv/rules.mak
@@ -50,7 +50,9 @@ OPENCV_CMAKECONF := \
 		-DWITH_OPENCLAMDBLAS=OFF \
 		-DWITH_PROTOBUF=OFF \
 		-DWITH_QUIRC=OFF \
-		-DWITH_IPP=OFF
+		-DWITH_IPP=OFF \
+		-DBUILD_opencv_java=OFF \
+		-DBUILD_opencv_python=OFF
 
 
 $(TARBALLS)/opencv-$(OPENCV_VERSION).tar.gz:
diff --git a/contrib/src/opencv_contrib/package.json b/contrib/src/opencv_contrib/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..54a57a734ffb7341839dec55f0bb41204e6f05c0
--- /dev/null
+++ b/contrib/src/opencv_contrib/package.json
@@ -0,0 +1,15 @@
+{
+    "name": "opencv_contrib",
+    "version": "4.1.1",
+    "url": "https://github.com/opencv/opencv_contrib/archive/__VERSION__.tar.gz",
+    "deps": [],
+    "patches": [],
+    "win_patches": [],
+    "project_paths": [],
+    "with_env" : "",
+    "custom_scripts": {
+        "pre_build": [],
+        "build": [],
+        "post_build": []
+    }
+}