Reliable TikTok Data collection for Influencers and Marketers

If you do not live under a cave, you sure must have heard of TikTok.
It allows users to create 15-second videos, sound tracked by music clips.
It sounds simple enough, but it is a wildly popular concept.

I am not a big fan but had to study it for one of our customers.

Here are some statistics. I’ll talk a little about this study then share how you can set up a flow to collect TikTok data. Click here to skip to code 📊 💻.

TikTok Stats at the time of writing-

  • Around 689 million active users.
  • It has over 2 billion downloads on the play store.
  • Over 1 billion total video views.
  • Available in 155 countries.
  • On average, a user spends 52 minutes per day!

source

It’s another decoy for getting meaningful things done in life 😠. But I’ll try to hate it less for the sake of this post 🙄

Heisenbergers GIFs - Get the best GIF on GIPHY

So it seems very useful for the following purposes –

Ads (Snuck in your feed)

Influencer Marketing

Branding

Customer Engagement

TikTok ads cost more than other social media platforms, with CPM as high as 10$.

Influencer marketing is less annoying and yields good returns since they’ve created their niche audience who are faithful to them 😇 At least for the time being 😟 until they get bored 😐 the influencer runs out of content 😢 or people find someone more interesting 🤩 But it seems relatively cheap and more effective.

So which data would you collect? It’s very subjective but, I think these could be useful

  1. Follower count
  2. Following count
  3. # of views on a post
  4. Likes on a post
  5. Comments on a post
  6. Shares of a post
  7. Hearts received (not literally)
  8. Delta counts per day of the above stats!
  9. Posts liked by a user
  10. Backing up user posts (Downloading)
Meme Creator - Funny data data everywhere ! Meme Generator at  MemeCreator.org!

To collect data from TikTok, they provide an API for content management and marketing. But we want to gather other user’s data, which they don’t allow 🙁

TikTok has a web app 🤔 So we turn to scrape ⛏️💪 !!!!!!!!!!

evil laughing raccoon - quickmeme

This library provides an API to scrape TikTok data from their web app using Selenium or Playwright Web Automation.

https://github.com/davidteather/TikTok-Api

TikTok realizes this and blocks your IP if you try to scrape data.
So what do we do?

Good Luck, I'm Behind 7 Proxies | Know Your Meme

Just use a Proxy Server !

evil laughing raccoon - quickmeme
evil laughing raccoon - quickmeme
evil laughing raccoon - quickmeme

Let’s outline the steps –

  1. Collect a list of TikTok handles you want to scrape.
    1. It can be a list of suggested accounts on TikTok from a seed account.
    2. It can be a list of Tiktok handles that users registered on your website.
    3. It can be a list of handles you’re managing or want to monitor (or spy ? 😈 ).
  2. Collect a list of stats required.
  3. If storing in a database, design a schema.
  4. Set up a script to loop over your list of users.
  5. Create a proxy server / Buy a service
    1. Buy a service ? (https://www.techradar.com/in/best/proxies) OR
    2. To create your own proxy server-
      1. Subscribe to ADEO Anonymous HTTPS Proxy (https://aws.amazon.com/marketplace/pp/prodview-lo2helasbjorq)
      2. Create an AWS EC2 instance
      3. Attach an elastic IP
      4. That’s it. This elastic IP is your proxy server IP!
  6. Create a headless browser instance using this proxy IP (The library has inbuilt functions for this)
  7. Call the profile details functions from the library. 
  8. Parse each JSON record and store it as a row in a CSV
  9. Import into your database, if applicable.
  10. Run static DB queries to populate the delta counts.

It roughly looks like this-

import requests
from TikTokApi import TikTokApi
import json

import utilityFunctions #custom made helper functions for eg Json key value to csv data
import time



class Tiktok( utilityFunctions):

	def __init__(self,args):
        #initialize global vars
		
		utilityFunctions.__init__(self, args)		
		
		#Put your configurations in a JSON file
        self.data_call = self.extractor_config['data']['call'][0]
		
		#proxy ip for avoiding reCaptcha after too many API Calls, in this case I've setup a proxy server on EC2 that rotates IPs
		self.proxy = self._chainedGet(self.data_call,"proxy_ip")
		
		#api response data file dump path
		self.local_tmp_file = self._chainedGet(self.data_call,"local_tmp_file")
		
		#response file content type
		self.content_type = self._chainedGet(self.data_call,"content_type")
		
		#Delay in seconds before each API call
		self.delay = self._chainedGet(self.data_call,"scrape_delay")

		#List of static tiktok hanldes to be scrapped, if you don't use API or want to manually add tiktok handles
		self.static_tiktok_handles = self._chainedGet(self.data_call,"static_tiktok_handles")
		
		if self.delay is None:
			self.delay = 5
		
		#user listing API/ this will give list of users to get the TikTok handles for
		self.user_list_api_url = self._chainedGet(self.data_call,"user_list_api_url")
		
		if self.proxy is not None:
			self.api = TikTokApi.get_instance(proxy=self.proxy)
		else:
			self.api = TikTokApi.get_instance()
		
		self.userList = self.getUserList()

		if self.static_tiktok_handles is not None and len(self.static_tiktok_handles)>0:
			self.userList = self.static_tiktok_handles + self.userList

	def getTikTokUserLikeList(self,tiktokUsername):
        #To scrape and assemble a JSON of posts Liked by a tiktok handle
		try:
			self.logger.info("Fetching User Likes for : "+tiktokUsername)			
			try:
				likeListDataResponse = self.api.userLikedbyUsername(tiktokUsername,count=2000, language='en')
			except Exception as e:
				self.logger.exception("Cannot fetch user "+tiktokUsername+": "+str(e))
				return
			self.logger.info("Delaying API Call for "+str(self.delay)+" seconds")
			time.sleep(self.delay)   
			if likeListDataResponse is not None:
				likeListData=[]
				
				for item in likeListDataResponse:
					item["uniqueId"]=tiktokUsername
					likeListData.append(item)

			return likeListData
		except Exception as e:
			self.logger.exception("Error in getTikTokUserLikeList: "+str(e))

	def getTikTokVideoData(self,transformerObj):
        #To loop over accounts and assemble all users posts details in a list of json objects
		try:
			local_tmp_file = self.local_tmp_file
			content_type = self.content_type

			if local_tmp_file is None:
				local_tmp_file = "/tmp/VideoData.json"

			if content_type is None:
				content_type = "json"

			self.transformerObj = transformerObj
			
			userList = self.userList

			TikTokData = []

			for idx,username in enumerate(userList):  
				self.logger.info("Total  Users: "+str(len(userList)))
				self.logger.info("Processing user no "+str(idx+1)+"  Username: "+username)
				try:
					TikTokUsername = self.getTikTokHandle(username)[0] 
					Id = self.getTikTokHandle(username)[1] 
					if TikTokUsername is None and username in self.static_tiktok_handles:
						TikTokUsername = username
               
					if TikTokUsername is not None:
						self.logger.info("TikTok handle: "+TikTokUsername)
						self.logger.info("Delaying API Call for "+str(self.delay)+" seconds")
						time.sleep(self.delay)    
						TikTokUserProfileData = self.getTikTokProfileVideos(TikTokUsername)
								
						TikTokData.extend(TikTokUserProfileData)
				except Exception as e:
					self.logger.exception("Error in getTikTokVideoData: "+str(e))
			
			data_count = len(TikTokData)

			with open(local_tmp_file, 'w', encoding='utf8') as json_file:
				json.dump(TikTokData, json_file, ensure_ascii=False)
			
			data_config = {
			"content_type": content_type
			}
			
			aFileContents = self.extractFileData(data_config, local_tmp_file)

			rTransformer = self.transformerObj.transform_process( aFileContents )

			self.pagination["total"] += data_count

			if( rTransformer != True ):
				self.logger.exception("Transformation Error")
				raise Exception("Transformation Error")

			return True
		except Exception as e:
			self.logger.exception("Error in getTikTokVideoData :"+str(e))
		

	def getTikTokUserData(self,transformerObj):
        #To loop over accounts and assemble all users profile details in a list of json objects
		try:
			local_tmp_file = self.local_tmp_file
			content_type = self.content_type

			if local_tmp_file is None:
				local_tmp_file = "/tmp/UserData.json"

			if content_type is None:
				content_type = "json"

			self.transformerObj = transformerObj
			
			userList = self.userList
			

			TikTokData = []
			for idx,username in enumerate(userList):  
				self.logger.info("Total  Users: "+str(len(userList)))
				self.logger.info("Processing user no "+str(idx+1)+"  Username: "+username)
				try:
					TikTokUsername = self.getTikTokHandle(username)[0]  
					Id = self.getTikTokHandle(username)[1]            
					if TikTokUsername is None and username in self.static_tiktok_handles:
						TikTokUsername = username

					if TikTokUsername is not None:
						self.logger.info("TikTok handle: "+TikTokUsername)
						self.logger.info("Delaying API Call for "+str(self.delay)+" seconds")
						time.sleep(self.delay)    
						TikTokUserProfileData = self.getTikTokProfile(TikTokUsername)
							
						TikTokData.extend(TikTokUserProfileData)
				except Exception as e:
					self.logger.exception("Error in getTikTokUserData: "+str(e))

			data_count = len(TikTokData)

			with open(local_tmp_file, 'w', encoding='utf8') as json_file:
				json.dump(TikTokData, json_file, ensure_ascii=False)
			
			data_config = {
			"content_type": "json"
			}

			aFileContents = self.extractFileData(data_config, local_tmp_file)

			self.pagination["total"] += data_count
			rTransformer = self.transformerObj.transform_process( aFileContents )

			if( rTransformer != True ):
				self.logger.exception("Transformation Error")
				raise Exception("Transformation Error")

			return True
		except Exception as e:
			self.logger.exception("Error in getTikTokUserData :"+str(e))
		
	def getTikTokUserLikeListData(self,transformerObj):
        #To loop over accounts and assemble all users liked tiktok posts details in a list of json objects
		try:
			
			local_tmp_file = self.local_tmp_file
			content_type = self.content_type
			

			if local_tmp_file is None:
				local_tmp_file = "/tmp/UserLikeListData.json"

			if content_type is None:
				content_type = "json"

			self.transformerObj = transformerObj
			
			userList = self.userList

			TikTokData = []
			for idx,username in enumerate(userList):  
				self.logger.info("Total  Users: "+str(len(userList)))
				self.logger.info("Processing user no "+str(idx+1)+"  Username: "+username)
				try:
					TikTokUsername = self.getTikTokHandle(username)[0]
					
					if TikTokUsername is None and username in self.static_tiktok_handles:
						TikTokUsername = username
               
					if TikTokUsername is not None:
						self.logger.info("TikTok handle: "+TikTokUsername)
						self.logger.info("Delaying API Call for "+str(self.delay)+" seconds")
						time.sleep(self.delay)    
						TikTokUserProfileLikeListData = self.getTikTokUserLikeList(TikTokUsername)
						if TikTokUserProfileLikeListData is not None and len(TikTokUserProfileLikeListData)>0:
							TikTokData.extend(TikTokUserProfileLikeListData)
				except Exception as e:
					self.logger.exception("Error in getTikTokUserData: "+str(e))

			data_count = len(TikTokData)

			with open(local_tmp_file, 'w', encoding='utf8') as json_file:
				json.dump(TikTokData, json_file, ensure_ascii=False)
			
			data_config = {
			"content_type": "json"
			}

			aFileContents = self.extractFileData(data_config, local_tmp_file)

			self.pagination["total"] += data_count
			rTransformer = self.transformerObj.transform_process( aFileContents )

			if( rTransformer != True ):
				self.logger.exception("Transformation Error")
				raise Exception("Transformation Error")

			return True
		except Exception as e:
			self.logger.exception("Error in getTikTokUserData :"+str(e))

	def getUserList(self):
		#This will call an API to get the list of users 
		if self.user_list_api_url is None:
			self.user_list_api_url = "<YOU API URL that return USER>" 
		
		UserListResponse = requests.get(self.user_list_api_url, params="", headers="",timeout=5)
		UserListResponseJson = json.loads(UserListResponse.text)
		userList = []
		for user in UserListResponseJson["data"]["data"]:
			userList.append(user["user"]["username"])
		return userList

	def getTikTokHandle(self,username):
        #This will call another API to get the tiktok handle for that user
		if username is None:
			self.logger.info("No Tiktok handle for user")
			return
		tiktok_handle = None

		try:
			UserProfileAPIURL = "YOUR API URL/get-user-details-endpoint/"+username
			UserProfileResponse = requests.get(UserProfileAPIURL, params="", headers="",timeout=5)
			UserProfileResponseJson = json.loads(UserProfileResponse.text)
			
			for social_network in UserProfileResponseJson["data"]["social_networks"]:        
				tiktok_handle = social_network["tiktok_handle"]
						
			return [tiktok_handle]
		except Exception as e:
			self.logger.exception("Error in getTikTokHandle: "+str(e))


	def getTikTokProfileVideos(self,tiktokUsername):
        #To get the stats for tiktok posts / videos
		try:
			self.logger.info("Fetching Posts for user: "+tiktokUsername)
			try:
				profileGenerator = self.api.getUserPager(tiktokUsername, cursor=0)
			except Exception as e:
				self.logger.exception("Cannot fetch user "+tiktokUsername+": "+str(e))
				return
			tikTokList = []
			count = 0
			for item in profileGenerator:
				if item is not None:        
					self.logger.info("Delaying API Call for "+str(self.delay)+" seconds")
					time.sleep(self.delay)    
					tikTokList.extend(item)
					count = count + len(item)
				self.logger.info("Posts fetched:"+str(count))
			return tikTokList
		except Exception as e:
			self.logger.exception("Error in getTikTokProfileVideos: "+str(e))

	def getTikTokProfile(self,tiktokUsername):
        #To scrape and assemble a JSON of TikTok Profile Data of a tiktok handle
		try:
			self.logger.info("Fetching User Profile for : "+tiktokUsername)
			try:
				profileDataResponse = self.api.getUser(tiktokUsername, cursor=0)
			except Exception as e:
				self.logger.exception("Cannot fetch user "+tiktokUsername+": "+str(e))
				return
			self.logger.info("Delaying API Call for "+str(self.delay)+" seconds")
			time.sleep(self.delay)    
			profileData = profileDataResponse["userInfo"]
			
			return [profileData]
		except Exception as e:
			self.logger.exception("Error in getTikTokProfile: "+str(e))

If you’re going to store it in a database, you can create a schema like below-

/*Table for TikTok UserData*/
create table IF NOT EXISTS orgUserData(
    org_id bigint(20) NOT NULL,
    userId bigint(20) NOT NULL,
    uniqueId varchar(255) NOT NULL,
    nickname varchar(255) DEFAULT NULL,
    userCreatedDate datetime DEFAULT NULL,
    avatarThumb text DEFAULT NULL,
    secUid varchar(255) DEFAULT NULL,
    privateAccount tinyint(1) DEFAULT '0',
    secret tinyint(1) DEFAULT '0',
    unique (org_id)
);

/*Table for TikTok UserStats*/
create table IF NOT EXISTS orgUserStats(
    userId bigint(20) NOT NULL,
    followerCount bigint(20) DEFAULT NULL,
    followingCount bigint(20) DEFAULT NULL,
    heartCount bigint(20) DEFAULT NULL,
    videoCount bigint(20) DEFAULT NULL,
    diggCount bigint(20) DEFAULT NULL,
    unique(userId)
);


/*Table for TikTok Posts meta Info*/
create table IF NOT EXISTS orgContentData(
    contentId bigint(20) NOT NULL,
    contentTitle varchar(255) DEFAULT NULL,
    contentDescription text,
    contentCreatedDate datetime DEFAULT NULL,
    contentCover text DEFAULT NULL,
    contentOriginCover text DEFAULT NULL,
    contentType varchar(10) DEFAULT 'video',
    PRIMARY KEY (contentId)
);

/*Table for TikTok Posts & User Data Mapping with Content Stats*/
CREATE TABLE IF NOT EXISTS orgUserContentUploadData (
    userId bigint(20) NOT NULL,
    contentId bigint(20) NOT NULL,
    diggCount bigint(20) DEFAULT NULL,
    shareCount bigint(20) DEFAULT NULL,
    commentCount bigint(20) DEFAULT NULL,
    playCount bigint(20) DEFAULT NULL,
    PRIMARY KEY (contentId),
    CONSTRAINT orgUserContentUploadData_ibfk_1 FOREIGN KEY (contentId) REFERENCES orgContentData (contentId)
);

/*Table for TikTok Posts Stats*/
create table IF NOT EXISTS orgContentStats(
    contentId bigint(20) NOT NULL,
    diggCount bigint(20) DEFAULT NULL,
    shareCount bigint(20) DEFAULT NULL,
    commentCount bigint(20) DEFAULT NULL,
    playCount bigint(20) DEFAULT NULL,
    PRIMARY KEY (contentId),
    CONSTRAINT orgContentDataInStats_ibfk_1 FOREIGN KEY (contentId) REFERENCES orgContentData (contentId)
);


/*Table for TikTok User Likes*/
create table IF NOT EXISTS orgUserLikes(
    userId bigint(20) NOT NULL,
    contentId bigint(20) NOT NULL,
    UNIQUE KEY user_content_unqiue (userId, contentId)
);

/*For Delta counts*/
select
    distinct cd.userId,
    cd.contentId,
    tmp.diggCount - cd.diggCount as diggDiff,
    tmp.shareCount - cd.shareCount as diggDiff,
    tmp.commentCount - cd.commentCount as commentDiff,
    tmp.playCount - cd.playCount as playDiff
from
    orgContentDataTmp tmp
    join orgUserContentUploadData cd on cd.contentId = tmp.contentId;

/*Table to store stats delta per day*/
/*We use a different DB for this, can you guess ? */
CREATE TABLE orgUserContentUploadDataDeltas (
    userId int NOT NULL,
    contentId int NOT NULL,
    diggCount int DEFAULT NULL :: int,
    shareCount int DEFAULT NULL :: int,
    commentCount int DEFAULT NULL :: int,
    playCount int DEFAULT NULL :: int,
    date_time timestamp DEFAULT (now()) :: timestamptz(6)
);

In the End 🥁🥁🥁🥁   –

Now you can run an Analysis on this data!
Use it for machine learning, running campaigns with influencers,
monitoring account stats for optimizing engagement and lots more.

Want to get this setup ? with detailed reporting, analytic dashboards with useful KPIs, and tracking.
You can check out Catchmedia Inc (https://www.catchmedia.com/). We’ve got an awesome team 😎

In case you have any questions or suggestions for this post. Please leave comments.
Thanks for checking this out! 🙂

Advertisement

Making music with Code 👨‍💻+🎸+🥁=🎶 (Alan walker – Fade, song cover)

Back in high school, I was very much interested in music. I took music lessons for different instruments and played in the morning assembly daily. We had a variety of instruments and I was really into music more than anything else. Then we moved to a different city and although I took my instruments with me I couldn’t continue with my music lessons, and it stalled to a complete stop. We sold off the instruments and I just kept a keyboard 🎹 and would occasionally play it, which stopped working right NOW, during the COVID quarantine! Although nothing beats playing music on an instrument, I still tried playing some on my PC & using some software.

hardcore nerd 🤓 in the making 🤣

last music class 😢

I just found out about SuperCollider, it creates a server for audio synthesis. There’s another amazing tool, FoxDot which lets us write code for SuperCollider as we play it! It has a bunch of prepackaged instruments and a big collection to extend it. I’m having fun with this, and I think it’s worth checking even if you don’t like coding. It uses basic programming concepts like functions, loops, conditionals, etc, and lets you make some decent music. I tried to recreate one of Alan Walker’s tracks – Fade (just a random song).  

Disclaimer ⚠️ I’m just having fun with the code so it’s nowhere nearly as good as his song. Just added some guitar and drums, added a simple drop and it’s all just a few lines of Python Code 🐍.

🚧🚧🛑🛑🛑🛑⚠️⚠️⚠️⚠️🛑🛑🛑🛑🚧🚧

The code is not very clean 😛 proceed with caution, it’s work in progress, I’ll be working on it later

import time as t # for sleep / delay between sounds

#speed of track
def setTempo(time):
    Clock.bpm=time #set tempo(its like speed of beats but not exactly speed)
    Clock.mod(16)

# guitar sounds and tracks
def guitar(fill1,fill2):
    if(fill1):
        #for slow guitar play in the music, with some echo
        #these nos are for keys, next number for next key viz. added on new lines for better understanding of keys
        p1>>pluck([0,0,0,2,5,5,5,4,2,2,2,2,0,-1,-1,-1,0],dur=[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/2,1/2],sus=1) 
    if(fill2): 
        #for the second overlapping guitar with double notes
        p2>>pluck([0,0,0,0,0,0,2,2,2,2,5,5,5,5,5,5,4,4,2,2,2,2,2,2,2,2,2,2,0,-1,-1,-1,-1,-1,-1,-1,0],dur=[1/2,1/2,1/2,1/2,1/2,1/2, 1/4,1/4,1/4,1/4, 1/2,1/2,1/2,1/2,1/2,1/2, 1/2,1/2, 1/2,1/2,1/2,1/2,1/2,1/2, 1/4,1/4,1/4,1/4, 1/2, 1/2,1/2,1/2,1/2,1/2,1/2, 1/4,1/4 ],amp=0.5,sus=3)

#beats for track
def regularBeat(preBeatDrop=2,trigger=True,ampp=4):
    if(trigger):
        p3>>play("x",dur=[preBeatDrop],amp=ampp)
    else:
        p3.stop()
    
#beats change before DROP    
def beforeDropBeat():
    regularBeat(1)
    t.sleep(16)
    regularBeat(1/2)
    t.sleep(8)
    regularBeat(1/4)
    t.sleep(6)
    regularBeat(1/8,True,4.5)
    t.sleep(4)
    regularBeat(0,True)

#track after the drop
def drop():
    guitar(1,0)
    p4>>play("x-",dur=[1/2,1/2],sample=[17,17],sus=5,amp=[4,1])

#stop music
def stop():
    Clock.clear()
    
#all components of song
def alanWalkerFaded():
    setTempo(80)
    guitar(1,0)
    t.sleep(16)
    guitar(1,1)
    t.sleep(8)
    regularBeat()
    t.sleep(2)
    beforeDropBeat()
    drop()
    t.sleep(40)
    stop()

#play the song !
alanWalkerFaded()

This code just plays the song, you can record it with any software. I used Audacity to record and export it as an MP3 file. You can download it or listen to it here, in the player below. Link to complete file.

Will add more… thanks for checking out !

Images, Screens, Apples, Pixels and Appxels 🖥️+🍎=🤯

I’ll get to appexels in a minute ! Selfies, profile pics and status update pics are cool. I had taken Image Processing course back in college thinking it was fun. Most of the times the class was really fun and I learned pretty good stuff along with the complex math involved which left me kind of amazed (only for a few days, I used to bunk lectures 😛😅)

I’ll be doing some random experiments and stuff with images and keep sharing to revise my concepts and hopefully help someone learn something. If you can correct me that’ll be a big plus !

zoomed in version of the matrix
Original: Rob Janoff / Public domain
Converted to matrix of 1 & 0 based on black and white color

Most images are a matrix of values, the values tell the program how to construct it on the screen. You can see 3 images above an apple, image of an apple converted to a set of 0s & 1s and a zoomed in version. 1 being a bright pixel and 0 a dark one. What you see is a matrix with each row and column representing what to show on the screen. In a simple LED screen a 0 says turn off the LED and 1 says turn on the LED. You can make out just looking at the matrix itself that it’s an apple. For color images it’s not that apparent.

Photo by Gustavo Fring on Pexels.com
red
red green

green
green blue

blue
blue red

So color images have something more than just 1s & 0s. For a RGB image format the values range from 0 – 255 instead of just 1 & 0. These values give a more control on how strong or light (bright/dim) the value will be represented in an image. For making up the colors however the values are adjusted such that the mix of values produces the desired colors. This is done by having a set of 3 values between 0 – 255, each for a primary color. i.e red, green and blue. Highlighted lines of code below can be changed to get different RGB combinations. Link to complete file

from matplotlib import image #for working with images
from matplotlib import pyplot #for plotting stuff on graphical gui

matrix = image.imread('p.png') #read file
#indexes for rgb values in a array of matrix values 
red = 0
green = 1
blue = 2
opacity = 3 #PNG format allows controlling transparency of image

for row in range(len(matrix)): #cycle through each row in the image
    for column in range(len(matrix[row])): #cycle through column of pixel in the image
        matrix[row][column][red]=matrix[row][column][red] #keep red
        matrix[row][column][green]=0 #remove green
        matrix[row][column][blue]=0 #remove blue

pyplot.axis('off')
pyplot.imshow(matrix)
pyplot.show()
addition
subtraction
inversion

multiplication
division

darker blacks, brighter whites

Some arithmetic calculations on these values like add, sub, multiply, divide. Highlighted line of code below can be changed to get different visual properties. Link to complete file

from matplotlib import image #for working with images
from matplotlib import pyplot #for plotting stuff on graphical gui

matrix = image.imread('p.png') #read file
#indexes for rgb values in a array of matrix values 
red = 0
green = 1
blue = 2
opacity = 3 #PNG format allows controlling transparency of image

for row in range(len(matrix)): #cycle through each row in the image
    for column in range(len(matrix[row])): #cycle through column of pixel in the image
        for value in range(len(matrix[row][column])): #cycle through rgb values for a pixel in the image
            
            # ADD A VALUE
            if(value!=opacity): #need not change opacity
                matrix[row][column][value]=matrix[row][column][value]+0.5 # 0 is least 1 is max

pyplot.axis('off')
pyplot.imshow(matrix)
pyplot.show()
By Smith131072 – Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=79679934 (modified – added black boxes)
close up picture of LCD screen zoomed in on box at the top
box to the bottom right
box to the bottom left

When the screen tries to display the image you can see these values inside the matrix on the screen. Checkout the zoomed in version of red circle, it’s half white and half red as you can see from the original picture. The LCD screen has 3 sub pixels for each pixel and you can see them turn on and off depending on the color being displayed. In the red region you just have the red sub pixels active and in the white region you can see all three sub pixels active. We need all three primary colors to produce white color. The images of the screen are dark and blurry because I took these macro shots from a mobile camera.

generate apple matrix
zoomed area
zoomed area
zoomed area

Here’s a fun one, I wrote a small script to convert a regular image of an apple into one with all pixels made up of red apple emoji. Link to complete file. Link to full resolution image. Well not really pixels, virtual pixels or VIXELS !? 🤔 🥁🥁🥁🥁🥁🥁🥁🥁🥁🥁 APPXELS ! no there’s nothing such as appxels just made it up

from PIL import Image, ImageDraw, ImageFont #for drawing image
from matplotlib import image #can be done with PIL probably, just for code reuse

matrix = image.imread('apple.png') #read file

#indexes for rgb values in a array of matrix values 
red = 0
green = 1
blue = 2
opacity = 3 #PNG format allows controlling transparency of image

scaling = 20 #scale factor for text size, spacing, line heighting, image dimensions
character = "\U0001F34E" #red apple emoticon unicode
canvasColor = (255, 255, 255)
dimension = len(matrix) # height = width, sq image

font_path = "seguiemj.ttf" #font to work with emoticons
font_size = scaling #set font size

img = Image.new('RGB', (dimension*scaling, dimension*scaling), color = canvasColor) #create canvas for image
d = ImageDraw.Draw(img) # draw into this canvas
font = ImageFont.truetype(font_path, font_size) #set font properties

for row in range(dimension): #cycle through each row in the image
    spaceCounter=0 # to space out the pixel apples
    for column in range(len(matrix[row])): #cycle through column of pixel in the image
        # color as per value
        redVal=matrix[row][column][red] #redness
        greenVal=matrix[row][column][green] #greeness
        blueVal=matrix[row][column][blue] #blueness
        hText = column+spaceCounter*scaling #scale and space out every pixel apple
        vText = row*scaling #space out every row
        d.text((hText,vText), character, fill=(int(redVal*255), int(greenVal*255), int(blueVal*255)),font=font) #place the appexel (apple) with color as per original image in corresponding coordinates
        spaceCounter+=1 #push every pixel apple in row further away
 
img.save('apples.png')

Will add more … thanks for checking out !

(source https://gifer.com/en/7kvq)