Qt/PyQt(/Other?): How do I change specific colors in a pixmap?
Asked Answered
S

2

5

How do I change specific colors in a pixmap? For example, I have a pixmap with white and black pixels, and I want to change all white pixels to blue, but leave the black ones alone. Or maybe change the black to white and the white to blue... [I am searching for a solution in Qt/PyQt, but maybe this is a general question as to how pixmaps are handled/composed.]

Sankaran answered 25/12, 2011 at 9:53 Comment(0)
D
10

You can use createMaskFromColor to create a bitmap for the white pixels, then use drawPixmap to overwrite them with another color.

    pix = QPixmap("test.png")
    mask = pix.createMaskFromColor(QColor(255, 255, 255), Qt.MaskOutColor)

    p = QPainter(pix)
    p.setPen(QColor(0, 0, 255))
    p.drawPixmap(pix.rect(), mask, mask.rect())
    p.end()

Note that createMaskFromColor is going to convert the pixmap to a QImage, so you should try to use a QImage directly, if possible.

Disprize answered 25/12, 2011 at 10:48 Comment(3)
I guess I'll accept this approach if there is no other. Is there really no way that a pixmap can iterate through its pixels, replacing ones of a certain color? Seems useful and simple to implement!Sankaran
Of course there is: you can convert it to a QImage, then use bits or pixel to access individual pixels. The approach with createMaskFromColor is going to be faster, I think, although probably not by much.Disprize
Hello, I know this is a old question (sorry for disturbing it) but did you have any luck with this? I am trying to do the same thingHaleigh
W
0

A couple of people have asked how bit-level manipulation (changing colors) can be done without using a mask. For this, we need to know how to read and set pixels directly. QImage is designed for just such pixel-level manipulation, while QPixmaps are needed for labels and other QWidgets. So we also must know how to convert from QPixmaps to QImages and back again.

Here is a PyQt6 example (written in Python on a Windows machine) that creates a QImage from scratch, uses QPainter to draw some colored circles, shows the 'before' image on the screen, then examines each pixel, keeping count of each red pixel, and changes each red pixel to white while ignoring all other colors. I know this code is ugly. I offer it as-is because no one else has come forward with an example that answers the original question. Please consider this code a place to launch your deeper studies, and forgive the warts. -Science_1

import sys
from PyQt6.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt6.QtCore import QSize,QPoint,QRect
from PyQt6.QtGui import QPainter, QPixmap, QPen, QColor, QImage
from random import choice, randint
from time import sleep

class myWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        COLORS = [QColor('red').rgb(),QColor('blue').rgb(),QColor('black').rgb(),QColor('green').rgb(),QColor('red')]

        img_1 = QPixmap(QSize(300,300)).toImage() # Create a 300x300 QPixmap image from scratch and convert it to a QImage.

        self.label = QLabel() # Declare a Qself.label for our widget.       
        self.label.resize(img_1.size())
        self.setCentralWidget(self.label)

        # Create our example image from scratch. A Green square with black outline, and a red diagonal line across it.
        img_1.fill(QColor('lightgreen')) # Fill the QImage with light-green.

        # Declare some colors that we will use with our QPens.
        targetColor = QColor('red') # We could use penColor = QColor(rgb(255,0,0,255)) here.

        # Draw a diagonal line across the QImage using QPainter
        artist = QPainter(img_1) # Higher an artist.

        for cnt in range(0,60): 
            artist.setPen(QPen(choice(COLORS),3)) # Have computer choose a color at random from our COLORS list.
            x = randint(0,300)
            y = randint(0,300)
            r = randint(15,40)
            artist.drawEllipse(x,y,r,r) # For a circle type ellipse, the length and width must be the same.
        artist.end() # Dismiss the artist.

        # You don't actually need QPainter to draw on a QImage, but without it, you must code everything it does yourself.
        # Also, setPixel() can't handle attempts to set pixels that are off screen, like QPainter can,
        # So you must be careful to stay in bounds.
        # Here is an example of setting pixels without using QPainter.
        diagonal_Color = QColor("brown").rgba()
        for a in range(0,298):
            img_1.setPixelColor(a,a,diagonal_Color) # The setPixel() or setPixelColor() commands can 
            img_1.setPixel(a+1,a,diagonal_Color)    # be used to set a pixel in an image.   
            img_1.setPixel(a+2,a,diagonal_Color)    # What's the difference? Beats me. If one gives you fits, try the other.    

        self.label.setPixmap(QPixmap.fromImage(img_1))
        self.show()

        # Here we tease the viewer, then suspend the program for three seconds so the 'before' image can be studied.
        print(' Wait for it...') # Tease
        sleep(3) # <-- A quick & dirty pause. For real code, use a one-off signal emitter, or a user-driven event. 

        # In this section, we progress incrementally through all of the pixels, checking the colors.
        # Any red pixels are over-written with white. This is a slow process. For speed, use a mask with QPainter.
        # But pixel-level manipulation has its place, if speed is not a criteria.

        rcount = 0
        x = self.label.size().width()  
        y = self.label.size().height() 

        for row in range(0,y):
            for column in range(0,x):
                pixel_color = img_1.pixelColor(QPoint(column,row)) # Read the pixel color from the QImage.
                if pixel_color == targetColor: # If the pixel is red..
                    rcount += 1 # Incriment the pixel count.
                    img_1.setPixel(column,row,QColor("white").rgb())    # Change the red pixel to white.

                    '''     
                    # If you want to examine the rgb colors individually, you can use .red(), .green(), .blue(), and .alpha()
                    r = pixel_color.red()
                    g = pixel_color.green()
                    b = pixel_color.blue()
                    a = pixel_color.alpha()                 
                    print('pixel_color RGBA = ('+str(r)+', '+str(g)+', '+str(b)+', '+str(a)+')')
                    ''' 

        self.label.setPixmap(QPixmap.fromImage(img_1))  # Convert the Image to a QPixmap, and put it on the label.
        print('\n Finished.')
        print(" All red pixels have been changed to white.")
        print(" Total number of pixels changed:",rcount)
        print("\n Close Window to Quit")            

        self.setCentralWidget(self.label) # A label, being a widget, and can be directly assigned to a QWindow.
        self.show()                       # But if you need several widgets on the screen, use one of the layouts and assign that.

app = QApplication(sys.argv)
window = myWindow()
window.show()
app.exec()
Witness answered 24/7 at 4:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.