Art Style Transfer has gained immense popularity as an exciting application of AI in the art world. This article delves into the captivating realm of AI art and how art style transfer transforms ordinary images into mesmerizing pieces of art. With the help of the Delphi ecosystem, we will build an Art Style Transfer app that allows users to blend the characteristics of one artwork with another, producing visually stunning and unique results. So, let’s dive right in!
Table of Contents
What is Art Style Transfer and Why is it Exciting?
Art Style Transfer or more technically known as Neural Style Transfer, is a technique that uses deep neural networks to apply the artistic style of one image (the style image) to the content of another image (the content image). This process results in a new image that combines the original image’s content with the style image’s visual characteristics. It opens up a world of possibilities for creating artistic and imaginative images that blend different artistic styles seamlessly. So in this tutorial, we employ deep learning to create an image in the style of another image, a process commonly referred to as neural style transfer.
What is the Python Delphi Ecosystem Consist of?
The Python Delphi ecosystem consists of the Delphi IDE (Integrated Development Environment) and Delphi FMX (FireMonkey), which enable developers to build cross-platform applications with visually stunning and responsive user interfaces. Delphi FMX supports multi-platform development, allowing apps to run on Windows, macOS, iOS, and Android using a single codebase.
Additionally, the ecosystem includes the Delphi4Python Exporter, a vital tool that facilitates the seamless integration of Python code within Delphi. This integration empowers developers to leverage Python’s vast libraries and AI capabilities while harnessing the visual prowess of Delphi FMX, offering a powerful environment for creating innovative applications, particularly in AI and machine learning domains.
How to Create an Art Style Transfer App?
What Are the Prerequisites for this Art Style Transfer App?
To create our art style transfer app, ensure you have Delphi IDE and the FireMonkey GUI framework installed on your device. Moreover, you will need a text editor or IDE that supports Python, such as PyScripter, along with the Delphi4PythonExporter tool for Python integration. If you don’t have these tools already, refer to the article “Powerful Python GUI Project Setup” for guidance.
Furthermore, make sure to install the required Python libraries by running the following commands in the command prompt or terminal:
1 2 3 4 |
pip install pillow pip install tensorflow pip install tensorflow-hub |
Lastly, to access the code for this tutorial, you can download it from our GitHub repository through the following link:
https://github.com/Embarcadero/PythonBlogExamples/tree/main/Art_Style_Transfer_App
By meeting these prerequisites, you’ll be fully equipped to dive into the exciting world of AI art and create your own art style transfer app using the Delphi ecosystem and Python libraries.
Are there any Python Libraries for Implementing the Neural Style Transfer Algorithm?
Yes, there are Python libraries available for implementing the Neural Style Transfer algorithm. One prominent library is TensorFlow Hub. TensorFlow Hub provides pre-trained models that can be used to easily apply the optimization technique to blend a content image with a style reference image, producing an output image that adopts the reference style while preserving the original image’s content.
How to Build the User Interface with Delphi?
Let’s begin by launching the Delphi CE application and creating a new project. To achieve this, navigate to File > New > Multi-Device Application > Blank Application > Ok
. For this tutorial, we’ve named our project ArtStyleTransferApp
.
If you are unfamiliar with the various sections of the Delphi IDE, we recommend consulting our complimentary eBook bundle, which covers the Delphi Python Ecosystem and provides insights into all Python GUI offerings.
Let’s begin by renaming our form. Right-click on the form and select QuickEdit. This will open up an edit box, where we will set the name of our form as MainForm
with the caption Art Style Transfer App
.
Now, head over to the Projects tab and rename the source file of the form from Unit1.pas
to Main.pas
. We are doing this to help us keep track of our forms.
Now let’s start working on our form. First, we will resize our forms by heading to the Object Inspector and searching for the ClientWidth
and ClientHeight
in the properties tab. We will resize our form by setting them to 624
and 640
, respectively.
Next, we’ll proceed by adding a few labels to our app. Access the standard palette and locate the TLabel
component. Drag and drop this component onto the form to include it. We will be adding three labels to our form. The first will be a title label, Title
, displayed at the top of the form. The second and third labels, StyleImageLabel
and ContentImagelabel
, will be used to indicate the style image and the image to be converted.
Following that, we’ll customize the text style using the TextSettings
property of each label. To do so, access the Object Inspector and search for TextSettings
. Click on the ellipsis button (...
) next to the Font property to explore various options for modifying the label’s appearance based on your preferences. In this case, we’ve chosen the SemiBold Segoe UI font family and distinct font sizes for each label. Additionally, we’ll set our labels’ HorzAlign
and VertAlign
properties to Center
.
Here is what our form looks like:
Next, let’s add a few buttons to our form. For this, we will use the TButton
component, so navigate to the Palette and search for the component.
We will add two Upload
buttons to our form. One button called UploadStyleButton
will be used to upload a style image that will be used for reference. The second upload button that, we will name UploadContentButton
will help us select the file to be converted. Finally, we will add an Apply button, ApplyButton
, to run the image generation algorithm.
Next, we’ll customize the text style using the TextSettings
property of each button. Here is what our form looks like:
Now, we will add two TOpenDialog
components to our form. These components will help us open a dialog box to select a file from our system. So open up the Palette and search for the component. Next, drag and drop this component anywhere into your form. The placement of these components will not matter as they will be invisible.
Finally, let’s finish our forms by adding some holders showing our images. Head over to the Palette and search for the TImage
component. Add three of these components to your form.
Now, right-click on each component and define its name. Here we will set the left image component under Style Image
as StyleImage,
the component under Content Image
as ContentImage
, and finally, the component at the bottom as FinalImage
. Here is what our form looks like:
With this, the functional components of our form are ready. Now we can add a few final touches to make our form more visually appealing. We will do this by downloading the free ebook bundle, which comes shipped with a multitude of styles that you can choose from.
After downloading your free ebook, extract the FMX styles zip in your chosen directory. Next, select the style you want to use. Here we will be using Drak.style
available in the bundle.
To use this style, head to the Palette and add the TStyleBook
component to your form. Again, the placement does not matter for this component, as this, too, will be invisible.
Double-click on the StyleBook1
component to open up your Style Designer. Next, click on the open button to open up a dialog box.
Now navigate to the Dark.style
file in your styles directory, select your file and finally click on exit.
Once you’ve finalized your style, select the MainForm and head to the Object Inspector. Here we will search for the StyleBook property. Select the drop-down menu and select StyleBook1.
Here is what our final form looks like:
How to Integrate the Style Transfer Algorithm into the App?
Before integrating the style transfer algorithm into our app, we must export it using the Delphi4Python exporter. However, exporting right now will cause the buttons to lose their function. Therefore, we will need to add functionality to each button in the form.
To do this, double-click on each button on the form. This will add an OnClick
method to that button. The procedure will be created in the .pas
file of the form. This allows you to add unique functionality to each button when clicked.
Because the Delphi4PythonExporter does not use run-time implementation, we must add at least one comment (//
) to each procedure to preserve the function when we export the form.
Now that our form is ready, we are ready to export. Select Tools > Export To Python > Export Current Entire Project
to export the project.
Here we will give the project the title Art Style Transfer App
. Next, select the directory of your choosing and click on Export:
Because we had one form, Delphi will generate two Python files and one .pyfmx
file. The ArtStyleTransferApp.py
file will be used to run the application, whereas the Main.py
file contains the class for the MainForm
. The Main.pyfmx
file contains the visual information for the MainForm
. Here are the contents for each file.
KMeansClustering.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from delphifmx import * from Main import MainForm def main(): Application.Initialize() Application.Title = 'Art Style Transfer App' Application.MainForm = MainForm(Application) Application.MainForm.Show() Application.Run() Application.MainForm.Destroy() if __name__ == '__main__': main() |
Main.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import os from delphifmx import * class MainForm(Form): def __init__(self, owner): self.Title = None self.StyleImageLabel = None self.ContentImageLabel = None self.UploadContentButton = None self.UploadStyleButton = None self.ApplyButton = None self.StyleImage = None self.ContentImage = None self.FinalImage = None self.OpenDialog1 = None self.OpenDialog2 = None self.StyleBook1 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Main.pyfmx")) def UploadContentButtonClick(self, Sender): pass def UploadStyleButtonClick(self, Sender): pass def ApplyButtonClick(self, Sender): pass |
Now, the only thing we need to do is add functionality to our form. Start by opening up Main.py
in the text editor of your choice and importing some libraries:
1 2 3 4 5 6 |
# Additional Imports import tensorflow as tf import numpy as np import PIL.Image import tensorflow_hub as hub |
Next, head over to the __init__
function. Here we will use the os library to load compressed models from tensorflow. Here is what our __init__
function looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def __init__(self, owner): self.Title = None self.StyleImageLabel = None self.ContentImageLabel = None self.UploadContentButton = None self.UploadStyleButton = None self.ApplyButton = None self.StyleImage = None self.ContentImage = None self.FinalImage = None self.OpenDialog1 = None self.OpenDialog2 = None self.StyleBook1 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Main.pyfmx")) # Load compressed models from tensorflow_hub os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED' |
Now let’s define a helper function called load_img
that will help us load and preprocess an image using a provided path. We will start by defining the maximum dimensions as 512
and load and convert the image data type to float32
.
1 2 3 4 5 6 7 |
def load_img(self, path_to_img): # Function to load and preprocess an image max_dim = 512 img = tf.io.read_file(path_to_img) img = tf.image.decode_image(img, channels=3) img = tf.image.convert_image_dtype(img, tf.float32) |
Next, we will cast the image, reshape it and finally return it. Here is what our final load_img
function looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def load_img(self, path_to_img): # Function to load and preprocess an image max_dim = 512 img = tf.io.read_file(path_to_img) img = tf.image.decode_image(img, channels=3) img = tf.image.convert_image_dtype(img, tf.float32) shape = tf.cast(tf.shape(img)[:-1], tf.float32) long_dim = max(shape) scale = max_dim / long_dim new_shape = tf.cast(shape * scale, tf.int32) img = tf.image.resize(img, new_shape) img = img[tf.newaxis, :] return img |
Next, we will define another function called tensor_to_image
which, as the name implies, will help convert a tensor image to a PIL image that we can save. Here is a simple code on how we can do that:
1 2 3 4 5 6 7 8 9 |
def tensor_to_image(self, tensor): # Function to convert a tensor to a PIL Image tensor = tensor*255 tensor = np.array(tensor, dtype=np.uint8) if np.ndim(tensor) > 3: assert tensor.shape[0] == 1 tensor = tensor[0] return PIL.Image.fromarray(tensor) |
Now we can move on and finish our button functions. Head to the UploadContentButtonClick
function and open a dialog box using the OpenDialog1
component. We will filter the files to only show the different image files in the dialogbox. Once the user selects the image, use the .Bitmap.LoadFromFile()
method to display it in the ContentImage
placeholder. Here is what our final UploadContentButtonClick
function looks like:
1 2 3 4 5 6 7 8 9 10 |
def UploadContentButtonClick(self, Sender): self.OpenDialog1.Title = "Select your Content Image" # Filtering only image formats to show for file pick self.OpenDialog1.Filter = "Image files (*.jpg;*.jpeg;*.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*" if self.OpenDialog1.Execute(): # Load the selected image into ContentImage component self.ContentImage.Bitmap.LoadFromFile(self.OpenDialog1.FileName) |
Moving on, let’s define our UploadStyleButtonClick
function similarly using the OpenDialog2
component. Here is what our function looks like:
1 2 3 4 5 6 7 8 9 10 |
def UploadStyleButtonClick(self, Sender): self.OpenDialog2.Title = "Select your Style Image" # Filtering only image formats to show for file pick self.OpenDialog2.Filter = "Image files (*.jpg;*.jpeg;*.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*" if self.OpenDialog2.Execute(): # Load the selected image into StyleImage component self.StyleImage.Bitmap.LoadFromFile(self.OpenDialog2.FileName) |
Finally, let’s complete our ApplyButtonClick
function. Start by loading our images using the load_img
function:
1 2 3 4 |
def ApplyButtonClick(self, Sender): content_image = self.load_img(self.OpenDialog1.FileName) style_image = self.load_img(self.OpenDialog2.FileName) |
Next, we will load a model using tensorflow_hub
and pass in our StyleImage
and ContentImage
to generate a stylized image.
1 2 3 4 5 6 7 8 9 |
def ApplyButtonClick(self, Sender): content_image = self.load_img(self.OpenDialog1.FileName) style_image = self.load_img(self.OpenDialog2.FileName) # Load the Hub model for image stylization hub_model = hub.load('https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2') stylized_image = hub_model(tf.constant(content_image), tf.constant(style_image))[0] stylized_image = self.tensor_to_image(stylized_image) |
Finally, we will save the stylized image locally with a specific filename and load the image to our FinalImage
component. Here is what our ApplyButtonClick
function looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def ApplyButtonClick(self, Sender): content_image = self.load_img(self.OpenDialog1.FileName) style_image = self.load_img(self.OpenDialog2.FileName) # Load the Hub model for image stylization hub_model = hub.load('https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2') stylized_image = hub_model(tf.constant(content_image), tf.constant(style_image))[0] stylized_image = self.tensor_to_image(stylized_image) # Save the stylized image locally with a specific filename fname = "{}_{}_result.jpg".format(os.path.splitext(os.path.basename(self.OpenDialog1.FileName))[0], os.path.splitext(os.path.basename(self.OpenDialog2.FileName))[0]) output_path = os.path.join(os.path.dirname(self.OpenDialog1.FileName), fname) stylized_image.save(output_path) # Load the stylized image into the FinalImage component self.FinalImage.Bitmap.LoadFromFile(output_path) |
Here is what our final Main.py
file looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
import os from delphifmx import * # Additional Imports import tensorflow as tf import numpy as np import PIL.Image import tensorflow_hub as hub class MainForm(Form): def __init__(self, owner): self.Title = None self.StyleImageLabel = None self.ContentImageLabel = None self.UploadContentButton = None self.UploadStyleButton = None self.ApplyButton = None self.StyleImage = None self.ContentImage = None self.FinalImage = None self.OpenDialog1 = None self.OpenDialog2 = None self.StyleBook1 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Main.pyfmx")) # Load compressed models from tensorflow_hub os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED' def load_img(self, path_to_img): # Function to load and preprocess an image max_dim = 512 img = tf.io.read_file(path_to_img) img = tf.image.decode_image(img, channels=3) img = tf.image.convert_image_dtype(img, tf.float32) shape = tf.cast(tf.shape(img)[:-1], tf.float32) long_dim = max(shape) scale = max_dim / long_dim new_shape = tf.cast(shape * scale, tf.int32) img = tf.image.resize(img, new_shape) img = img[tf.newaxis, :] return img def tensor_to_image(self, tensor): # Function to convert a tensor to a PIL Image tensor = tensor*255 tensor = np.array(tensor, dtype=np.uint8) if np.ndim(tensor) > 3: assert tensor.shape[0] == 1 tensor = tensor[0] return PIL.Image.fromarray(tensor) def UploadContentButtonClick(self, Sender): self.OpenDialog1.Title = "Select your Content Image" # Filtering only image formats to show for file pick self.OpenDialog1.Filter = "Image files (*.jpg;*.jpeg;*.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*" if self.OpenDialog1.Execute(): # Load the selected image into ContentImage component self.ContentImage.Bitmap.LoadFromFile(self.OpenDialog1.FileName) def UploadStyleButtonClick(self, Sender): self.OpenDialog2.Title = "Select your Style Image" # Filtering only image formats to show for file pick self.OpenDialog2.Filter = "Image files (*.jpg;*.jpeg;*.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*" if self.OpenDialog2.Execute(): # Load the selected image into StyleImage component self.StyleImage.Bitmap.LoadFromFile(self.OpenDialog2.FileName) def ApplyButtonClick(self, Sender): content_image = self.load_img(self.OpenDialog1.FileName) style_image = self.load_img(self.OpenDialog2.FileName) # Load the Hub model for image stylization hub_model = hub.load('https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2') stylized_image = hub_model(tf.constant(content_image), tf.constant(style_image))[0] stylized_image = self.tensor_to_image(stylized_image) # Save the stylized image locally with a specific filename fname = "{}_{}_result.jpg".format(os.path.splitext(os.path.basename(self.OpenDialog1.FileName))[0], os.path.splitext(os.path.basename(self.OpenDialog2.FileName))[0]) output_path = os.path.join(os.path.dirname(self.OpenDialog1.FileName), fname) stylized_image.save(output_path) # Load the stylized image into the FinalImage component self.FinalImage.Bitmap.LoadFromFile(output_path) |
Now that our application is ready, let’s test it out. Head over to ArtStyleTransferApp.py
and run the file.
Next, we will upload our style and content images by clicking the Upload buttons. Here we will be using Vincent van Gogh’s Starry Night as the Style image and a picture of penguins as our Content image.
Once you’ve selected your image, it should appear on the image placeholders:
Now press the Apply button to evaluate your image. Your result image should appear in the FinalImage
placeholder:
Your file should also be saved in the same directory as your image:
What Are the Key Take Aways of this Tutorial?
In conclusion, our Art Style Transfer app demonstrates the exciting potential of AI art and the capabilities of the Delphi ecosystem in creating innovative applications. By providing users with a simple and interactive platform for artistic expression, we encourage creativity and exploration in the field of AI-generated art.
By harnessing the fusion of Delphi’s visual capabilities and Python’s extensive libraries, the Art Style Transfer app exemplifies the exciting potential of AI in the art world. Whether you’re an experienced developer or a newcomer, embracing the Delphi ecosystem can revolutionize your app development journey.
What Are Some FAQs Related to this Tutorial?
How does Neural Style Transfer work?
Using a convolutional network, neural Style Transfer works by extracting content and style reference statistics from the content and style reference images. It then optimizes an output image to match the content statistics of the content image and the style statistics of the style reference image.
What is TensorFlow Hub?
TensorFlow Hub is a Python library that provides a collection of pre-trained models and modules for various tasks, including Neural Style Transfer. It simplifies the implementation of complex algorithms by offering access to pre-trained convolutional networks and other resources.
Do I need to train the model from scratch for Neural Style Transfer?
No, one of the advantages of using TensorFlow Hub is that it provides pre-trained models for Neural Style Transfer. You can use these models directly without the need for training from scratch.
Can I adjust the intensity of style transfer in the output image?
Yes, you can adjust the intensity of style transfer by controlling the weight of the style loss during optimization. This allows you to fine-tune the artistic style of the content image.
Is Neural Style Transfer computationally intensive?
Neural Style Transfer can be computationally intensive, especially when working with large images and complex styles. However, using pre-trained models from TensorFlow Hub can significantly reduce the computation time.
Are there other libraries besides TensorFlow Hub for Neural Style Transfer?
Yes, other libraries and frameworks, such as PyTorch, offer implementations of Neural Style Transfer. Each library may have its advantages and specific use cases, so developers can choose based on their preferences and project requirements.
What programming languages are supported by Delphi?
Delphi primarily supports Object Pascal as its native programming language. However, it also provides support for C++ through the use of the C++Builder IDE.
Is Delphi suitable for beginners in programming?
Delphi is considered beginner-friendly due to its visual programming features and user-friendly IDE. It provides an excellent learning platform for newcomers to programming.