Skip to content

Commit

Permalink
convert rgb module removed
Browse files Browse the repository at this point in the history
  • Loading branch information
luuvish committed Feb 12, 2013
1 parent 0534a9f commit 6ebf9c7
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 102 deletions.
1 change: 0 additions & 1 deletion yuvist/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""\
Expand Down
21 changes: 21 additions & 0 deletions yuvist/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-

"""\
Kivy YUV Image Viewer
Copyright (C) 2012 Luuvish <luuvish@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

__all__ = ()
122 changes: 122 additions & 0 deletions yuvist/core/image/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-

"""\
Kivy YUV Image Viewer
Copyright (C) 2012 Luuvish <luuvish@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

__all__ = ('Converter', )


class Converter(object):

YUV_FIX = 16 # fixed-point precision
YUV_RANGE_MIN = -227 # min value of r/g/b output
YUV_RANGE_MAX = 256 + 226 # max value of r/g/b output
YUV_HALF = 1 << (YUV_FIX - 1)
VP8kVToR = [0] * 256
VP8kUToB = [0] * 256
VP8kVToG = [0] * 256
VP8kUToG = [0] * 256
VP8kClip = [0] * (YUV_RANGE_MAX - YUV_RANGE_MIN)

def __init__(self):

for i in xrange(256):
self.VP8kVToR[i] = (89858 * (i - 128) + self.YUV_HALF) >> self.YUV_FIX
self.VP8kUToG[i] = -22014 * (i - 128) + self.YUV_HALF
self.VP8kVToG[i] = -45773 * (i - 128)
self.VP8kUToB[i] = (113618 * (i - 128) + self.YUV_HALF) >> self.YUV_FIX

for i in xrange(self.YUV_RANGE_MIN, self.YUV_RANGE_MAX):
k = ((i - 16) * 76283 + self.YUV_HALF) >> self.YUV_FIX
k = 0 if k < 0 else 255 if k > 255 else k
self.VP8kClip[i - self.YUV_RANGE_MIN] = k

def clip(self, value):
return min(max(0, value), 255)

def rgb_float(self, y, u, v):

r = 1.164 * (y-16) + 1.596 * (v-128)
g = 1.164 * (y-16) - 0.391 * (u-128) - 0.813 * (v-128)
b = 1.164 * (y-16) + 2.018 * (u-128)

return self.clip(int(r)), self.clip(int(g)), self.clip(int(b))

def rgb_int(self, y, u, v):

r = 298 * (y-16) + 409 * (v-128)
g = 298 * (y-16) - 100 * (u-128) - 208 * (v-128)
b = 298 * (y-16) + 516 * (u-128)

return self.clip((r+128)>>8), self.clip((g+128)>>8), self.clip((b+128)>>8)

def rgb_table(self, y, u, v):

r_off = self.VP8kVToR[v]
g_off = (self.VP8kVToG[v] + self.VP8kUToG[u]) >> self.YUV_FIX
b_off = self.VP8kUToB[u]

r = self.VP8kClip[y + r_off - self.YUV_RANGE_MIN]
g = self.VP8kClip[y + g_off - self.YUV_RANGE_MIN]
b = self.VP8kClip[y + b_off - self.YUV_RANGE_MIN]

return r, g, b

def raster(self, yuv, buf):

format = yuv.format
subpixel = yuv.image['subpixel']
ysize = yuv.image['size'][0]
csize = yuv.image['size'][1]

ybuf, ubuf, vbuf = buf
y, u, v = 128, 128, 128

for posy in xrange(ysize[1]):

for posx in xrange(ysize[0]):

y = ybuf[ysize[0] * posy + posx]

if format != 'yuv400':
p = csize[0] * (posy // subpixel[1]) + (posx // subpixel[0])
u = ubuf[p]
v = vbuf[p]

yield ord(y), ord(u), ord(v)

def convert(self, yuv, buf, type='float'):

converter = None
if type == 'int':
converter = self.rgb_int
elif type == 'table':
converter = self.rgb_table
else: # type == 'float':
converter = self.rgb_float

rgb = []

for y, u, v in self.raster(yuv, buf):
r, g, b = converter(y, u, v)
rgb.append(r)
rgb.append(g)
rgb.append(b)

rgb = ''.join(map(chr, rgb))
return rgb, '', ''
4 changes: 2 additions & 2 deletions yuvist/core/image/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
BooleanProperty, NumericProperty, OptionProperty)
from kivy.uix.image import Image

from .file import File
from .yuv import Yuv


Builder.load_string('''
Expand Down Expand Up @@ -177,7 +177,7 @@ def _image_load(self, *largs):
return
if self._image is not None:
self._image.unbind(on_texture=self._on_tex_change)
self._image = ci = File(filename, format=self.format, size=self.resolution)
self._image = ci = Yuv(filename, format=self.format, size=self.resolution)
self._image.volume = self.volume
ci.bind(on_texture=self._on_tex_change, on_eos=self._on_eos)
if self.state == 'play' or self.play:
Expand Down
141 changes: 42 additions & 99 deletions yuvist/core/image/file.py → yuvist/core/image/yuv.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,48 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

__all__ = ('File', )
__all__ = ('Yuv', )

import os
from threading import Lock

from kivy.clock import Clock
from kivy.event import EventDispatcher
from kivy.properties import (StringProperty, ObjectProperty,
BooleanProperty, NumericProperty, OptionProperty,
ListProperty, ReferenceListProperty)


YUV_FIX = 16 # fixed-point precision
YUV_RANGE_MIN = -227 # min value of r/g/b output
YUV_RANGE_MAX = 256 + 226 # max value of r/g/b output
YUV_HALF = 1 << (YUV_FIX - 1)
VP8kVToR = [0] * 256
VP8kUToB = [0] * 256
VP8kVToG = [0] * 256
VP8kUToG = [0] * 256
VP8kClip = [0] * (YUV_RANGE_MAX - YUV_RANGE_MIN)
from kivy.properties import BooleanProperty, NumericProperty, StringProperty, \
OptionProperty, ObjectProperty, ListProperty, ReferenceListProperty
from kivy.graphics.texture import Texture

from .converter import Converter

def _init():
for i in xrange(256):
VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX
VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF
VP8kVToG[i] = -45773 * (i - 128)
VP8kUToB[i] = (113618 * (i - 128) + YUV_HALF) >> YUV_FIX

for i in xrange(YUV_RANGE_MIN, YUV_RANGE_MAX):
k = ((i - 16) * 76283 + YUV_HALF) >> YUV_FIX
k = 0 if k < 0 else 255 if k > 255 else k
VP8kClip[i - YUV_RANGE_MIN] = k
YUV_CHROMA_FORMAT = (
'yuv400',
'yuv420',
'yuv422',
'yuv422v',
'yuv444'
)

_init()
YUV_CHROMA_SUBPIXEL = {
'yuv400' : (1, 1),
'yuv420' : (2, 2),
'yuv422' : (1, 2),
'yuv422v': (2, 1),
'yuv444' : (1, 1)
}

OUTPUT_COLOR_FORMAT = (
'yuv',
'rgb'
)

class File(EventDispatcher):

chroma = {
'yuv400' : (1,1),
'yuv420' : (2,2),
'yuv422' : (1,2),
'yuv422v': (2,1),
'yuv444' : (1,1)
}
class Yuv(EventDispatcher):

copy_attributes = ('_size', '_filename', '_texture', '_image')

filename = StringProperty(None)
format = OptionProperty('yuv420', options=('yuv400', 'yuv420',
'yuv422', 'yuv422v', 'yuv444'))
format = OptionProperty(YUV_CHROMA_FORMAT[1], options=YUV_CHROMA_FORMAT)
width = NumericProperty(0)
height = NumericProperty(0)
size = ReferenceListProperty(width, height)
Expand All @@ -79,23 +70,26 @@ class File(EventDispatcher):
volume = NumericProperty(1.)
options = ObjectProperty({})

outfmt = OptionProperty(OUTPUT_COLOR_FORMAT[0], options=OUTPUT_COLOR_FORMAT)
image = ObjectProperty(None)
texture = ListProperty([])

def __init__(self, arg, **kwargs):
self.register_event_type('on_texture')
self.register_event_type('on_eos')

from threading import Lock
self._buffer_lock = Lock()
self._buffer = None

super(File, self).__init__(**kwargs)
super(Yuv, self).__init__(**kwargs)

self.format = kwargs.get('format', 'yuv')
self.format = kwargs.get('format', YUV_CHROMA_FORMAT[1])
self.size = kwargs.get('size', [0, 0])
self.outfmt = kwargs.get('outfmt', OUTPUT_COLOR_FORMAT[0])
self.filename = arg

self.converter = Converter()

def play(self):
Clock.unschedule(self._update_glsl)
Clock.schedule_interval(self._update_glsl, 1 / 30.)
Expand Down Expand Up @@ -134,10 +128,12 @@ def on_position(self, instance, value):
y = fp.read(self.image['byte'][0])
u = fp.read(self.image['byte'][1])
v = fp.read(self.image['byte'][1])
#self._buffer = self._convert_rgb((y, u, v))
#self._update_texture_rgb(self._buffer)
self._buffer = y, u, v
self._update_texture(self._buffer)
if self.outfmt == OUTPUT_COLOR_FORMAT[0]:
self._buffer = y, u, v
self._update_texture(self._buffer)
else:
self._buffer = self.converter.convert(self, (y, u, v), type='float')
self._update_texture_rgb(self._buffer)
self.dispatch('on_texture')
elif value == self.duration:
self.eos = True
Expand Down Expand Up @@ -169,9 +165,9 @@ def _load_image(self, *largs):
raise Exception("Can't open file %s" % filename)
filesize = os.path.getsize(filename)

if format not in self.chroma:
if format not in YUV_CHROMA_SUBPIXEL:
raise Exception("Not support chroma format")
subpixel = self.chroma[format]
subpixel = YUV_CHROMA_SUBPIXEL[format]
ysize = size
csize = size[0] // subpixel[0], size[1] // subpixel[1]
ybyte = ysize[0] * ysize[1]
Expand All @@ -180,7 +176,7 @@ def _load_image(self, *largs):
self.image = {
'file' : fp,
'filesize': filesize,
'subpixel': self.chroma[format],
'subpixel': subpixel,
'size' : (ysize, csize),
'byte' : (ybyte, cbyte, ybyte + cbyte * 2)
}
Expand Down Expand Up @@ -215,7 +211,6 @@ def populate_texture_v(texture):
texture.flip_vertical()
texture.blit_buffer(self._buffer[2], size=csize, colorfmt='luminance')

from kivy.graphics.texture import Texture
texture = [Texture.create(size=ysize, colorfmt='luminance'),
Texture.create(size=csize, colorfmt='luminance'),
Texture.create(size=csize, colorfmt='luminance')]
Expand All @@ -241,61 +236,9 @@ def populate_texture(texture):
texture.flip_vertical()
texture.blit_buffer(self._buffer[0], size=ysize, colorfmt='rgb')

from kivy.graphics.texture import Texture
texture = [Texture.create(size=ysize, colorfmt='rgb'), None, None]
texture[0].add_reload_observer(populate_texture)
texture[0].flip_vertical()

texture[0].blit_buffer(self._buffer[0], size=ysize, colorfmt='rgb')
self.texture = texture

def _convert_rgb(self, buf):
format = self.format
subpixel = self.image['subpixel']
ysize = self.image['size'][0]
csize = self.image['size'][1]

def raster(buf):
ybuf, ubuf, vbuf = buf
y, u, v = 128, 128, 128
for posy in xrange(ysize[1]):
for posx in xrange(ysize[0]):
y = ybuf[ysize[0] * posy + posx]
if format != 'yuv400':
p = csize[0] * (posy // subpixel[1]) + (posx // subpixel[0])
u = ubuf[p]
v = vbuf[p]
yield ord(y), ord(u), ord(v)

clip = lambda x: min(max(0, x), 255)

def rgb(y, u, v):
r = 1.164 * (y-16) + 1.596 * (v-128)
g = 1.164 * (y-16) - 0.391 * (u-128) - 0.813 * (v-128)
b = 1.164 * (y-16) + 2.018 * (u-128)
return clip(int(r)), clip(int(g)), clip(int(b))

def rgb2(y, u, v):
r = 298 * (y-16) + 409 * (v-128)
g = 298 * (y-16) - 100 * (u-128) - 208 * (v-128)
b = 298 * (y-16) + 516 * (u-128)
return clip((r+128)>>8), clip((g+128)>>8), clip((b+128)>>8)

def rgb3(y, u, v):
r_off = VP8kVToR[v]
g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX
b_off = VP8kUToB[u]

r = VP8kClip[y + r_off - YUV_RANGE_MIN]
g = VP8kClip[y + g_off - YUV_RANGE_MIN]
b = VP8kClip[y + b_off - YUV_RANGE_MIN]
return r, g, b

rgb = []
for y, u, v in raster(buf):
r, g, b = rgb3(y, u, v)
rgb.append(r)
rgb.append(g)
rgb.append(b)
rgb = ''.join(map(chr, rgb))
return rgb, '', ''
1 change: 1 addition & 0 deletions yuvist/yuvist.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@


class Yuvist(App):

title = 'Yuvist-v' + __version__
icon = 'data/images/yuvist.png'

Expand Down

0 comments on commit 6ebf9c7

Please sign in to comment.