Compare commits
No commits in common. "c6371ce2573b1245908c6b01bc03ec32ee99e59c" and "bfe829e75d897f3c039f049046f038dd0b502c0c" have entirely different histories.
c6371ce257
...
bfe829e75d
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"font_sizes":{
|
|
||||||
"smallest":1.5,
|
|
||||||
"small":2.5,
|
|
||||||
"normal":3.5,
|
|
||||||
"large":4.5,
|
|
||||||
"huge":5.5,
|
|
||||||
"hugest":6.5
|
|
||||||
},
|
|
||||||
"font_name":"normal"
|
|
||||||
}
|
|
87
c4k/main.py
87
c4k/main.py
@ -1,87 +0,0 @@
|
|||||||
from kivy.uix.boxlayout import BoxLayout
|
|
||||||
from camera4kivy import Preview
|
|
||||||
from kivyblocks.utils import blocksImage, CSize
|
|
||||||
from kivyblocks.clickable import ClickableImage
|
|
||||||
from kivy.app import App
|
|
||||||
from kivy.utils import platform
|
|
||||||
from kivy.clock import Clock
|
|
||||||
|
|
||||||
class NewCamera(BoxLayout):
|
|
||||||
def __init__(self, **kw):
|
|
||||||
super().__init__(orientation='vertical')
|
|
||||||
self.preview = Preview(**kw)
|
|
||||||
self.add_widget(self.preview)
|
|
||||||
box = BoxLayout(orientation='horizontal',
|
|
||||||
size_hint_y=None,
|
|
||||||
height=CSize(2))
|
|
||||||
self.camera = ClickableImage(size_hint=[None, None],
|
|
||||||
height=CSize(1.6),
|
|
||||||
width=CSize(1.6),
|
|
||||||
pos_hint={
|
|
||||||
'x':self.width/2 - CSize(1.6),
|
|
||||||
'y':0
|
|
||||||
},
|
|
||||||
source=blocksImage('photo.png'),
|
|
||||||
img_kw={
|
|
||||||
'size_hint':[None,None],
|
|
||||||
'height':CSize(1.5),
|
|
||||||
'width':CSize(1.5)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.lensid = ClickableImage(size_hint=[None, None],
|
|
||||||
height=CSize(1.6),
|
|
||||||
width=CSize(1.6),
|
|
||||||
pos_hint={
|
|
||||||
'x':self.width/2,
|
|
||||||
'y':0
|
|
||||||
},
|
|
||||||
source=blocksImage('lensid.png'),
|
|
||||||
img_kw={
|
|
||||||
'size_hint':[None,None],
|
|
||||||
'height':CSize(1.5),
|
|
||||||
'width':CSize(1.5)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.camera.bind(on_press=self.take_a_pic)
|
|
||||||
self.lensid.bind(on_press=self.change_lensid)
|
|
||||||
box.add_widget(self.camera)
|
|
||||||
box.add_widget(self.lensid)
|
|
||||||
self.add_widget(box)
|
|
||||||
# self.bind(size=self.change_btn_position)
|
|
||||||
Clock.schedule_once(self.open_camera, 0.5)
|
|
||||||
|
|
||||||
def open_camera(self, *args):
|
|
||||||
self.preview.connect_camera(aspect_ratio='16:9',
|
|
||||||
filepath_callback=self.photo_saved)
|
|
||||||
|
|
||||||
def photo_saved(self, path:str):
|
|
||||||
print(f'{path} saved')
|
|
||||||
|
|
||||||
def change_lensid(self, *args):
|
|
||||||
if platform not in [ 'android', 'ios' ]:
|
|
||||||
return
|
|
||||||
if self.preview.preview.index == 0:
|
|
||||||
self.preview.select_camera("1")
|
|
||||||
else:
|
|
||||||
self.preview.select_camera("0")
|
|
||||||
|
|
||||||
def change_btn_position(self, *args):
|
|
||||||
self.camera.pos_hint = {
|
|
||||||
'x':self.width/2 - CSize(1.6),
|
|
||||||
'y':0
|
|
||||||
}
|
|
||||||
self.lensid.pos_hint = {
|
|
||||||
'x':self.width/2,
|
|
||||||
'y':0
|
|
||||||
}
|
|
||||||
|
|
||||||
def take_a_pic(self, o):
|
|
||||||
self.preview.capture_photo()
|
|
||||||
|
|
||||||
class C4KApp(App):
|
|
||||||
def build(self):
|
|
||||||
x = NewCamera()
|
|
||||||
return x
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
C4KApp().run()
|
|
101
kivycv/audio.py
101
kivycv/audio.py
@ -16,7 +16,6 @@ class Audio(AppLogger, pyaudio.PyAudio):
|
|||||||
self.chunk = 1024
|
self.chunk = 1024
|
||||||
self.devices = [ self.get_device_info_by_index(i) \
|
self.devices = [ self.get_device_info_by_index(i) \
|
||||||
for i in range(self.get_device_count()) ]
|
for i in range(self.get_device_count()) ]
|
||||||
self.recording = False
|
|
||||||
|
|
||||||
def get_input_device(self):
|
def get_input_device(self):
|
||||||
return [ d for d in self.devices if d.maxInputChannels > 0 ]
|
return [ d for d in self.devices if d.maxInputChannels > 0 ]
|
||||||
@ -28,16 +27,24 @@ class Audio(AppLogger, pyaudio.PyAudio):
|
|||||||
x = tempfile.mkstemp(suffix='.wav')
|
x = tempfile.mkstemp(suffix='.wav')
|
||||||
os.close(x[0])
|
os.close(x[0])
|
||||||
self.temp_filename = x[1]
|
self.temp_filename = x[1]
|
||||||
return self.temp_filename
|
|
||||||
|
|
||||||
def record_cb(self, in_data, frame_count, time_info, status):
|
def record_cb(self, in_data, frame_count, time_info, status):
|
||||||
|
bdata = pickle.dumps(in_data)
|
||||||
|
self.info('frame_count=%s, time_info=%s, status=%s, bytes count=%s', \
|
||||||
|
frame_count, time_info, status, len(bdata))
|
||||||
|
self.rec_frames += frame_count
|
||||||
|
self.current_ts = time.time()
|
||||||
self.wavfile.writeframesraw(in_data)
|
self.wavfile.writeframesraw(in_data)
|
||||||
if self.recording:
|
if self.running:
|
||||||
return (None, pyaudio.paContinue)
|
return (None, pyaudio.paContinue)
|
||||||
|
|
||||||
return (None, pyaudio.paComplete)
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
def replay_cb(self, in_data, frame_count, time_info, status):
|
def replay_cb(self, in_data, frame_count, time_info, status):
|
||||||
data = self.wavfile.readframes(frame_count)
|
data = self.wavfile.readframes(frame_count)
|
||||||
|
bdata = pickle.dumps(data)
|
||||||
|
self.info('frame_count=%s, data length in bytes=%s', \
|
||||||
|
frame_count, len(bdata))
|
||||||
if not data:
|
if not data:
|
||||||
return (None, pyaudio.paComplete)
|
return (None, pyaudio.paComplete)
|
||||||
|
|
||||||
@ -50,74 +57,47 @@ class Audio(AppLogger, pyaudio.PyAudio):
|
|||||||
print(x)
|
print(x)
|
||||||
return dev_cnt - 1
|
return dev_cnt - 1
|
||||||
|
|
||||||
def write_audiofile(self, fn, audio_data, channels=2, rate=44100):
|
def record(self, save_file=None, stop_cond_func=None):
|
||||||
wf = wave.open(fn, 'wb')
|
filename = save_file
|
||||||
wf.setnchannels(channels)
|
if filename is None:
|
||||||
wf.setsampwidth(2)
|
self.tmpfile()
|
||||||
wf.setframerate(rate)
|
filename = self.temp_filename
|
||||||
wf.writeframesraw(audio_data)
|
|
||||||
wf.close()
|
|
||||||
|
|
||||||
def start_record(self,
|
self.wavfile = wave.open(filename, 'wb')
|
||||||
savefile=None,
|
self.wavfile.setnchannels(2)
|
||||||
channels=2,
|
|
||||||
rate=44100
|
|
||||||
):
|
|
||||||
if savefile is None:
|
|
||||||
savefile = self.tmpfile()
|
|
||||||
self.save_file = savefile
|
|
||||||
self.wavfile = wave.open(self.save_file, 'wb')
|
|
||||||
self.wavfile.setnchannels(channels)
|
|
||||||
self.wavfile.setsampwidth(2)
|
self.wavfile.setsampwidth(2)
|
||||||
self.wavfile.setframerate(rate)
|
self.wavfile.setframerate(44100.00)
|
||||||
self.stream = self.open(format=pyaudio.paInt16,
|
self.stream = self.open(format=pyaudio.paInt16,
|
||||||
channels=channels,
|
channels=2,
|
||||||
rate=rate,
|
rate=44100,
|
||||||
input=True,
|
input=True,
|
||||||
frames_per_buffer=self.chunk,
|
frames_per_buffer=self.chunk,
|
||||||
stream_callback=self.record_cb)
|
stream_callback=self.record_cb)
|
||||||
self.stream.start_stream()
|
self.stream.start_stream()
|
||||||
self.recording = True
|
self.running = True
|
||||||
|
self.rec_frames = 0
|
||||||
|
self.start_ts = self.current_ts = time.time()
|
||||||
|
while self.stream.is_active():
|
||||||
|
if stop_cond_func and stop_cond_func():
|
||||||
|
self.running = False
|
||||||
|
time.sleep(0.05)
|
||||||
|
self.stream.stop_stream()
|
||||||
|
self.stream.close()
|
||||||
|
self.wavfile.close()
|
||||||
|
if save_file is None:
|
||||||
|
self.replay()
|
||||||
|
|
||||||
def stop_record(self):
|
|
||||||
if self.recording:
|
|
||||||
self.recording = False
|
|
||||||
self.stream.stop_stream()
|
|
||||||
self.stream.close()
|
|
||||||
self.wavfile.close()
|
|
||||||
|
|
||||||
def record(self, stop_cb,
|
|
||||||
savefile=None,
|
|
||||||
channels=2,
|
|
||||||
rate=44100,
|
|
||||||
):
|
|
||||||
self.start_record(savefile=savefile, channels=channels,rate=rate)
|
|
||||||
while not stop_cb():
|
|
||||||
time.sleep(0.1)
|
|
||||||
self.stop_record()
|
|
||||||
|
|
||||||
def get_audio_spec(self, audiofile):
|
|
||||||
wavfile = wave.open(audiofile, 'rb')
|
|
||||||
sampwidth = wavfile.getsampwidth()
|
|
||||||
format = self.get_format_from_width(sampwidth)
|
|
||||||
framerate=wavfile.getframerate()
|
|
||||||
channels = wavfile.getnchannels()
|
|
||||||
return {
|
|
||||||
"format":format,
|
|
||||||
"sampwidth":sampwidth,
|
|
||||||
"framerate":framerate,
|
|
||||||
"channels":channels
|
|
||||||
}
|
|
||||||
|
|
||||||
def replay(self, play_file=None):
|
def replay(self, play_file=None):
|
||||||
idx = self.get_output_index()
|
idx = self.get_output_index()
|
||||||
x = self.get_device_info_by_index(idx)
|
x = self.get_device_info_by_index(idx)
|
||||||
y = self.get_default_input_device_info()
|
y = self.get_default_input_device_info()
|
||||||
|
self.info('default_input=%s, default_output=%s', y, x)
|
||||||
if play_file is None:
|
if play_file is None:
|
||||||
play_file = self.temp_filename
|
play_file = self.temp_filename
|
||||||
self.wavfile = wave.open(play_file, 'rb')
|
self.wavfile = wave.open(play_file, 'rb')
|
||||||
format = self.get_format_from_width(self.wavfile.getsampwidth())
|
format = self.get_format_from_width(self.wavfile.getsampwidth())
|
||||||
framerate=self.wavfile.getframerate()
|
framerate=self.wavfile.getframerate()
|
||||||
|
self.info('format=%s, framerate=%s', format, framerate)
|
||||||
self.stream = self.open(format=format,
|
self.stream = self.open(format=format,
|
||||||
channels=self.wavfile.getnchannels(),
|
channels=self.wavfile.getnchannels(),
|
||||||
rate=framerate,
|
rate=framerate,
|
||||||
@ -132,14 +112,15 @@ class Audio(AppLogger, pyaudio.PyAudio):
|
|||||||
self.stream.close()
|
self.stream.close()
|
||||||
self.wavfile.close()
|
self.wavfile.close()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.temp_filename:
|
||||||
|
os.remove(self.temp_filename)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
t_begin = time.time()
|
|
||||||
def stop_func(audio):
|
def stop_func(audio):
|
||||||
t = time.time()
|
if audio.current_ts - audio.start_ts >= 10:
|
||||||
if t - t_begin >= 10:
|
audio.running = False
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
create_logger('audio', levelname='debug')
|
create_logger('audio', levelname='debug')
|
||||||
a = Audio()
|
a = Audio()
|
||||||
@ -154,4 +135,4 @@ if __name__ == '__main__':
|
|||||||
if len(sys.argv) >= 2:
|
if len(sys.argv) >= 2:
|
||||||
sf = sys.argv[1]
|
sf = sys.argv[1]
|
||||||
f = partial(stop_func, a)
|
f = partial(stop_func, a)
|
||||||
a.record(f, savefile=sf)
|
a.record(sf, stop_cond_func=f)
|
||||||
|
@ -3,14 +3,14 @@ import tempfile
|
|||||||
import pyaudio
|
import pyaudio
|
||||||
import wave
|
import wave
|
||||||
from kivy.event import EventDispatcher
|
from kivy.event import EventDispatcher
|
||||||
from kivy.properties import NumericProperty, StringProperty, ObjectProperty
|
from kivy.properties import NumericProperty
|
||||||
from kivyblocks.baseWidget import HBox
|
from kivyblocks.baseWidget import HBox
|
||||||
from kivycv.micphone import Micphone
|
from kviycv.micphone import Micphone
|
||||||
|
|
||||||
class AudioRecorder(EventDispatcher):
|
class AudioRecorder(EventDispatcher):
|
||||||
fs = NumericProperty(None)
|
fs = NumericProperty(None)
|
||||||
filename = StringProperty(None)
|
filename = StringProperty(None)
|
||||||
voice_src = ObjectProperty(None)
|
voice_src = OjbectProperty(None)
|
||||||
def __init__(self, **kw):
|
def __init__(self, **kw):
|
||||||
super(AudioRecorder, self).__init__(**kw)
|
super(AudioRecorder, self).__init__(**kw)
|
||||||
self.saving = False
|
self.saving = False
|
||||||
|
@ -6,7 +6,7 @@ from kivy.uix.camera import Camera
|
|||||||
from kivy.properties import NumericProperty
|
from kivy.properties import NumericProperty
|
||||||
from kivy.event import EventDispatcher
|
from kivy.event import EventDispatcher
|
||||||
|
|
||||||
from kivyblocks.android_rotation import *
|
from .android_rotation import *
|
||||||
|
|
||||||
if kivy.platform in [ 'win', 'linux', 'macosx' ]:
|
if kivy.platform in [ 'win', 'linux', 'macosx' ]:
|
||||||
from PIL import ImageGrab
|
from PIL import ImageGrab
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import pyaudio
|
|
||||||
from kivy.uix.label import Label
|
|
||||||
from kivy.properties import NumericProperty
|
|
||||||
from kivyblocks.baseWidget import HBox, VBox
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
try:
|
|
||||||
from .audio import Audio
|
|
||||||
except:
|
|
||||||
from audio import Audio
|
|
||||||
|
|
||||||
class Recorder(VBox):
|
|
||||||
rate = NumericProperty(44100)
|
|
||||||
channel = NumericProperty(2)
|
|
||||||
def __init__(self, **kw):
|
|
||||||
super().__init__(**kw)
|
|
||||||
self.audio = Audio()
|
|
||||||
self.register_event_type('on_recorded')
|
|
||||||
|
|
||||||
def on_recorded(self, record_file):
|
|
||||||
print('on_recorded:', record_file)
|
|
||||||
|
|
||||||
def on_touch_down(self, touch):
|
|
||||||
if self.collide_point(*touch.pos):
|
|
||||||
self.audio.start_record(channels=self.channel,
|
|
||||||
rate=self.rate)
|
|
||||||
return False
|
|
||||||
return super().on_touch_down(touch)
|
|
||||||
|
|
||||||
def on_touch_up(self, touch):
|
|
||||||
if self.audio.recording:
|
|
||||||
self.audio.stop_record()
|
|
||||||
self.dispatch('on_recorded', self.audio.save_file)
|
|
||||||
|
|
||||||
return super().on_touch_up(touch)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from kivy.app import App
|
|
||||||
class TestApp(App):
|
|
||||||
def build(self):
|
|
||||||
x = Recorder()
|
|
||||||
self.recorder = x
|
|
||||||
x.bind(on_recorded=self.play_audio)
|
|
||||||
return x
|
|
||||||
|
|
||||||
def play_audio(self, o, f):
|
|
||||||
print('play file', f)
|
|
||||||
self.recorder.audio.replay(f)
|
|
||||||
print('play finished')
|
|
||||||
# os.remove(f)
|
|
||||||
|
|
||||||
TestApp().run()
|
|
@ -1,13 +1,13 @@
|
|||||||
from kivy.factory import Factory
|
from kivy.factory import Factory
|
||||||
|
|
||||||
from .audio import Audio
|
from audio import Audio
|
||||||
from .audiorecorder import AudioRecorder
|
from audiorecorder import AudioRecorder
|
||||||
from .camerawithmic import CameraWithMic
|
from camerawithmic import CameraWithMic
|
||||||
from .custom_camera import CustomCamera, QrReader
|
from custom_camera import CustomCamera, QrReader
|
||||||
from .kivycamera import KivyCamera
|
from kivycamera import KivyCamera
|
||||||
from .micphone import Micphone
|
from micphone import Micphone
|
||||||
from .qrcodereader import QRCodeReader
|
from qrcodereader import QRCodeReader
|
||||||
from .version import __version__
|
from version import __version__
|
||||||
r = Factory.register
|
r = Factory.register
|
||||||
r('Audio', Audio)
|
r('Audio', Audio)
|
||||||
r('AudioRecorder', AudioRecorder)
|
r('AudioRecorder', AudioRecorder)
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
"""
|
|
||||||
Exposes `XCamera` directly in `xcamera` rather than `xcamera.xcamera`.
|
|
||||||
Also note this may break `pip` since all imports within `xcamera.py` would be
|
|
||||||
required at setup time. This is because `version.py` (same directory) is used
|
|
||||||
by the `setup.py` file.
|
|
||||||
Hence we're not exposing `XCamera` if `pip` is detected.
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
project_dir = os.path.abspath(
|
|
||||||
os.path.join(__file__, os.pardir, os.pardir, os.pardir, os.pardir))
|
|
||||||
using_pip = os.path.basename(project_dir).startswith('pip-')
|
|
||||||
# only exposes `XCamera` if not within `pip` ongoing install
|
|
||||||
if not using_pip:
|
|
||||||
from .xcamera import XCamera # noqa
|
|
@ -1,85 +0,0 @@
|
|||||||
from kivy.logger import Logger
|
|
||||||
|
|
||||||
from jnius import JavaException, PythonJavaClass, autoclass, java_method
|
|
||||||
|
|
||||||
Camera = autoclass('android.hardware.Camera')
|
|
||||||
AndroidActivityInfo = autoclass('android.content.pm.ActivityInfo')
|
|
||||||
AndroidPythonActivity = autoclass('org.kivy.android.PythonActivity')
|
|
||||||
PORTRAIT = AndroidActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
|
||||||
LANDSCAPE = AndroidActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
|
||||||
|
|
||||||
|
|
||||||
class ShutterCallback(PythonJavaClass):
|
|
||||||
__javainterfaces__ = ('android.hardware.Camera$ShutterCallback', )
|
|
||||||
|
|
||||||
@java_method('()V')
|
|
||||||
def onShutter(self):
|
|
||||||
# apparently, it is enough to have an empty shutter callback to play
|
|
||||||
# the standard shutter sound. If you pass None instead of shutter_cb
|
|
||||||
# below, the standard sound doesn't play O_o
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PictureCallback(PythonJavaClass):
|
|
||||||
__javainterfaces__ = ('android.hardware.Camera$PictureCallback', )
|
|
||||||
|
|
||||||
def __init__(self, filename, on_success):
|
|
||||||
super(PictureCallback, self).__init__()
|
|
||||||
self.filename = filename
|
|
||||||
self.on_success = on_success
|
|
||||||
|
|
||||||
@java_method('([BLandroid/hardware/Camera;)V')
|
|
||||||
def onPictureTaken(self, data, camera):
|
|
||||||
s = data.tostring()
|
|
||||||
with open(self.filename, 'wb') as f:
|
|
||||||
f.write(s)
|
|
||||||
Logger.info('xcamera: picture saved to %s', self.filename)
|
|
||||||
camera.startPreview()
|
|
||||||
self.on_success(self.filename)
|
|
||||||
|
|
||||||
|
|
||||||
class AutoFocusCallback(PythonJavaClass):
|
|
||||||
__javainterfaces__ = ('android.hardware.Camera$AutoFocusCallback', )
|
|
||||||
|
|
||||||
def __init__(self, filename, on_success):
|
|
||||||
super(AutoFocusCallback, self).__init__()
|
|
||||||
self.filename = filename
|
|
||||||
self.on_success = on_success
|
|
||||||
|
|
||||||
@java_method('(ZLandroid/hardware/Camera;)V')
|
|
||||||
def onAutoFocus(self, success, camera):
|
|
||||||
if success:
|
|
||||||
Logger.info('xcamera: autofocus succeeded, taking picture...')
|
|
||||||
shutter_cb = ShutterCallback()
|
|
||||||
picture_cb = PictureCallback(self.filename, self.on_success)
|
|
||||||
camera.takePicture(shutter_cb, None, picture_cb)
|
|
||||||
else:
|
|
||||||
Logger.info('xcamera: autofocus failed')
|
|
||||||
|
|
||||||
|
|
||||||
def take_picture(camera_widget, filename, on_success):
|
|
||||||
# to call the android API, we need access to the underlying
|
|
||||||
# android.hardware.Camera instance. However, there is no official way to
|
|
||||||
# retrieve it from the camera widget, so we need to dig into internal
|
|
||||||
# attributes :-( This works at least on kivy 1.9.1, but it might break any
|
|
||||||
# time soon.
|
|
||||||
camera = camera_widget._camera._android_camera
|
|
||||||
params = camera.getParameters()
|
|
||||||
params.setFocusMode("auto")
|
|
||||||
camera.setParameters(params)
|
|
||||||
cb = AutoFocusCallback(filename, on_success)
|
|
||||||
Logger.info('xcamera: starting autofocus...')
|
|
||||||
try:
|
|
||||||
camera.autoFocus(cb)
|
|
||||||
except JavaException as e:
|
|
||||||
Logger.info('Error when calling autofocus: {}'.format(e))
|
|
||||||
|
|
||||||
|
|
||||||
def set_orientation(value):
|
|
||||||
previous = get_orientation()
|
|
||||||
AndroidPythonActivity.mActivity.setRequestedOrientation(value)
|
|
||||||
return previous
|
|
||||||
|
|
||||||
|
|
||||||
def get_orientation():
|
|
||||||
return AndroidPythonActivity.mActivity.getRequestedOrientation()
|
|
Binary file not shown.
Binary file not shown.
@ -1,43 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
from kivy.app import App
|
|
||||||
from kivy.lang import Builder
|
|
||||||
|
|
||||||
kv = """
|
|
||||||
#:import XCamera kivy_garden.xcamera.XCamera
|
|
||||||
|
|
||||||
FloatLayout:
|
|
||||||
orientation: 'vertical'
|
|
||||||
|
|
||||||
XCamera:
|
|
||||||
id: xcamera
|
|
||||||
on_picture_taken: app.picture_taken(*args)
|
|
||||||
|
|
||||||
BoxLayout:
|
|
||||||
orientation: 'horizontal'
|
|
||||||
size_hint: 1, None
|
|
||||||
height: sp(50)
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: 'Set landscape'
|
|
||||||
on_release: xcamera.force_landscape()
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: 'Restore orientation'
|
|
||||||
on_release: xcamera.restore_orientation()
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class CameraApp(App):
|
|
||||||
def build(self):
|
|
||||||
return Builder.load_string(kv)
|
|
||||||
|
|
||||||
def picture_taken(self, obj, filename):
|
|
||||||
print('Picture taken and saved to {}'.format(filename))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
CameraApp().run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,37 +0,0 @@
|
|||||||
from kivy.utils import platform
|
|
||||||
|
|
||||||
|
|
||||||
def play_shutter():
|
|
||||||
# bah, apparently we need to delay the import of kivy.core.audio, lese
|
|
||||||
# kivy cannot find a camera provider, at lease on linux. Maybe a
|
|
||||||
# gstreamer/pygame issue?
|
|
||||||
from kivy.core.audio import SoundLoader
|
|
||||||
sound = SoundLoader.load("data/shutter.wav")
|
|
||||||
sound.play()
|
|
||||||
|
|
||||||
|
|
||||||
if platform == 'android':
|
|
||||||
from .android_api import (
|
|
||||||
LANDSCAPE, PORTRAIT, take_picture, set_orientation, get_orientation)
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
# generic fallback for taking pictures. Probably not the best quality,
|
|
||||||
# they are meant mostly for testing
|
|
||||||
LANDSCAPE = 'landscape'
|
|
||||||
PORTRAIT = 'portrait'
|
|
||||||
|
|
||||||
def take_picture(camera_widget, filename, on_success):
|
|
||||||
camera_widget.texture.save(filename, flipped=False)
|
|
||||||
play_shutter()
|
|
||||||
on_success(filename)
|
|
||||||
|
|
||||||
def set_orientation(value):
|
|
||||||
previous = get_orientation()
|
|
||||||
print('FAKE orientation set to {}'.format(value))
|
|
||||||
get_orientation.value = value
|
|
||||||
return previous
|
|
||||||
|
|
||||||
def get_orientation():
|
|
||||||
return get_orientation.value
|
|
||||||
get_orientation.value = PORTRAIT
|
|
@ -1 +0,0 @@
|
|||||||
__version__ = '2020.0613'
|
|
@ -1,41 +0,0 @@
|
|||||||
#:import xcamera kivyblocks.xcamera.xcamera
|
|
||||||
|
|
||||||
<XCameraIconButton>
|
|
||||||
icon_color: (0, 0, 0, 1)
|
|
||||||
_down_color: xcamera.darker(self.icon_color)
|
|
||||||
icon_size: dp(50)
|
|
||||||
|
|
||||||
canvas.before:
|
|
||||||
Color:
|
|
||||||
rgba: self.icon_color if self.state == 'normal' else self._down_color
|
|
||||||
Ellipse:
|
|
||||||
pos: self.pos
|
|
||||||
size: self.size
|
|
||||||
|
|
||||||
size_hint: None, None
|
|
||||||
size: self.icon_size, self.icon_size
|
|
||||||
font_size: self.icon_size/2
|
|
||||||
|
|
||||||
|
|
||||||
<XCamera>:
|
|
||||||
# \ue800 corresponds to the camera icon in the font
|
|
||||||
icon: u"[font=data/icons.ttf]\ue800[/font]"
|
|
||||||
icon_color: (0.13, 0.58, 0.95, 0.8)
|
|
||||||
icon_size: dp(70)
|
|
||||||
|
|
||||||
id: camera
|
|
||||||
resolution: 640, 480 # 1920, 1080
|
|
||||||
allow_stretch: True
|
|
||||||
|
|
||||||
# Shoot button
|
|
||||||
XCameraIconButton:
|
|
||||||
id: shoot_button
|
|
||||||
markup: True
|
|
||||||
text: root.icon
|
|
||||||
icon_color: root.icon_color
|
|
||||||
icon_size: root.icon_size
|
|
||||||
on_release: root.shoot()
|
|
||||||
|
|
||||||
# position
|
|
||||||
right: root.width - dp(10)
|
|
||||||
center_y: root.center_y
|
|
@ -1,116 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import os
|
|
||||||
|
|
||||||
from kivy.clock import mainthread
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.properties import ObjectProperty
|
|
||||||
from kivy.resources import resource_add_path
|
|
||||||
from kivy.uix.behaviors import ButtonBehavior
|
|
||||||
from kivy.uix.camera import Camera
|
|
||||||
from kivy.uix.label import Label
|
|
||||||
from kivy.utils import platform
|
|
||||||
|
|
||||||
from .platform_api import LANDSCAPE, set_orientation, take_picture
|
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
resource_add_path(ROOT)
|
|
||||||
|
|
||||||
|
|
||||||
def darker(color, factor=0.5):
|
|
||||||
r, g, b, a = color
|
|
||||||
r *= factor
|
|
||||||
g *= factor
|
|
||||||
b *= factor
|
|
||||||
return r, g, b, a
|
|
||||||
|
|
||||||
|
|
||||||
def get_filename():
|
|
||||||
return datetime.datetime.now().strftime('%Y-%m-%d %H.%M.%S.jpg')
|
|
||||||
|
|
||||||
|
|
||||||
def is_android():
|
|
||||||
return platform == 'android'
|
|
||||||
|
|
||||||
|
|
||||||
def check_camera_permission():
|
|
||||||
"""
|
|
||||||
Android runtime `CAMERA` permission check.
|
|
||||||
"""
|
|
||||||
if not is_android():
|
|
||||||
return True
|
|
||||||
from android.permissions import Permission, check_permission
|
|
||||||
permission = Permission.CAMERA
|
|
||||||
return check_permission(permission)
|
|
||||||
|
|
||||||
|
|
||||||
def check_request_camera_permission(callback=None):
|
|
||||||
"""
|
|
||||||
Android runtime `CAMERA` permission check & request.
|
|
||||||
"""
|
|
||||||
had_permission = check_camera_permission()
|
|
||||||
if not had_permission:
|
|
||||||
from android.permissions import Permission, request_permissions
|
|
||||||
permissions = [Permission.CAMERA]
|
|
||||||
request_permissions(permissions, callback)
|
|
||||||
return had_permission
|
|
||||||
|
|
||||||
|
|
||||||
class XCameraIconButton(ButtonBehavior, Label):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class XCamera(Camera):
|
|
||||||
directory = ObjectProperty(None)
|
|
||||||
_previous_orientation = None
|
|
||||||
__events__ = ('on_picture_taken', 'on_camera_ready')
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
# Builder.load_file(os.path.join(ROOT, "xcamera.kv"))
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
|
|
||||||
def _on_index(self, *largs):
|
|
||||||
"""
|
|
||||||
Overrides `kivy.uix.camera.Camera._on_index()` to make sure
|
|
||||||
`camera.open()` is not called unless Android `CAMERA` permission is
|
|
||||||
granted, refs #5.
|
|
||||||
"""
|
|
||||||
@mainthread
|
|
||||||
def on_permissions_callback(permissions, grant_results):
|
|
||||||
"""
|
|
||||||
On camera permission callback calls parent `_on_index()` method.
|
|
||||||
"""
|
|
||||||
if all(grant_results):
|
|
||||||
self._on_index_dispatch(*largs)
|
|
||||||
if check_request_camera_permission(callback=on_permissions_callback):
|
|
||||||
self._on_index_dispatch(*largs)
|
|
||||||
|
|
||||||
def _on_index_dispatch(self, *largs):
|
|
||||||
super()._on_index(*largs)
|
|
||||||
self.dispatch('on_camera_ready')
|
|
||||||
|
|
||||||
def on_picture_taken(self, filename):
|
|
||||||
"""
|
|
||||||
This event is fired every time a picture has been taken.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_camera_ready(self):
|
|
||||||
"""
|
|
||||||
Fired when the camera is ready.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def shoot(self):
|
|
||||||
def on_success(filename):
|
|
||||||
self.dispatch('on_picture_taken', filename)
|
|
||||||
filename = get_filename()
|
|
||||||
if self.directory:
|
|
||||||
filename = os.path.join(self.directory, filename)
|
|
||||||
take_picture(self, filename, on_success)
|
|
||||||
|
|
||||||
def force_landscape(self):
|
|
||||||
self._previous_orientation = set_orientation(LANDSCAPE)
|
|
||||||
|
|
||||||
def restore_orientation(self):
|
|
||||||
if self._previous_orientation is not None:
|
|
||||||
set_orientation(self._previous_orientation)
|
|
56
setup.py
56
setup.py
@ -1,56 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from kivycv.version import __version__
|
|
||||||
import codecs
|
|
||||||
try:
|
|
||||||
from setuptools import setup
|
|
||||||
except ImportError:
|
|
||||||
from distutils.core import setup
|
|
||||||
|
|
||||||
description = "kivy opencv related modules"
|
|
||||||
author = "yumoqing"
|
|
||||||
email = "yumoqing@icloud.com"
|
|
||||||
version = __version__
|
|
||||||
depandent_packages = []
|
|
||||||
with codecs.open('./requirements.txt', 'r', 'utf-8') as f:
|
|
||||||
b = f.read()
|
|
||||||
b = ''.join(b.split('\r'))
|
|
||||||
depandent_packages = b.split('\n')
|
|
||||||
|
|
||||||
package_data = {
|
|
||||||
"kivycv":[
|
|
||||||
"*.txt",
|
|
||||||
'xcamera/data/*.ttf',
|
|
||||||
'xcamera/data/*.wav',
|
|
||||||
'xcamera/xcamera.kv',
|
|
||||||
'image_processing/cascades/*.xml'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="kivycv",
|
|
||||||
ext_modules= [
|
|
||||||
],
|
|
||||||
version=version,
|
|
||||||
|
|
||||||
# uncomment the following lines if you fill them out in release.py
|
|
||||||
description=description,
|
|
||||||
author=author,
|
|
||||||
author_email=email,
|
|
||||||
|
|
||||||
install_requires=depandent_packages,
|
|
||||||
packages=[
|
|
||||||
'kivycv',
|
|
||||||
'kivycv.xcamera',
|
|
||||||
'kivycv.image_processing'
|
|
||||||
],
|
|
||||||
package_data=package_data,
|
|
||||||
keywords = [
|
|
||||||
],
|
|
||||||
classifiers = [
|
|
||||||
'Development Status :: 3 - Alpha',
|
|
||||||
'Operating System :: OS Independent',
|
|
||||||
'Programming Language :: Python :: 3.5',
|
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
|
||||||
],
|
|
||||||
platforms= 'any'
|
|
||||||
)
|
|
Loading…
Reference in New Issue
Block a user