Skip to content

Commit

Permalink
Add the possibility to resize images before sending them to the API
Browse files Browse the repository at this point in the history
  • Loading branch information
AAClause committed Dec 3, 2023
1 parent 7aa6a67 commit 7d9ecfc
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 20 deletions.
98 changes: 81 additions & 17 deletions addon/globalPlugins/openai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@
"conversationMode": "boolean(default=True)",
"saveSystem": "boolean(default=False)",
"advancedMode": "boolean(default=False)",
"images": {
"maxHeight": "integer(min=0, default=720)",
"maxWidth": "integer(min=0, default=0)",
"quality": "integer(min=0, max=100, default=85)",
"resize": "boolean(default=False)",
"resizeInfoDisplayed": "boolean(default=False)"
},
"renewClient": "boolean(default=False)",
"debug": "boolean(default=False)"
}
Expand Down Expand Up @@ -106,47 +113,41 @@ def makeSettings(self, settingsSizer):

sHelper.addItem(orgSizer)

mainDialogGroup = _("Main dialog")
mainDialogSizer = wx.StaticBoxSizer(wx.VERTICAL, self, label=mainDialogGroup)
mainDialogGroupLabel = _("Main dialog")
mainDialogSizer = wx.StaticBoxSizer(wx.VERTICAL, self, label=mainDialogGroupLabel)
mainDialogBox = mainDialogSizer.GetStaticBox()
mainDialog = gui.guiHelper.BoxSizerHelper(self, sizer=mainDialogSizer)

if not conf["use_org"]:
self.org_name.Disable()
self.org_key.Disable()
mainDialogGroup = gui.guiHelper.BoxSizerHelper(self, sizer=mainDialogSizer)

label = _("Block the closing using the &escape key")
self.blockEscape = wx.CheckBox(
self,
label=label,
)
self.blockEscape.SetValue(conf["blockEscapeKey"])
mainDialog.addItem(self.blockEscape)
mainDialogGroup.addItem(self.blockEscape)

label = _("Remember the content of the S&ystem field between sessions")
self.saveSystem = wx.CheckBox(
self,
label=label,
)
self.saveSystem.SetValue(conf["saveSystem"])
mainDialog.addItem(self.saveSystem)
mainDialogGroup.addItem(self.saveSystem)

label = _("Enable &advanced settings (including temperature and probability mass)")
self.advancedMode = wx.CheckBox(
self,
label=label,
)
self.advancedMode.SetValue(conf["advancedMode"])
mainDialog.addItem(self.advancedMode)

sHelper.addItem(mainDialogSizer)
mainDialogGroup.addItem(self.advancedMode)

TTSGroup = _("Text To Speech")
TTSSizer = wx.StaticBoxSizer(wx.VERTICAL, self, label=TTSGroup)
TTSGroupLabel = _("Text To Speech")
TTSSizer = wx.StaticBoxSizer(wx.VERTICAL, self, label=TTSGroupLabel)
TTSBox = TTSSizer.GetStaticBox()
TTS = gui.guiHelper.BoxSizerHelper(self, sizer=TTSSizer)
TTSGroup = gui.guiHelper.BoxSizerHelper(self, sizer=TTSSizer)

self.voiceList = TTS.addLabeledControl(
self.voiceList = TTSGroup.addLabeledControl(
_("&Voice:"),
wx.Choice,
choices=TTS_VOICES,
Expand All @@ -156,7 +157,7 @@ def makeSettings(self, settingsSizer):
itemToSelect = TTS_VOICES.index(conf["TTSVoice"])
self.voiceList.SetSelection(itemToSelect)

self.modelList = TTS.addLabeledControl(
self.modelList = TTSGroup.addLabeledControl(
_("&Model:"),
wx.Choice,
choices=TTS_MODELS,
Expand All @@ -168,10 +169,67 @@ def makeSettings(self, settingsSizer):

sHelper.addItem(TTSSizer)

imageGroupLabel = _("Images")
imageSizer = wx.StaticBoxSizer(wx.VERTICAL, self, label=imageGroupLabel)
imageBox = imageSizer.GetStaticBox()
imageGroup = gui.guiHelper.BoxSizerHelper(self, sizer=imageSizer)

label = _("&Resize images before sending them to the API")
self.resize = imageGroup.addItem(
wx.CheckBox(
imageBox,
label=label,
)
)
self.resize.SetValue(conf["images"]["resize"])
self.resize.Bind(
wx.EVT_CHECKBOX,
self.onResize
)

label = _("Maximum &width (0 to resize proportionally to the height):")
self.maxWidth = imageGroup.addLabeledControl(
label,
wx.SpinCtrl,
min=0,
max=2000
)
self.maxWidth.SetValue(conf["images"]["maxWidth"])

label = _("Maximum &height (0 to resize proportionally to the width):")
self.maxHeight = imageGroup.addLabeledControl(
label,
wx.SpinCtrl,
min=0,
max=2000
)
self.maxHeight.SetValue(conf["images"]["maxHeight"])

label = _("&Quality for JPEG images (0 [worst] to 95 [best], values above 95 should be avoided):")
self.quality = imageGroup.addLabeledControl(
label,
wx.SpinCtrl,
min=1,
max=100
)
self.quality.SetValue(conf["images"]["quality"])

sHelper.addItem(imageSizer)

sHelper.addItem(mainDialogSizer)

self.onUseOrg(None)
self.onResize(None)

def onUseOrg(self, evt):
self.org_name.Enable(self.use_org.GetValue())
self.org_key.Enable(self.use_org.GetValue())

def onResize(self, evt):
self.maxWidth.Enable(self.resize.GetValue())
self.maxHeight.Enable(self.resize.GetValue())
self.quality.Enable(self.resize.GetValue())

def onSave(self):
api_key = self.APIKey.GetValue().strip()
api_key_manager.save_api_key(api_key)
Expand All @@ -194,9 +252,15 @@ def onSave(self):
conf["renewClient"] = True
conf["saveSystem"] = self.saveSystem.GetValue()
conf["advancedMode"] = self.advancedMode.GetValue()

conf["TTSVoice"] = self.voiceList.GetString(self.voiceList.GetSelection())
conf["TTSModel"] = self.modelList.GetString(self.modelList.GetSelection())

conf["images"]["resize"] = self.resize.GetValue()
conf["images"]["maxWidth"] = self.maxWidth.GetValue()
conf["images"]["maxHeight"] = self.maxHeight.GetValue()
conf["images"]["quality"] = self.quality.GetValue()


class GlobalPlugin(globalPluginHandler.GlobalPlugin):

Expand Down
37 changes: 35 additions & 2 deletions addon/globalPlugins/openai/imagehelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,44 @@
additionalLibsPath = os.path.join(ADDON_DIR, "lib")
sys.path.insert(0, additionalLibsPath)
from openai import OpenAI
from PIL import Image
sys.path.remove(additionalLibsPath)

# Borrowed from <https://platform.openai.com/docs/guides/vision>
def resize_image(
src: str,
max_width: int = 0,
max_height: int = 0,
quality: int = 85,
target: str = "Compressed.PNG"
):
"""
Compress an image and save it to a specified file by resizing according to
given maximum dimensions and adjusting the quality.
@param src: path to the source image.
@param max_width: Maximum width for the compressed image. If 0, only `max_height` is used to calculate the ratio.
@param max_height: Maximum height for the compressed image. If 0, only `max_width` is used to calculate the ratio.
@param quality: the quality of the compressed image
@param target: output path for the compressed image
@return: True if the image was successfully compressed and saved, False otherwise
"""
if max_width <= 0 and max_height <= 0:
return False
image = Image.open(src)
orig_width, orig_height = image.size
if max_width > 0 and max_height > 0:
ratio = min(max_width / orig_width, max_height / orig_height)
elif max_width > 0:
ratio = max_width / orig_width
else:
ratio = max_height / orig_height
new_width = int(orig_width * ratio)
new_height = int(orig_height * ratio)
resized_image = image.resize((new_width, new_height), Image.ANTIALIAS)
resized_image.save(target, optimize=True, quality=quality)
return True


# Function to encode the image
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
Expand Down
25 changes: 24 additions & 1 deletion addon/globalPlugins/openai/maindialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import ui
from logHandler import log
from .consts import ADDON_DIR, DATA_DIR
from .imagehelper import describeFromImageFileList, encode_image
from .imagehelper import resize_image, describeFromImageFileList, encode_image
additionalLibsPath = os.path.join(ADDON_DIR, "lib")
sys.path.insert(0, additionalLibsPath)
import openai
Expand Down Expand Up @@ -767,6 +767,18 @@ def onOk(self, evt):
wx.OK|wx.ICON_ERROR
)
return
if (
model.name == MODEL_VISION
and not self.conf["images"]["resize"]
and not self.conf["images"]["resizeInfoDisplayed"]
):
msg = _("Be aware that the add-on may auto-resize images before API submission to lower request sizes and costs. Adjust this feature in the Open AI settings if needed. This message won't show again.")
gui.messageBox(
msg,
_("Open AI"),
wx.OK|wx.ICON_INFORMATION
)
self.conf["images"]["resizeInfoDisplayed"] = True
system = self.systemText.GetValue().strip()
if self.conf["saveSystem"] and system != self._lastSystem and system:
self.data["system"] = system
Expand Down Expand Up @@ -930,6 +942,7 @@ def getImages(
self,
pathList: list = None
) -> list:
conf = self.conf
if not pathList:
pathList = self.pathList
images = []
Expand All @@ -938,6 +951,16 @@ def getImages(
if url_re.match(path):
images.append({"type": "image_url", "image_url": {"url": path}})
elif os.path.isfile(path):
if conf["images"]["resize"]:
path_ = os.path.join(DATA_DIR, "last_resized.jpg")
resize_image(
path,
max_width=conf["images"]["maxWidth"],
max_height=conf["images"]["maxHeight"],
quality=conf["images"]["quality"],
target=path_
)
path = path_
base64_image = encode_image(path)
format = path.split(".")[-1]
mime_type = f"image/{format}"
Expand Down

0 comments on commit 7d9ecfc

Please sign in to comment.