Skip to content

Commit

Permalink
simplify code and make it more compatible with versions of opencv whi…
Browse files Browse the repository at this point in the history
…ch may not have all the constants: soft-load conversions at runtime

git-svn-id: https://xpra.org/svn/Xpra/trunk@13183 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Aug 3, 2016
1 parent 1f962d0 commit fcf5f44
Showing 1 changed file with 83 additions and 93 deletions.
176 changes: 83 additions & 93 deletions src/xpra/codecs/csc_opencv/colorspace_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from xpra.codecs.codec_constants import csc_spec
from xpra.codecs.image_wrapper import ImageWrapper
from xpra.util import print_nested_dict
from xpra.os_util import _memoryview


Expand All @@ -35,40 +36,45 @@ def get_info():
info = {"version" : cv2.__version__}
return info

RGB_MODES = ["RGB", "RGBX", "RGBA", "BGR", "BGRA", "BGRX"]
YUV_MODES = ["YUV420P"]
ALL_MODES = RGB_MODES + YUV_MODES

FLAG_STR = {cv2.COLOR_YUV2RGB_I420 : "COLOR_YUV2RGB_I420",
cv2.COLOR_YUV2RGBA_I420 : "COLOR_YUV2RGBA_I420",
cv2.COLOR_YUV2RGBA_I420 : "COLOR_YUV2RGBA_I420",
cv2.COLOR_YUV2BGR_I420 : "COLOR_YUV2BGR_I420",
cv2.COLOR_YUV2BGRA_I420 : "COLOR_YUV2BGRA_I420",
cv2.COLOR_YUV2BGRA_I420 : "COLOR_YUV2BGRA_I420",
cv2.COLOR_RGB2YUV_I420 : "COLOR_RGB2YUV_I420",
cv2.COLOR_RGBA2YUV_I420 : "COLOR_RGBA2YUV_I420",
cv2.COLOR_RGBA2YUV_I420 : "COLOR_RGBA2YUV_I420",
cv2.COLOR_BGR2YUV_I420 : "COLOR_BGR2YUV_I420",
cv2.COLOR_BGRA2YUV_I420 : "COLOR_BGRA2YUV_I420",
cv2.COLOR_BGRA2YUV_I420 : "COLOR_BGRA2YUV_I420"}
FLAG_STR = {}
CONVERSIONS = {}
for rgb_fmt, conv in {
"RGB" : ("YUV2RGB_I420", "RGB2YUV_I420"),
"RGBX" : ("YUV2RGBA_I420", "RGBA2YUV_I420"),
"RGBA" : ("YUV2RGBA_I420", "RGBA2YUV_I420"),
"BGR" : ("YUV2BGR_I420", "BGR2YUV_I420"),
"BGRA" : ("YUV2BGRA_I420", "BGRA2YUV_I420"),
"BGRX" : ("YUV2BGRA_I420", "BGRA2YUV_I420"),
}.items():
fromyuv, toyuv = conv
for src_fmt, dst_fmt, conv_const in (
("YUV420P", rgb_fmt, fromyuv),
(rgb_fmt, "YUV420P", toyuv),
):
f = "COLOR_%s" % conv_const
v = getattr(cv2, f, None)
if v is not None:
FLAG_STR[int(v)] = conv_const
CONVERSIONS.setdefault(src_fmt, {})[dst_fmt] = int(v)
log("csc_opencv FLAGS: %s", FLAG_STR)
log("csc_opencv CONVERSIONS:")
print_nested_dict(CONVERSIONS, print_fn=log)


def get_input_colorspaces():
#return ["YUV420P", "RGB", "RGBA", "RGBX", "BGR", "BGRA", "BGRX"]
return ["RGB", "RGBA", "RGBX", "BGR", "BGRA", "BGRX"]
#we don't handle planar input yet:
return [x for x in CONVERSIONS.keys() if x!="YUV420P"]

def get_output_colorspaces(input_colorspace):
if input_colorspace in YUV_MODES:
return RGB_MODES
assert input_colorspace in RGB_MODES
return YUV_MODES
return CONVERSIONS[input_colorspace]

def get_spec(in_colorspace, out_colorspace):
assert in_colorspace in ALL_MODES, "invalid input colorspace: %s (must be one of %s)" % (in_colorspace, get_input_colorspaces())
assert out_colorspace in ALL_MODES, "invalid output colorspace: %s (must be one of %s)" % (out_colorspace, get_output_colorspaces(in_colorspace))
assert in_colorspace in CONVERSIONS, "invalid input colorspace: %s (must be one of %s)" % (in_colorspace, get_input_colorspaces())
out_cs = CONVERSIONS[in_colorspace]
assert out_colorspace in out_cs, "invalid output colorspace: %s (must be one of %s)" % (out_colorspace, out_cs)
#low score as this should be used as fallback only:
return csc_spec(ColorspaceConverter, codec_type=get_type(), quality=50, speed=0, setup_cost=40, min_w=2, min_h=2, max_w=16*1024, max_h=16*1024, can_scale=False)
#width_mask=0xFFFE, height_mask=0xFFFE)


class ColorspaceConverter(object):
Expand All @@ -82,24 +88,11 @@ def init_context(self, src_width, src_height, src_format, dst_width, dst_height,
self.height = src_height
self.src_format = src_format
self.dst_format = dst_format
self.flag = None
if src_format=="YUV420P":
self.flag = {"RGB" : cv2.COLOR_YUV2RGB_I420,
"RGBX" : cv2.COLOR_YUV2RGBA_I420,
"RGBA" : cv2.COLOR_YUV2RGBA_I420,
"BGR" : cv2.COLOR_YUV2BGR_I420,
"BGRA" : cv2.COLOR_YUV2BGRA_I420,
"BGRX" : cv2.COLOR_YUV2BGRA_I420,
}.get(dst_format)
elif src_format in RGB_MODES and dst_format in YUV_MODES:
self.flag = {"RGB" : cv2.COLOR_RGB2YUV_I420,
"RGBX" : cv2.COLOR_RGBA2YUV_I420,
"RGBA" : cv2.COLOR_RGBA2YUV_I420,
"BGR" : cv2.COLOR_BGR2YUV_I420,
"BGRA" : cv2.COLOR_BGRA2YUV_I420,
"BGRX" : cv2.COLOR_BGRA2YUV_I420,
}.get(src_format)
assert self.flag, "invalid colorspace conversion: %s to %s" % (src_format, dst_format)
global CONVERSIONS
assert src_format in CONVERSIONS, "invalid input colorspace: %s" % src_format
conv = CONVERSIONS[src_format]
assert dst_format in conv, "invalid output colorspace: %s" % dst_format
self.flag = conv[dst_format]
log("csc_opencv: %s to %s=%s (%i)", src_format, dst_format, FLAG_STR.get(self.flag, self.flag), self.flag)

def clean(self): #@DuplicatedSignature
Expand All @@ -114,10 +107,11 @@ def is_closed(self):
return self.flag is None

def get_info(self): #@DuplicatedSignature
info = {
return {
"width" : self.width,
"height" : self.height}
return info
"height" : self.height,
"function" : FLAG_STR.get(self.flag, self.flag),
}

def __repr__(self):
return "csc_opencv(%s to %s - %sx%s)" % (self.src_format, self.dst_format, self.width, self.height)
Expand Down Expand Up @@ -157,55 +151,51 @@ def convert_image(self, image):
assert pixels, "failed to get pixels from %s" % image
input_stride = image.get_rowstride()
log("convert_image(%s) input=%s, strides=%s", image, len(pixels), input_stride)
if self.src_format in YUV_MODES:
raise NotImplementedError()
assert iplanes==ImageWrapper.PACKED, "invalid input format: %s planes" % iplanes
Bpp = len(self.src_format)
if _memoryview and isinstance(pixels, _memoryview):
na = numpy.asarray(pixels, numpy.uint8)
else:
na = numpy.frombuffer(pixels, numpy.uint8)
#stride in number of pixels per line:
stride = input_stride//Bpp
iwidth = self.width #roundup(self.width, 2)
iheight = self.height #roundup(self.height, 2)
if len(pixels)!=input_stride*iheight:
#ensure the input matches the array size we want:
log("resizing numpy array from %s to %s (%i*%i*%i)", na.shape, (iwidth*iheight*Bpp), iwidth, iheight, Bpp)
rgb = numpy.resize(na, (stride*iheight*Bpp))
else:
assert self.src_format in RGB_MODES
assert iplanes==ImageWrapper.PACKED, "invalid input format: %s planes" % iplanes
Bpp = len(self.src_format)
if _memoryview and isinstance(pixels, _memoryview):
na = numpy.asarray(pixels, numpy.uint8)
else:
na = numpy.frombuffer(pixels, numpy.uint8)
#stride in number of pixels per line:
stride = input_stride//Bpp
iwidth = self.width #roundup(self.width, 2)
iheight = self.height #roundup(self.height, 2)
if len(pixels)!=input_stride*iheight:
#ensure the input matches the array size we want:
log("resizing numpy array from %s to %s (%i*%i*%i)", na.shape, (iwidth*iheight*Bpp), iwidth, iheight, Bpp)
rgb = numpy.resize(na, (stride*iheight*Bpp))
else:
rgb = na
#reshape the numpy array into the format expected by opencv:
cv_in_buf = rgb.reshape((iheight, stride, Bpp))
if iheight%2!=0 or stride%2!=0:
#we need to trim the array to use even dimensions for opencv:
nwidth = stride&0xFFFE
nheight = iheight&0xFFFE
#copy into a new array one line at a time:
resized = numpy.empty((nheight, nwidth, Bpp), numpy.uint8)
for i in range(nheight):
resized[i] = cv_in_buf[i][:nwidth]
cv_in_buf = resized
iwidth = nwidth
iheight = nheight
out = cv2.cvtColor(cv_in_buf, self.flag)
log("cv2.cvtColor(%s bytes, %s)=%s", len(pixels), FLAG_STR.get(self.flag, self.flag), out.shape)
#read the output buffer into a flat buffer, then split it into components:
Ystride = iwidth
Ustride = Vstride = Ystride//2
flat_out = out.reshape(-1)
Yend = (Ystride * h)
Y = flat_out.data[:Yend]
Uend = Yend + (Ustride*iheight//2)
U = flat_out.data[Yend:Uend]
Vend = Uend + (Vstride*iheight//2)
V = flat_out.data[Uend:Vend]
pixels = (Y, U, V)
strides = (Ystride, Ustride, Vstride)
planes = ImageWrapper._3_PLANES
log("output strides: %s", strides)
rgb = na
#reshape the numpy array into the format expected by opencv:
cv_in_buf = rgb.reshape((iheight, stride, Bpp))
if iheight%2!=0 or stride%2!=0:
#we need to trim the array to use even dimensions for opencv:
nwidth = stride&0xFFFE
nheight = iheight&0xFFFE
#copy into a new array one line at a time:
resized = numpy.empty((nheight, nwidth, Bpp), numpy.uint8)
for i in range(nheight):
resized[i] = cv_in_buf[i][:nwidth]
cv_in_buf = resized
iwidth = nwidth
iheight = nheight
out = cv2.cvtColor(cv_in_buf, self.flag)
log("cv2.cvtColor(%s bytes, %s)=%s", len(pixels), FLAG_STR.get(self.flag, self.flag), out.shape)
#read the output buffer into a flat buffer, then split it into components:
Ystride = iwidth
Ustride = Vstride = Ystride//2
flat_out = out.reshape(-1)
Yend = (Ystride * h)
Y = flat_out.data[:Yend]
Uend = Yend + (Ustride*iheight//2)
U = flat_out.data[Yend:Uend]
Vend = Uend + (Vstride*iheight//2)
V = flat_out.data[Uend:Vend]
pixels = (Y, U, V)
strides = (Ystride, Ustride, Vstride)
planes = ImageWrapper._3_PLANES
log("output strides: %s", strides)
return ImageWrapper(0, 0, w, h, pixels, self.dst_format, 24, strides, planes)


Expand Down

0 comments on commit fcf5f44

Please sign in to comment.