109 lines
4.7 KiB
Python
109 lines
4.7 KiB
Python
import sys
|
|
import cv2
|
|
import numpy as np
|
|
from PIL import Image
|
|
import objc
|
|
from AppKit import (
|
|
NSApplication, NSApp, NSWindow, NSView, NSImageView, NSButton,
|
|
NSStackView, NSImage, NSBitmapImageRep, NSBackingStoreBuffered,
|
|
NSWindowStyleMaskTitled, NSWindowStyleMaskClosable,
|
|
NSWindowStyleMaskResizable, NSWindowStyleMaskMiniaturizable,
|
|
NSTimer, NSMakeSize, NSMakeRect, NSObject, NSLog,
|
|
NSUserInterfaceLayoutOrientationVertical, NSLayoutAttributeCenterX,
|
|
NSLayoutAttributeCenterY, NSLayoutAttributeWidth, NSLayoutAttributeHeight,
|
|
NSLayoutAttributeTop, NSLayoutAttributeBottom, NSLayoutAttributeLeading,
|
|
NSLayoutAttributeTrailing
|
|
)
|
|
from Foundation import NSObject, NSTimer, NSDate
|
|
|
|
class ItemSenseApp(NSObject):
|
|
def applicationDidFinishLaunching_(self, notification):
|
|
self.window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
|
|
NSMakeRect(0, 0, 800, 600),
|
|
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable,
|
|
NSBackingStoreBuffered,
|
|
False
|
|
)
|
|
self.window.setTitle_("ItemSense")
|
|
self.window.center()
|
|
|
|
# Main content view (StackView for layout)
|
|
self.stack_view = NSStackView.alloc().init()
|
|
self.stack_view.setOrientation_(NSUserInterfaceLayoutOrientationVertical)
|
|
self.stack_view.setSpacing_(10)
|
|
self.stack_view.setEdgeInsets_((10, 10, 10, 10))
|
|
self.window.setContentView_(self.stack_view)
|
|
|
|
# Image View for Camera Feed
|
|
self.image_view = NSImageView.alloc().init()
|
|
self.image_view.setImageScaling_(0) # NSImageScaleProportionallyDown
|
|
self.stack_view.addView_inGravity_(self.image_view, 1) # Top gravity
|
|
|
|
# Capture Button
|
|
self.capture_button = NSButton.buttonWithTitle_target_action_("Capture", self, "captureClicked:")
|
|
self.stack_view.addView_inGravity_(self.capture_button, 3) # Bottom gravity
|
|
|
|
self.window.makeKeyAndOrderFront_(None)
|
|
|
|
# Initialize Camera
|
|
self.cap = cv2.VideoCapture(0)
|
|
if not self.cap.isOpened():
|
|
NSLog("Error: Could not open camera")
|
|
|
|
# Start Timer for 30 FPS
|
|
self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
|
1.0/30.0, self, "updateFrame:", None, True
|
|
)
|
|
|
|
def applicationShouldTerminateAfterLastWindowClosed_(self, sender):
|
|
return True
|
|
|
|
def applicationWillTerminate_(self, notification):
|
|
if hasattr(self, 'cap') and self.cap.isOpened():
|
|
self.cap.release()
|
|
|
|
def updateFrame_(self, timer):
|
|
if hasattr(self, 'cap') and self.cap.isOpened():
|
|
ret, frame = self.cap.read()
|
|
if ret:
|
|
# Convert BGR to RGB
|
|
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
|
|
# Convert to NSImage
|
|
height, width, channels = rgb_frame.shape
|
|
bytes_per_line = channels * width
|
|
|
|
# Create BitmapRep
|
|
bitmap_rep = NSBitmapImageRep.alloc().initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(
|
|
None, width, height, 8, 3, False, False, "NSDeviceRGBColorSpace", bytes_per_line, 24
|
|
)
|
|
|
|
# Copy data
|
|
bitmap_data = bitmap_rep.bitmapData()
|
|
# We need to copy the bytes. This is the PyObjC way to write to the buffer requires a bit of care.
|
|
# A safer/easier way with PIL:
|
|
image = Image.fromarray(rgb_frame)
|
|
img_data = image.tobytes()
|
|
|
|
# Low-level memory copy might be tricky in pure python/objc without unsafe pointers.
|
|
# Alternative: Use PIL to save to memory buffer (TIFF/PNG) and load NSImage from data.
|
|
# This is slightly slower but safer and easier in Python.
|
|
import io
|
|
# Using PPM format is fast (uncompressed)
|
|
header = f"P6 {width} {height} 255 ".encode()
|
|
data = header + rgb_frame.tobytes()
|
|
ns_data = objc.lookUpClass("NSData").dataWithBytes_length_(data, len(data))
|
|
ns_image = NSImage.alloc().initWithData_(ns_data)
|
|
|
|
self.image_view.setImage_(ns_image)
|
|
|
|
def captureClicked_(self, sender):
|
|
print("Capture clicked")
|
|
|
|
if __name__ == "__main__":
|
|
app = NSApplication.sharedApplication()
|
|
delegate = ItemSenseApp.alloc().init()
|
|
app.setDelegate_(delegate)
|
|
NSApp.activateIgnoringOtherApps_(True)
|
|
app.run()
|