Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Interlacing #66

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ec8cd66
Fix first line also on preview
rgm89git Sep 13, 2022
7dffa5d
Commenting audio filtering function (atm)
rgm89git Sep 13, 2022
66bdfa9
Merge pull request #1 from rgm89git/fix/firstline
rgm89git Sep 13, 2022
d91dd4d
Another comment
rgm89git Sep 13, 2022
96aad7f
First add to feature
rgm89git Oct 25, 2022
1b67716
Add some thing
rgm89git Oct 25, 2022
a3f1e82
Composite layer fix
rgm89git Oct 28, 2022
00e44fc
New fix
rgm89git Oct 28, 2022
a9d403a
Another fix
rgm89git Oct 28, 2022
e32a560
Finished! - Version 1
rgm89git Dec 10, 2022
abadb6c
Interlacing - First Fix
rgm89git Dec 12, 2022
d637229
Revert subcarrier thing
rgm89git Dec 12, 2022
ebbbf38
Fix interlacing for non-double frame counts
rgm89git Dec 12, 2022
4f479c1
Fix part 2 - interlacing
rgm89git Dec 12, 2022
6c95dd2
Fix part 3 - interlacing
rgm89git Dec 12, 2022
3ee0348
Refactor to be more absctract
Jan 2, 2023
8b402d5
Add read 2 ahead frame buffer
Jan 2, 2023
688fdba
Add next frame context setting
Jan 2, 2023
3d95451
Revert "Fix part 3 - interlacing"
JargeZ Jan 2, 2023
4aaa557
Revert "Fix part 2 - interlacing"
JargeZ Jan 2, 2023
1fc4c1d
Revert "Fix interlacing for non-double frame counts"
JargeZ Jan 2, 2023
b6c0f02
Revert "Revert subcarrier thing"
JargeZ Jan 2, 2023
cb3b869
Revert "Interlacing - First Fix"
JargeZ Jan 2, 2023
2086cd0
Revert "Finished! - Version 1"
JargeZ Jan 2, 2023
61567f3
Revert "Another fix"
JargeZ Jan 2, 2023
1a6d891
Revert "New fix"
JargeZ Jan 2, 2023
9af5fd8
Revert "Composite layer fix"
JargeZ Jan 2, 2023
c11fe41
Revert "Add some thing"
JargeZ Jan 2, 2023
182b993
Revert "First add to feature"
JargeZ Jan 2, 2023
8c2ed49
Revert "Another comment"
JargeZ Jan 2, 2023
f13bf69
Merge branch 'feature/abstract' into feature/interlacing
JargeZ Jan 2, 2023
5e11678
fix corners
JargeZ Jan 2, 2023
f7887c3
move ui-setter to helper func
JargeZ Jan 2, 2023
901f375
Fix last frame preview
JargeZ Jan 2, 2023
cee9781
Implement InterlacedRenderer
JargeZ Jan 2, 2023
5d0b40e
Merge branch 'master' into feature/interlacing
JargeZ Jan 2, 2023
57b9b16
Using copyMakeBorder instead of warpAffine
rgm89git Feb 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions app/InterlacedRenderer.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,10 +1,43 @@
import cv2
from app.Renderer import DefaultRenderer
from app.ntsc import Ntsc


class InterlacedRenderer(DefaultRenderer):
@staticmethod
def apply_main_effect(nt: Ntsc, frame1, frame2=None):
raise NotImplementedError()
# TODO: RGM
if frame2 is None:
frame2 = frame1

frame1 = nt.composite_layer(frame1, frame1, field=0, fieldno=0)
frame1 = cv2.convertScaleAbs(frame1)
#frame2 = nt.composite_layer(frame2, frame2, field=2, fieldno=1)
#frame2 = cv2.convertScaleAbs(frame2)

frame2 = cv2.copyMakeBorder(frame2,1,0,0,0,cv2.BORDER_CONSTANT)
frame2 = nt.composite_layer(frame2, frame2, field=2, fieldno=2)
frame2 = cv2.convertScaleAbs(frame2)
frame = frame1
frame[1::2,:] = frame2[2::2,:]

# import numpy as np
# debug1 = np.concatenate((frame1.copy(), frame2), axis=1)
# debug2 = np.concatenate((frame1[0:-2:2], frame2[2::2]), axis=1)
# frame1[1:-1:2] = frame1[0:-2:2] / 2 + frame2[2::2] / 2
# debug3 = np.concatenate(
# (
# frame1,
# frame1
# ), axis=1)
#
# debug = cv2.vconcat((debug1, debug2, debug3))
# return debug
# TODO: Ensure, that we combine
# N N+1 RESULT
# [A, A, A] [b, b, b] [A, A, A]
# [A, A, A] [b, b, b] [b, b, b]
# [A, A, A] [b, b, b] [A, A, A]
# for now im not sure in field and fieldno behaviour

# frame1[1:-1:2] = frame1[0:-2:2] / 2 + frame2[2::2] / 2
return frame
57 changes: 41 additions & 16 deletions app/NtscApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@
from app.config_dialog import ConfigDialog
from app.logs import logger
from app.Renderer import DefaultRenderer
from app.funcs import resize_to_height, pick_save_file, trim_to_4width
from app.funcs import resize_to_height, pick_save_file, set_ui_element, trim_to_4width
from app.ntsc import random_ntsc, Ntsc
from ui import mainWindow
from ui.DoubleSlider import DoubleSlider




class NtscApp(QtWidgets.QMainWindow, mainWindow.Ui_MainWindow):
def __init__(self):
self.RendererClass = DefaultRenderer
self.videoRenderer: DefaultRenderer = None
self.current_frame: numpy.ndarray = False
self.next_frame: numpy.ndarray = False
Expand Down Expand Up @@ -74,6 +77,7 @@ def __init__(self):
"_vhs_svideo_out": self.tr("VHS svideo out"),
"_output_ntsc": self.tr("NTSC output"),
"_black_line_cut": self.tr("Cut 2% black line"),
"interlaced": self.tr("Interlaced"),
}
self.add_slider("_composite_preemphasis", 0, 10, float)
self.add_slider("_vhs_out_sharpen", 1, 5)
Expand Down Expand Up @@ -107,9 +111,10 @@ def __init__(self):
self.add_checkbox("_vhs_svideo_out", (5, 2), pro=True)
self.add_checkbox("_output_ntsc", (6, 1), pro=True)
self.add_checkbox("_black_line_cut", (1, 2), pro=False)
self.add_checkbox("interlaced", (1, 2), pro=False)

self.renderHeightBox.valueChanged.connect(
lambda: self.set_current_frames(*self.get_current_video_frames())
lambda: self.set_current_frames(self.current_frame, self.next_frame)
)
self.openFile.clicked.connect(self.open_file)
self.renderVideoButton.clicked.connect(self.render_video)
Expand Down Expand Up @@ -163,12 +168,17 @@ def add_builtin_templates(self):
button.clicked.connect(set_values)
self.templatesLayout.addWidget(button)

def get_render_class(self):
is_interlaced = False # Get state from UI choice
def update_render_class(self):
is_interlaced = self.nt_controls["interlaced"].isChecked()
if is_interlaced:
return InterlacedRenderer
logger.debug("Use InterlacedRenderer")
Cls = InterlacedRenderer
else:
return DefaultRenderer
logger.debug("Use DefaultRenderer")
Cls = DefaultRenderer

self.RendererClass = Cls
self.nt_update_preview()

def setup_renderer(self):
try:
Expand All @@ -183,8 +193,7 @@ def setup_renderer(self):
# создадим поток
self.thread = QtCore.QThread()
# создадим объект для выполнения кода в другом потоке
RendererClass = self.get_render_class()
self.videoRenderer = RendererClass()
self.videoRenderer = self.RendererClass()
# перенесём объект в другой поток
self.videoRenderer.moveToThread(self.thread)
# после чего подключим все сигналы и слоты
Expand Down Expand Up @@ -293,7 +302,10 @@ def set_render_state(self, is_render_active):

def sync_nt_to_sliders(self):
for parameter_name, element in self.nt_controls.items():
value = getattr(self.nt, parameter_name)
if parameter_name.startswith("_"):
value = getattr(self.nt, parameter_name)
else:
continue

# This is necessary because some parameters that have a real float type, but in the interface,
# the slide is simplified to int. However, when setting the initial parameters that occur here,
Expand Down Expand Up @@ -321,6 +333,9 @@ def value_changed_slot(self):
elif isinstance(element, QCheckBox):
value = element.isChecked()

if parameter_name == "interlaced":
self.update_render_class()

logger.debug(f"Set {parameter_name} to {value}")
setattr(self.nt, parameter_name, value)
self.nt_update_preview()
Expand Down Expand Up @@ -400,6 +415,7 @@ def add_slider(self, param_name, min_val, max_val, slider_value_type: Union[int,
def get_current_video_frames(self):
preview_h = self.renderHeightBox.value()
if not self.input_video or preview_h < 10:
logger.debug(f"{self.input_video=} {preview_h=}")
return None, None
frame_no = self.videoTrackSlider.value()
self.input_video["cap"].set(1, frame_no)
Expand Down Expand Up @@ -493,16 +509,21 @@ def set_render_heigth(self, height):
self.update_status(
self.tr('The image resolution is large. For the best effect, the output height is set to 600'))
else:
self.renderHeightBox.setValue(height // 120 * 120)
h = height // 120 * 120
if h < 10:
self.renderHeightBox.setValue(120)
else:
self.renderHeightBox.setValue(h)


def open_image(self, img: numpy.ndarray):
self.setup_renderer()
self.update_render_class()
height, width, channels = img.shape
self.orig_wh = width, height

self.set_render_heigth(height)

self.set_current_frames(img)
self.set_current_frames(img, None)

def nt_get_config(self):
values = {}
Expand All @@ -519,12 +540,16 @@ def nt_get_config(self):

def nt_set_config(self, values: List[Dict[str, Union[int, float]]]):
for parameter_name, value in values.items():
setattr(self.nt, parameter_name, value)
if parameter_name.startswith("_"):
setattr(self.nt, parameter_name, value)
else:
element = self.nt_controls[parameter_name]
set_ui_element(element, value)

self.sync_nt_to_sliders()

def open_video(self, path: Path):
self.setup_renderer()
self.update_render_class()
logger.debug(f"file: {path}")
cap = cv2.VideoCapture(str(path.resolve()))
logger.debug(f"cap: {cap} isOpened: {cap.isOpened()}")
Expand Down Expand Up @@ -588,7 +613,7 @@ def render_video(self):
self.thread.start()

def nt_process(self, frame) -> ndarray:
_ = self.nt.composite_layer(frame, frame, field=2, fieldno=2)
_ = self.nt.composite_layer(frame, frame, field=0, fieldno=1)
ntsc_out_image = cv2.convertScaleAbs(_)
ntsc_out_image[1:-1:2] = ntsc_out_image[0:-2:2] / 2 + ntsc_out_image[2::2] / 2
return ntsc_out_image
Expand All @@ -603,7 +628,7 @@ def nt_update_preview(self):
self.render_preview(self.current_frame)
return None

ntsc_out_image = self.videoRenderer.apply_main_effect(self.nt, self.current_frame, self.next_frame)
ntsc_out_image = self.RendererClass.apply_main_effect(self.nt, self.current_frame, self.next_frame)

if self.compareMode:
ntsc_out_image = numpy.concatenate(
Expand Down