I'm trying to get to grips with image manipulation using GIMP and I'm failing at the first hurdle:
With an image loaded in GIMP, I want to run a registered script to run a function - simply rotate the whole image by 90 degrees and then display a message.
#!/usr/bin/env python
import os
import sys
import time
from gimpfu import *
def simple_test():
num = 90
image = gimp.pdb # not sure if this is calling the active image
drawable = pdb.gimp_image_get_active_layer(image)
rotate_it(image, 90)
def rotate_it(image, deg):
msg = "simple_test!! " + str(deg) + "\n"
pdb.gimp_image_rotate(gimp.pdb, num)
gimp.message(msg)
register(
"simple_test",
"A simple Python-Fu plug-in",
"When run this plug-in rotates an image 90 degrees",
"Ghoul Fool",
"Ghoul Fool",
"2020",
"simple test",
"",
[],
[],
simple_test,
menu="<Image>/Filters/simple-test",
)
main()
More importantly is trying to get some error message/log/console output to find out where I'm going wrong - only that doesn't seem to display by default.
it definitely looks like you are calling the current image wrongly. Perhaps this example script can help you out:
#!/usr/bin/env python
# Tutorial available at: https://www.youtube.com/watch?v=nmb-0KcgXzI
# Feedback welcome: jacksonbates#hotmail.com
from gimpfu import *
def hello_warning(image, drawable):
pdb.gimp_message("Hello world!")
register(
"python-fu-hello-warning",
"SHORT DESCRIPTION",
"LONG DESCRIPTION",
"Jackson Bates", "Jackson Bates", "2015",
"Hello warning",
"", # type of image it works on (*, RGB, RGB*, RGBA, GRAY etc...)
[
(PF_IMAGE, "image", "takes current image", None),
(PF_DRAWABLE, "drawable", "Input layer", None)
],
[],
hello_warning, menu="<Image>/File") # second item is menu location
main()
I would also suggest this guy's video series about gimp python fu: https://www.youtube.com/watch?v=nmb-0KcgXzI. Unfortunately, the official documentation (in my opinion) lacks beginner friendliness considering the debugging.
The image is always the first parameter of the function of your plug-in and it will be derived from the context so you don't need it to get it before, as paddy.exe showed your plug-ing should have it as the first argument:
def hello_warning(image, drawable):
pdb.gimp_message("Hello world!")
For getting the image if you want to do some testing using the console you should use:
image = gimp.image_list()[0]
Related
I have a multilayer Gimp XCF template and my goal is to automate inserting JPGs into it and export them from the command line.
I have a first working python-fu plugin for inserting the image, and a second working python-fu plugin for flattening/saving the image (one line of executed code), but I want to combine the two plugins to make them easier to call from the command line. Eventually I also want to automate the opening of the XCF file. For now though I'm just trying to combine the two functions. Both plugins receive "image" and "layer" as input parameters.
My function parameters for the combined plugin are image, layer, the JPG to insert (file), X and Y offsets for placing the image in the XCF template (x_offset, y_offset), and a place for the export (outputFolder).
When I add the save command (pdb.file_jpeg_save shown near the bottom of my code) to the first working script, it fails. Why would this work on its own but fail here?
My code is shown below.
#!/usr/bin/env python
from gimpfu import *
def add_flatten_save(image, layer, file, x_offset, y_offset, outputFolder):
''' Add image to new layer, flatten, then saveSave the current layer into a PNG file, a JPEG file and a BMP file. '''
# Indicates that the process has started.
gimp.progress_init("Opening '" + file + "'...")
try:
# Open file.
fileImage = None
if(file.lower().endswith(('.jpeg', '.jpg'))):
fileImage = pdb.file_jpeg_load(file, file)
# Create new layer.
newLayer = gimp.Layer(image, "New Layer Name", layer.width, layer.height, layer.type, layer.opacity, layer.mode)
# the +1 adds it behind the top layer
image.add_layer(newLayer, +1)
# Put image into the new layer.
fileLayer = fileImage.layers[0]
pdb.gimp_edit_copy(fileLayer)
floating = pdb.gimp_edit_paste(newLayer, True)
# Update the new layer.
newLayer.flush()
newLayer.merge_shadow(True)
newLayer.update(0, 0, newLayer.width, newLayer.height)
# Flatten + offset floating layer, then flatten image
pdb.gimp_floating_sel_to_layer(floating)
pdb.gimp_layer_set_offsets(floating, x_offset, y_offset)
pdb.gimp_image_flatten(image)
# Export JPG of flattened image
pdb.file_jpeg_save(image, layer, outputFolder + "/" + layer.name + ".jpg", "raw_filename", 0.9, 0, 0, 0, "Creating with GIMP", 0, 0, 0, 0)
else:
gimp.message("The image could not be opened since it is not an image file.")
except Exception as err:
gimp.message("Unexpected error: " + str(err))
register(
"python_fu_add_flatten_save",
"Add image to layer",
"Add image to layer and flatten.",
"Tim B.",
"Tim B.",
"2021",
"<Image>/Filters/Tim/Add, flatten, save",
"*",
[
(PF_FILE, "file", "File to open", ""),
(PF_INT, "x_offset", "X offset", ""),
(PF_INT, "y_offset", "Y offset", ""),
(PF_DIRNAME, "outputFolder", "Output directory", ""),
],
[],
add_flatten_save)
main()
The fundamental problem is that when you flatten the image, the layer "layer" doesn't exist anymore.
Try adding layer = pdb.gimp_image_get_active_layer(image) before you save.
I'm trying to convert a Jupyter notebook that is using RISE to visualize the slides as a slideshow in the browser into a PDF file. The PDF file should have all pages in landscape mode and resemble the view in the browser. Of course, animations are not possible, but it should be possible to have fragments either combined in a single PDF slide or spread across multiple sort of "accumulating" slides (i.e. building upon their forerunner slides) .
I've been trying to create my own Jinja template that generates a LaTeX document utilizing the beamer document class, with not much success so far.
Do you know if there are any tools or templates or exporters or anything available that can help me with this process? Preferably automatically, like, utilizing nbconvert?
Figured it out myself. Take these steps:
launch jupyter nbconvert --to slides --post serve the_notebook.ipynb; the browser will open the node hosted the_notebook.slides.html
replace the # after the the_notebook.slides.html in the browser URL with ?print-pdf so that the url looks most likely like http://127.0.0.1:8000/the_notebook.slides.html?print-pdf
print to PDF file
Some time ago, I needed to programmatically convert Jupyter Notebook presentations to PDF slides. I did some research and you can use puppeteer to automate the process. You need a simple Python script for this:
import asyncio
import os
import tempfile
from subprocess import PIPE, Popen
from pyppeteer import launch
import concurrent.futures
async def html_to_pdf(html_file, pdf_file, pyppeteer_args=None):
"""Convert a HTML file to a PDF"""
browser = await launch(
handleSIGINT=False,
handleSIGTERM=False,
handleSIGHUP=False,
headless=True,
args=["--no-sandbox"],
)
page = await browser.newPage()
await page.setViewport(dict(width=994, height=768))
await page.emulateMedia("screen")
await page.goto(f"file://{html_file}", {"waitUntil": ["networkidle2"]})
page_margins = {
"left": "20px",
"right": "20px",
"top": "30px",
"bottom": "30px",
}
dimensions = await page.evaluate(
"""() => {
return {
width: document.body.scrollWidth,
height: document.body.scrollHeight,
offsetWidth: document.body.offsetWidth,
offsetHeight: document.body.offsetHeight,
deviceScaleFactor: window.devicePixelRatio,
}
}"""
)
width = dimensions["width"]
height = dimensions["height"]
await page.pdf(
{
"path": pdf_file,
"format": "A4",
"printBackground": True,
"margin": page_margins,
}
)
await browser.close()
if __name__ == "__main__":
html_input_file = "/you/need/full/path/here/presentation.slides.html?print-pdf"
pdf_output_file = "slides.pdf"
pool = concurrent.futures.ThreadPoolExecutor()
pool.submit(
asyncio.run,
html_to_pdf(
html_input_file,
pdf_output_file
),
).result()
The script accepts the HTML slides as input and produces the PDF slides as output. Please note that you need to provide full path for the HTML file. I wrote an article on how to convert notebook presentations to pdf slides. If you would like to apply styling, here is longer version of the script.
I guess jupyter nbconvert --to pdf the_notebook.ipynb should work fine.
You do need to install latex though.
I want to process images before I send them to Tesseract for OCR.
For example:
Resize the image
Change the resolution to 300 dpi
Threshold (B&W image)
Sharpen image
How can I automate this process?
I've just put together an answer (https://graphicdesign.stackexchange.com/questions/53919/editing-several-hundred-images-gimp/53965#53965 ) on graphicdesign, which is intended as an GIMP automation primer for people with no programing skills -
it should be nice for understanding Python-fu as well.
On the very same answer, there are links to the official documentation, and one example of how to create a small script. You should them brose GIMP's PDB to findout about the exact proceeds you want.
But, all in all, you can create a Python file like this:
from gimpfu import *
import glob
def auto():
for filename in glob(source_folder + "/*.png"):
img = pdb.gimp_file_load(source_folder + filename, source_folder + filename)
# place the PDB calls to draw on the image before your interation here
#disp = pdb.gimp_display_new(img)
pdb.gimp_image_merge_visible_layers(img, CLIP_TO_IMAGE)
pdb.gimp_file_save(img, img.layers[0], dest_folder + filename, dest_folder + filename)
# pdb.gimp_display_delete(disp)
pdb.gimp_image_delete(img) # drops the image from gimp memory
register("batch_process_for_blah",
"<short dexcription >Batch Process for Bla",
"<Extended description text>",
"author name",
"license text",
"copyright note",
"menu label for plug-in",
"", # image types for which the plug-in apply - "*" for all, blank for plug-in that opens image itself
[(PF_DIRNAME, "source_folder", "Source Folder", None),
(PF_DIRNAME, "dest_folder", "Dest Folder", None)], # input parameters -
[], # output parameters
menu="<Image>/File", # location of the entry on the menus
)
main()
To find the wanted operations inside the for loop, go to Help->Procedure Browser - or better yet, Filters->Python->Console and hit Browse - it is almost the same, but with an "apply" button that makes it easy to test the call, and copy it over to your plug-in code.
Is there a way to set a OpenCV window to be always on top?
And can i remove the minimize and close button from my window?
Thank you.
You can use:
cvGetWindowHandle()
to obtain Widows handler. Then using regular windows API you can do anything you like
I found the best solution posted in comments here: Python OpenCV open window on top of other applications
Simply add the command below after opening a window, e.g.
cv2.namedWindow('img_file_name', cv2.WINDOW_NORMAL) # Creates a window
os.system('''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "python" to true' ''') # To make window active
Use "python" in lower case. Using "Python", as I found in some answers, gave me an error:
21:62: execution error: Finder got an error: Can’t set process "Python" to true. (-10006))
cv2.namedWindow('CCPDA')
cv2.resizeWindow('CCPDA', 200, 200)
hWnd = win32gui.FindWindow(None, 'CCPDA')
win32gui.SetWindowPos(hWnd, win32con.HWND_TOPMOST, 0, 0, 0, 0,
win32con.SWP_SHOWWINDOW | win32con.SWP_NOSIZE | win32con.SWP_NOMOVE)
You can use setWindowProperty
to set an OpenCV window on top with the property WND_PROP_TOPMOST.
This works with OpenCV 3.4.8+ and 4.1.2+.
E.g. in python:
import cv2, numpy as np
window_name = "Top Window"
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
cv2.setWindowProperty(window_name, cv2.WND_PROP_TOPMOST, 1)
cv2.imshow(window_name, np.full((480,640,3), (234,183,39), dtype=np.int8))
cv2.waitKey(0)
The above python code should create a foreground window:
I found that all I needed to do was set my main window to fullscreen then back to normal.
#!/usr/bin/env python
import cv2
import numpy
WindowName="Main View"
view_window = cv2.namedWindow(WindowName,cv2.WINDOW_NORMAL)
# These two lines will force your "Main View" window to be on top with focus.
cv2.setWindowProperty(WindowName,cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_FULLSCREEN)
cv2.setWindowProperty(WindowName,cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_NORMAL)
# The rest of this does not matter. This would be the rest of your program.
# This just shows an image so that you can see that this example works.
img = numpy.zeros((400,400,3), numpy.uint8)
for x in range(0,401,100):
for y in range(0,401,100):
cv2.line(img,(x,0),(0,y),(128,128,254),1)
cv2.line(img,(x,399),(0,y),(254,128,128),1)
cv2.line(img,(399,y),(x,399),(128,254,128),1)
cv2.line(img,(399,y),(x,0),(254,254,254),1)
cv2.imshow(WindowName, img)
cv2.waitKey(0)
cv2.destroyWindow(WindowName)
for me this work fine in macOS, I think that it's will work well in another os
destroyWindow( "windowName" );
namedWindow( "windowName", WINDOW_NORMAL );
moveWindow( "windowName", posx, posy );
imshow( "windowName", frame );
this repeat in loop, the sequence are:
destroy window, this force to create new window
declare new window
move window at position that you want, for example last
position
show window
Is it possible to copy guide lines from one image to another?
I need this because I have several images that need exactly the same composition, so I want to use the guide lines for this.
There's no option to select & copy a guide line, so I must add them manually.
It would be nice, if there's a little script-fu script.
Okay, there are some interesting functions I found:
(gimp-image-find-next-guide image index)
(gimp_image_add_hguide image xposition)
(gimp_image_add_vguide image yposition)
(gimp_image_get_guide_orientation image guide)
(gimp_image_get_guide_position image guide)
Thanks in advance!
I'd really like to help you but I'm not sure I understand what you are trying to do. Could you edit the question to provide more details?
At a guess (pending more information) are you looking for something like this?
guide = 0
while guide = gimp_image_find_next_guide (image_1,guide) != 0
position = gimp_image_get_guide_position (image_1,guide)
if gimp_image_get_guide_orientation (image_1,guide) == 0
gimp_image_add_hguide (image_2,position)
else
gimp_image_add_vguide (image_2,position)
Note that this is pseudo-code, since the functions you mentioned seem to be part of an API that is using a syntax other than scheme-ish script fu.
But the first question is what are you trying to accomplish? -- after that we can worry about the details of how.
Having been wanting to learn Gimp Scripts (PythonFu) for a while and requiring this functionality I used the Pseudo code provided by MarkusQ and this handy tutorial https://jacksonbates.wordpress.com/python-fu-gimp-scripting-tutorial-pages/ to create a script to copy guidelines from one image to another.
#!/usr/bin/env python
from gimpfu import *
def CopyGuidelines(image_1, drawable, image_2):
guide = pdb.gimp_image_find_next_guide(image_1, 0)
while guide != 0 :
position = pdb.gimp_image_get_guide_position (image_1,guide)
if pdb.gimp_image_get_guide_orientation (image_1,guide) == 0:
pdb.gimp_image_add_hguide (image_2,position)
else:
pdb.gimp_image_add_vguide (image_2,position)
guide = pdb.gimp_image_find_next_guide (image_1,guide)
register(
"python-fu-CopyGuidelines",
"Copy Guidelines",
"Copy Guidelines from one image to another",
"Anthony", "JustAGuyCoding", "2017",
"Copy Guidelines",
"", # type of image it works on (*, RGB, RGB*, RGBA, GRAY etc...)
[
(PF_IMAGE, "image_1", "takes current image", None),
(PF_DRAWABLE, "drawable", "Input layer", None),
(PF_IMAGE, "image_2", "takes other image", None)
],
[],
CopyGuidelines, menu="<Image>/Tools")
main()
You'll need to copy this in to a CopyGuidelines.py file and put it your Gimp's plugin directory (See Preferences > Folders ) and restart Gimp to see the CopyGuideline option under Tools. Then open up the two images, select the one with the Guidelines and select CopyGuidelines to run the script.